Javascript中,Array对象的slice方法通常用于获得数组的一部分.常规的使用我就不探讨了,我们谈点特别的。
1.复制数组
用slice方法来复制一个数组实在是太方便了。看代码:
var a = [1, 2, 3];
var b = a.slice();
console.log(b); //-> [1, 2, 3]
没错,我们只要简简单的slice一下,不传递任何参数,就得到了一个新的数组。
当然,复制数组还有其他简便的方式,比如运用concat方法:
var a = [1, 2, 3];
var b = [].concat(a);
console.log(b); //->[1, 2, 3]
选哪种方法取决于你的喜好了:-)
2.将"类数组对象"转换成数组对象
Update:此方法在IE下无效!
所谓类数组对象,就是具有数组的某些特征,最基本的就是拥有索引元素和长度属性,比如function对象的
arguments对象,还有通过dom方法getElementsByTagName()获得的Html元素集。它们都具有数组的特征,比如可以通过下标访问以及拥有length属性,但是它们又不是真正的数组,不具备数组对象的其他方法(如push,pop,concat,slice,splice等)。
尽管大多数时候我们并不要求它们是一个真正的数组,但有时候确实非常需要(相信你碰到过),
Douglas Crockford也说,没有把arguments对象实现成一个真正的数组对象是Javascript语言设计的一个错误。那么怎么把这些"类数组对象"快速转换成一个数组呢?用slice方法吧!
但是我们无法直接在这些"类数组对象"上运用slice方法,因为他们压根就不是数组!ok,那我们就采用apply调用的方式,如下:
var realArray = Array.prototype.
slice.apply(arrayLike);
我们来实践一下:
function test(a, b, c) {
arguments = Array.prototype.slice.apply(arguments);
console.log(arguments instanceof
Array);
}
test(1, 2, 3); //-> true
再看一个例子:
var elements = document.getElementsByTagName('*');
elements = Array.prototype.slice.apply(elements);
console.log(elements instanceof Array); //->true
更狠的:
var someObj = {
'0' : 'hello',
'1' : 'world',
'length' : 2
};
var arrayObj = Array.prototype.slice.apply(someObj);
console.log(arrayObj); //-> ['hello','world']
可以看到,只要“类数组对象”具有索引元素和length,slice方法就能把它们转换成一个真正的数组。
如果你觉得Array.prototype.slice.apply()的写法太麻烦了,好办,我们可以绑定到Array.prototype上面
Array.prototype.makeArray= function (arrayLike) {
if ( !arrayLike || !arrayLike.length || !/^\d+$/.test('' + arrayLike.length) ) {
return []; //如果arrayLike为undefined,或者它没有length属性,或者它的length属性不是数字,返回空数组
}
return this.slice.apply(arrayLike);
};
这样,我们就能以下面的方式调用了:
var realAray = [].makeArray(arrayLike);
似乎和上面复制数组的方式相似:-)
ok,让我们和以前繁琐的处理方式说拜拜吧!
Update
多谢
Erik提醒,我找到
Ecma_262文档,查看了slice方法的内部实现方式,指南如下:
15.4.4.10 Array.prototype.slice (start, end)
The slice method takes two arguments, start and end, and returns an array containing the elements
of the array from element start up to, but not including, element end (or through the end of the array
if end is undefined). If start is negative, it is treated as (length+start)where length is the length of
the array. If end is negative, it is treated as (length+end)where length is the length of the array. The
following steps are taken:
1. Let A be a new array created as if by the expression new Array().
创建一个新数组A。
2. Call the [[Get]] method of this object with argument "length".
调用当前对象的[[Get]]方法,参数为'length'.
3. Call ToUint32(Result(2)).
将Result(2),即length,转换为无符号32位整数。
4. Call ToInteger(start).
将start转换成整数。
5. If Result(4) is negative, use max((Result(3)+Result(4)),0); else use min(Result(4),Result(3))
如果start为负数,使用0和(length + start)中较大的一个;否则,使用start和length中较小的一个。
6. Let k be Result(5).
将 k 赋值为第5步计算结果,即 k = start.
7. If end is undefined, use Result(3); else use ToInteger(end).
如果end未定义(没有传递第二个参数),那么end值为length,否则,将第二个参数转换成整数并赋值给end
8. If Result(7) is negative, use max((Result(3)+Result(7)),0); else use min(Result(7),Result(3))
如果end为负,将赋值为 0 和 (length + end) 中较大的一个;否则,赋值为 end 和 length中较小的一个。
9. Let n be 0.
将n赋值为0.
10. If k is greater than or equal to Result(8), go to step 19.
如果 k, 即start,大于等于 第8部结果,即end,转到第19步.
11. Call ToString(k).
将K 转换成字符串。
12. If this object has a property named by Result(11), go to step 13; but if this object has no property named by Result(11), then go to step 16.
如果该对象拥有属性K,转到第 13 步;如果没有,转到第16步。
13. Call ToString(n).
将n转换成字符串.
14. Call the [[Get]] method of this object with argument Result(11).
调用当前对象的[[Get]]方法,参数为第11步结果,即k.
15. Call the [[Put]] method of A with arguments Result(13) and Result(14).
调用数组A的[[Put]]方法,参数为n和k
16. Increase k by 1.
k增加1.
17. Increase n by 1.
n增加1
18. Go to step 10.
转到第10步
19. Call the [[Put]] method of A with arguments "length" and n.
调用数组A的[[put]]方法,参数为'length' 和 n.
20. Return A.
返回数组A.
从上面我们可以看出,slice方法返回的是一个新数组,被转换的对象必须拥有length属性,如果没有length属性,其行为是未定义的,第2步并没有指明如果读取length属性失败怎么办,所幸的是,Mozilla和IE都将返回一个空数组而不是抛出异常。
还有一个意外是,当读取转换对象某个索引属性值失败时,比如读取Obj[1],但是obj并没有属性1,按照指南,新数组将不会新增元素,但是Mozilla和IE都不一而同的给新数组push了一个undefined.
看代码:
var testObj = {
'1' : 'hell',
'3' : 'world',
'length' : '4'
}
Array.prototype.slice.apply(testObj); //-> [undefined,'hell',undefined,'world']