深入理解 JavaScript 方法集的特性与最佳实践

来源:互联网 发布:c# socket编程 编辑:程序博客网 时间:2024/05/22 08:00

JavaScript 包含了一套小型的、可用在标准类型上的方法集。下面我们对这些方法一一进行分析。

1 Array

1.1 array.concat(item…)

concat 方法会产生一个新数组,它是数组的浅复制,并把一个或多个 item 附加在其后。如果 item 是一个数组,那么它的每一个元素都会被添加:

var a = ['a', 'b', 'c'];var b = ['x', 'y', 'z'];var c = a.concat(b, true);console.log(c);//[ "a", "b", "c", "x", "y", "z", true ]

1.2 array.join(separator)

join 方法会把一个 array 构造成一个字符串。它先把 array 中的每一个元素都构造成一个字符串,然后再用 separator 分隔符把它们连接起来。默认的 separator 是逗号 ‘,’。如果想要实现无间隔连接,可以把 separator 设置为空字符串。

var a = ['a', 'b', 'c'];a.push('d');console.log(a.join(''));//abcd

也可以使用 + 运算符连接这些字符串。目前主流的浏览器(包括 IE8 之后的版本)都对 + 运算符做了特别的优化,所以它的性能已经显著高于 Array.join() 咯,在大多数情况下,如果需要连接字符串,建议首选 + 运算符哦O(∩_∩)O~

1.3 array.pop()

pop 与 push 方法可以使得数组像堆栈一样工作。pop 方法会移除 array 中的最后一个元素并返回这个元素。如果 array 是空数组,那么会返回 undefined:

var a = ['a', 'b', 'c'];console.log(a.pop());//c

pop 可以像这样实现:

Array.method('pop2', function () {    return this.splice(this.length - 1, 1)[0];});console.log(a.pop2());//b

1.4 array.push()

push 方法把一个或多个参数 item 附加到一个数组的尾部。它会修改 array。如果 item 是一个数组,它会把这个数组作为单个元素添加到数组中,并返回这个 array 的新长度值:

var a = ['a', 'b', 'c'];var b = ['x', 'y', 'z'];console.log(a.push(b, true));//5console.log(a);//[ "a", "b", "c", Array[3], true ]

push 可以像这样实现:

Array.method('push2', function () {    //[this.length,0] 中的 length 指定 slice 的初始索引;0 表示执行不删除    this.splice.apply(this, [this.length, 0].concat(Array.prototype.slice.apply(arguments)));    return this.length;});console.log(a.push2(b, true));//7console.log(a);//[ "a", "b", "c", Array[3], true, Array[3], true ]

1.5 array.reverse()

这个方法会反转 array 里元素的顺序,并返回 array 本身:

var a = ['a', 'b', 'c'];var b = a.reverse();console.log(a);//[ "c", "b", "a" ]console.log(b);//[ "c", "b", "a" ]

1.6 array.shift()

shift 会移除数组 array 中的第 1 个元素并返回该元素。如果 array 是空数组,那么会返回 undefined。shift 通常比 pop 慢的多:

var a = ['a', 'b', 'c'];console.log(a.shift());//aconsole.log(a);//[ "b", "c" ]

shift 可以像这样实现:

Array.method('shift2', function () {    return this.splice(0, 1)[0];});console.log(a.shift2());//bconsole.log(a);//[ "c" ]

1.7 array.slice(start, end)

slice 方法会对 array 中的一段进行浅复制。从 array[start] 一直复制到 array[end]。end 参数是可选的,默认为数据的长度 array.length。

以下是特殊情况:
* 如果两个参数中任意一个是负数,这个负值的参数会与 array.length 相加,试图让它变为非负数。
* 如果 start 大等于 array.length ,将会得到一个空数组。

var a = ['a', 'b', 'c'];console.log(a.slice(0, 1));// [ "a" ]console.log(a.slice(1));//[ "b", "c" ]console.log(a.slice(1, 2));

1.8 array.sort(comparefn)

sort 方法会对 array 的内容进行排序,默认的比较函数是把要排序的元素视为字符串,所以它不能正确地给一组数字排序:

var n = [4, 8, 29, 33, 48, 50, 98];n.sort();//默认要排序的元素都为字符串console.log(n);//[ 29, 33, 4, 48, 50, 8, 98 ]

我们可以使用自定义的比较函数来解决数字排序的问题。比较函数接受两个参数:
* 如果这两个参数相等,则返回 0。
* 如果第一个参数排在前面,则返回负数。
* 如果第二个参数排在前面,则返回正数。

n.sort(function (a, b) {    return a - b;});console.log(n);//[ 4, 8, 29, 33, 48, 50, 98 ]

上面的这个比较函数可以给数字排序,但不能为字符串排序。我们可以定义一个可以给任意简单值的数组进行排序的比较函数:

var m = ['a', 'cc', 'aa', 4, 8, 29, 33, 48, 50, 98];m.sort(function (a, b) {    if (a === b) {        return 0;    }    if (typeof a === typeof b) {        return a < b ? -1 : 1;    }    return typeof a < typeof b ? -1 : 1;});console.log(m);//[ 4, 8, 29, 33, 48, 50, 98, "a", "aa", "cc" ]

如果大小写不重要,应该在比较前,先把待比较的值都转化为小写。

现在编写一个更智能的比较函数,它能够对对象数组排序:

/** * 为对象数组排序 * @param name 以对象中的哪一个成员名排序 * @return 可进行排序的比较函数 */var by = function (name) {    return function (o, p) {        var a, b;        if (typeof o === 'object' && typeof p === 'object' && o && p) {            a = o[name];            b = p[name];            if (a === b) {                return 0;            }            if (typeof a === typeof b) {                return a < b ? -1 : 1;            }            return typeof a < typeof b ? -1 : 1;        } else {            throw{                name: 'Error',                message: 'Expected an object when sorting by ' + name            }        }    };};var s = [    {first: 'Joe', last: 'Besser'},    {first: 'Hoe', last: 'Howard'},    {first: 'Joe', last: 'Derita'},    {first: 'Shemp', last: 'Howard'},    {first: 'Larry', last: 'Fine'},    {first: 'Curly', last: 'Howard'}];s.sort(by('first'));console.log(s);

sort 方法是不稳定的,即排序后会改变相等值的相对位置。所以如果想基于多个键值进行排序,需要修改这个比较函数,让它可以接受第 2 个参数,当主要的键值产生了一个匹配时,另一个 compare 方法可以被调用,以决定最终的顺序:

/** * * @param name 以对象中的哪一个成员名排序 * @param minor 次要比较函数(可选) * @returns {Function} 可进行排序的比较函数 */var by = function (name, minor) {    return function (o, p) {        var a, b;        if (o && p && typeof o === 'object' && typeof p === 'object') {            a = o[name];            b = p[name];            if (a === b) {                return typeof minor === 'function' ? minor(o, p) : 0;            }            if (typeof a === typeof b) {                return a < b ? -1 : 1;            }            return typeof a < typeof b ? -1 : 1;        } else {            throw{                name: 'Error',                message: 'Expected an object when sorting by ' + name            }        }    };};s.sort(by('last', by('first')));console.log(s);

1.9 array.splice(start, deleteCount, item…)

注意:splice 与 slice 方法名很容易混淆,请注意区分。如果不记得 slice 方法了,可以把页面拉上去再看看哦O(∩_∩)O~

splice 方法从 array 中移除一个或多个元素,并用新的 item 来替换这些元素。参数 start 是需要移除元素的起始位置。deleteCount 是要移除元素的个数。如果有额外的参数,那么 item 会被插入到被移除元素的位置上。这个方法会返回一个包含被移除元素的数组。

splice 主要用途是从数组中删除元素:

var a = ['a', 'b', 'c'];var r = a.splice(1, 1, 'deniro', 'li');console.log(a);// [ "a", "deniro", "li", "c" ]console.log(r);// [ "b" ]

splice 可以像这样实现:

//start:需要移除元素的索引位置//deleteCount:要移除元素的个数Array.method('splice2', function (start, deleteCount) {    var max = Math.max,//最大值函数        min = Math.min,//最小值函数        delta,//增量        element,        insertCount = max(arguments.length - 2, 0),//需要插入的元素个数        k = 0,        len = this.length,//数组长度        new_len,//新数组长度        result = [],//返回结果集        shift_count;    start = start || 0;//设置需要移除元素的索引位置的默认值为 0    if (start < 0) {        start += len;//纠正起始索引为负值的情况    }    start = max(min(start, len), 0);//纠正起始索引为非正常值的情况    deleteCount = max(min(typeof deleteCount === 'number' ? deleteCount : len, len - start), 0);//纠正要移除元素的个数为非正常值的情况    delta = insertCount - deleteCount;    new_len = len + delta;    //把需要删除的元素放入返回结果集    while (k < deleteCount) {        element = this[start + k];        if (element !== undefined) {            result[k] = element;        }        k += 1;    }    shift_count = len - start - deleteCount;//原数组元素被替换的个数    if (delta < 0) {//数组变短        k = start + insertCount;        while (shift_count) {//从要删除的最后一个元素的下一个元素开始,都向前移动            this[k] = this[k - delta];            k += 1;            shift_count -= 1;        }        this.length = new_len;    } else if (delta > 0) {//数组变长        k = 1;        while (shift_count) {//从起始位置的下一元素开始到数组结尾,从后往前,把这些元素都向后移动            this[new_len - k] = this[len - k];            k += 1;            shift_count -= 1;        }        this.length = new_len;    }    //插入新元素    for (k = 0; k < insertCount; k += 1) {        this[start + k] = arguments[k + 2];    }    return result;});var a = ['a', 'b', 'c'];var r = a.splice2(1, 1, 'deniro', 'li');console.log(a);// [ "a", "deniro", "li", "c" ]console.log(r);// [ "b" ]

1.10 array.unshift(item…)

unshift 方法会把元素添加到数组的首部,并返回 array 新的 length:

var a = ['a', 'b', 'c'];var r = a.unshift('?', '@');console.log(a);//[ "?", "@", "a", "b", "c" ]console.log(r);//5

unshift 可以像这样实现:

Array.method('unshift2', function () {    this.splice.apply(this, [0, 0].concat(Array.prototype.slice.apply(arguments)));    return this.length;});var a = ['a', 'b', 'c'];var r = a.unshift2('?', '@');console.log(a);//[ "?", "@", "a", "b", "c" ]console.log(r);//5

2 function.apply(thisArg, argArray)

使用 array 方法调用 function,会传递一个绑定到 this 上的对象和一个可选的数组作为参数:

Function.method('bind2', function (that) {    var method = this;    return function () {        return method.apply(that, arguments);    };});var x = function () {    return this.value;}.bind2({value: 666});console.log(x());

3 Number

3.1 number.toExponential(fractionDigits)

toExponential 方法会把 number 转换为一个指数形式的字符串。可选参数 fractionDigits 控制小数点后的数字位数,它的值范围是 0 ~ 20:

console.log(Math.PI.toExponential(0));//3e+0console.log(Math.PI.toExponential(2));//3.14e+0console.log(Math.PI.toExponential(7));//3.1415927e+0console.log(Math.PI.toExponential(16));//3.1415926535897931e+0console.log(Math.PI.toExponential());//3.141592653589793e+0

3.2 number.toFixed(fractionDigits)

toFixed 方法会把 number 转换为一个十进制数形式的字符串。可选参数 fractionDigits 控制小数点后的数字位数,它的值范围是 0 ~ 20,默认为 0:

console.log(Math.PI.toFixed(0));//3console.log(Math.PI.toFixed(2));//3.14console.log(Math.PI.toFixed(7));//3.1415927console.log(Math.PI.toFixed(16));//3.1415926535897931console.log(Math.PI.toFixed());//3

3.3 number.toPrecision(precision)

toPrecision 方法会把 number 转换为一个十进制数形式的字符串。可选参数 precision 控制数字的精度,它的值的范围是 0 ~ 21:

console.log(Math.PI.toPrecision(2));//3.1console.log(Math.PI.toPrecision(7));//3.141593console.log(Math.PI.toPrecision(16));//3.141592653589793console.log(Math.PI.toPrecision());//3.141592653589793

3.4 number.toString(radix)

toString 方法会把 number 转换为一个字符串。可选参数 radix 控制基数,它的值的范围是 2 ~ 36。默认的 radix 是 10。radix 一般是整数,但也可以是任意数字。可以把 number.toString() 简写为 String(number)。

console.log(Math.PI.toString(2));//11.001001000011111101101010100010001000010110100011console.log(Math.PI.toString(8));//3.1103755242102643console.log(Math.PI.toString(16));//3.243f6a8885a3console.log(Math.PI.toString());//3.141592653589793

4 object.hasOwnProperty(name)

如果 object 中包含名为 name 的属性(不检查原型链中的同名属性),这个方法就返回 true。

 Object.create = function (o) {        var F = function () {        };        F.prototype = o;        return new F();    }var a = {member: true};var b = Object.create(a);console.log(a.hasOwnProperty('member'));//trueconsole.log(a.hasOwnProperty('false'));//falseconsole.log(b.hasOwnProperty('member'));//falseconsole.log(b.member);//true

5 RegExp

5.1 regexp.exec(string)

exec 方法是正则表达式中最强大、也是运行最慢的方法。如果它成功匹配了 regexp 和 string,它就会返回一个数组 array。array[0] 包含 regexp 匹配的子字符串。array[1] 是分组 1 捕获的文本,array[2] 是分组 2 捕获的文本,以此类推。如果匹配失败,会返回 null。

如果 regexp 带有 g 标识(全局标识),那么查找会从 regexp.lastIndex(初始值为 0)位置开始。如果匹配成功,那么 regexp.lastIndex 会被设置为这个匹配后的第一个字符位置。匹配不成功,会重置 regexp.lastIndex 为 0。利用这样的设计,就可以循环调用 exec 来查询一个匹配模式在一个字符串中发生了几次。但要注意两点:
* 如果提前退出了循环,再次进入这个循环之前,必须把 regexp.lastIndex 重置为 0。
* ^ 仅匹配 regexp.lastIndex 为 0 的情况。

/** * regexp.text(string) */var b = /&.+;/.test('frank & beans');console.log(b);//true/** * 把 HTML 文本分解为标签和文本 * 产生这样一个数组: * [0] 匹配的标签和文本 * [1] /(斜杠);如果有 * [2] 标签名 * [3] 属性;如果有 */var text = '<html><body bgcolor="linen"><p>This is <b>bold</b>!</p></body></html>';var tags = /[^<>]+|<(\/?)([A-Za-z]+)([^<>]*)>/g;var a, i;while ((a = tags.exec(text))) {    for (i = 0; i < a.length; i += 1) {        console.log((('// [' + i + '] ' + a[i])));    }}

分解为标签和文本的结果

5.2 regexp.test(string)

test 方法是使用正则表达式最简单也是最快的方法。如果 regexp 匹配 string,它会返回 true;否则返回 false。

var b = /&.+;/.test('frank &amp; beans');console.log(b);//true

test 可以像这样实现:

RegExp.method('test2', function (string) {    return this.exec(string) !== null;});console.log(/&.+;/.test2('frank &amp; beans'));//true

6 String

6.1 string.charAt(pos)

charAt 方法会返回 string 中 pos 位置处的字符。如果 pos 小于 0 或大等于 string 的长度,这个方法会返回空字符串。JavaScript 没有字符类型,所以这个方法返回的结果是字符串:

var name = "Deniro";console.log(name.charAt(0));//D

charAt 可以像这样实现:

String.method('charAt2', function (pos) {    return this.slice(pos, pos + 1);});console.log(name.charAt2(0));//D

6.2 string.charCodeAt(pos)

charCodeAt 方法会返回 string 中 pos 位置处的字符所对应的字符码位(整数形式)。如果 pos 小于 0 或大等于 string 的长度,这个方法会返回 NaN。

var name = "Deniro";console.log(name.charCodeAt(0));//68

6.3 string.concat(string…)

concat 方法会把其他字符串连接起来,构造一个新的字符串。它很少使用,因为使用 + 运算符更方便。

console.log('C'.concat('a', 't'));//Cat

6.4 string.indexOf(searchString,position)

indexOf 方法在 string 中查找字符串 searchString。如果被找到,就返回第一个匹配字符的位置,否则返回 -1。可选参数 position 可设置从 string 的某个指定位置开始查找:

var text = 'Denironi';console.log(text.indexOf('ni'));//2console.log(text.indexOf('ni', 4));//6console.log(text.indexOf('ni', 8));//-1

6.5 string.lastIndexOf(searchString,position)

lastIndexOf 方法和 indexOf 方法类似,只是它是从字符串的末尾开始查找的:

var text = 'Denironi';console.log(text.lastIndexOf('ni'));//6console.log(text.lastIndexOf('ni', 4));//2console.log(text.lastIndexOf('ni', 8));//6

6.6 string.localeCompare(that)

localeCompare 方法比较两个字符串。类似 array.sort 比较函数的约定:

var m = ['AAA', 'A', 'aa', 'a', 'Aa', 'aaa'];m.sort(function (a, b) {    return a.localeCompare(b);});console.log(m);//[ "a", "A", "aa", "Aa", "aaa", "AAA" ]

6.7 string.match(regexp)

这个方法根据 g 标识来决定如何匹配:
* 如果没有 g 标识 - 那么调用 string.match(regexp) 的结果与调用 regexp.exec(string) 的结果相同。
* 如果带有 g 标识 - 那么这个方法会返回一个包含所有匹配(除了捕获分组之外)的数组。

var text = '<html><body bgcolor="#faf0e6"><p>This is <b>bold</b>!</p></body></html> ';var tags = /[^<>]+|<(\/?)([A-Za-z]+)([^<>]*)>/g;var a, i;a = text.match(tags);for (i = 0; i < a.length; i += 1) {    console.log('[' + i + '] ' + a[i]);}

6.8 string.replace(searchValue, replaceValue)

replace 方法会对 string 进行查找与替换,并返回一个新的字符串。参数 searchValue 可以是一个字符串或一个正则表达式的对象。如果它是一个字符串,那么 searchValue 只会在第 1 次出现的地方被替换:

console.log("mother_in_law".replace('_', '-'));//mother-in_law

这恐怕不是你想要的结果哦o( ̄︶ ̄)o

replaceValue 可以是字符串或函数。如果是字符串,那么字符 $ 有着特别的含义:

美元符号序列 替换对象 $$ $ $^ 整个匹配的文本 $number 分组捕获的文本 $' 匹配后的文本
$`:匹配之前的文本

因为符号太特殊咯,所以不得不写在这里O(∩_∩)O~

var oldareacode = /\((\d{3})\)/g;console.log('(555)666-1212'.replace(oldareacode, '$1-'));//555-666-1212

如果 replaceValue 是一个函数,那么每遇到一次匹配,就会调用一次函数,该函数的返回值将会被作为替换文本。传递给这个函数的第一个参数是整个被匹配的文本,第二个参数是分组 1 捕获的文本,第三个参数是分组 2 捕获的文本,以此类推。

String.method('entityify', function () {    var character = {        '<': '&lt;',        '>': '&gt;',        '&': '&amp;',        '"': '&quot;'    };    return function () {        return this.replace(/[<>&"]/g, function (c) {            return character[c];        });    };}());console.log("entityify:"+("<&>".entityify()));//&lt;&amp;&gt;

6.9 string.search(regexp)

search 方法与 indexOf 方法类似,但它只接受一个正则表达式对象作为参数。如果找到,会返回第一个匹配的首字符位置;如果没有找到,就返回 -1。该方法会忽略 g 标识,它也没有 position 参数哦O(∩_∩)O~

var text = 'and in it he says "Any damn fool could';console.log(text.search(/["']/));//18

6.10 string.slice(start, end)

slice 方法会复制 string 的一部分来构造新的字符串。如果 start 是负数,那么它会与 string.length 相加,试图让它变为正数。end 参数是可选的,默认值是 string.length。如果 end 是负数,那么它也会与 string.length 相加。end 参数是你要取得的最后一个字符的位置值加 1。如果想要取从位置 p 开始的 n 个字符,那么可以这样做:string.slice(p, p+n)。

console.log(text.slice(18));//"Any damn fool couldconsole.log(text.slice(0, 3));//andconsole.log(text.slice(-5));//couldconsole.log(text.slice(19, 32));//Any damn fool

6.11 string.split(separator, limit)

split 方法会把 string 分割成片段,然后返回这些片段组成的数组。可选参数 limit 会限制被分割的片段数量。separator 参数可以是字符串,也可以是正则表达式。

如果 separator 参数是一个空字符,那么这个方法会返回一个单字符的数组:

var digits = '0123456789';console.log(digits.split('', 5));//[ "0", "1", "2", "3", "4" ]

这个方法会忽略 g 标识,并把分隔符两边的文本都复制到要返回的数组中:

console.log('192.168.0.1'.split('.'));//[ "192", "168", "0", "1" ]console.log('|a|b|c|'.split('|')); //[ "", "a", "b", "c", "" ]var text = 'last, first, middle';console.log(text.split(/\s*,\s*/));//[ "last", "first", "middle" ]

注意:来自分组捕获的文本也会被包含在被分割后的数组中:

console.log(text.split(/\s*(,)\s*/));//[ "last", ",", "first", ",", "middle" ]

当 separator 是一个正则表达式时,老的浏览器(比如 IE8)会在输出数组中排除掉空字符串。新的浏览器就会保留:

console.log('|a|b|c|'.split(/\|/));//[ "", "a", "b", "c", "" ]

6.12 String.fromCharCode(char…)

这个方法会根据数字编码返回一个字符串:

console.log(String.fromCharCode(67,97,116));//Cat

6.13 string.substring(start, end)

substring 方法与 slice 相同,但它不能处理负数的参数,所以请使用 slice 方法哦O(∩_∩)O~

阅读全文
0 0
原创粉丝点击