今天在使用 Array.prototype.slice.call(arguments);
的时候,突然有一个疑问?为什么 slice() 方法可以将类数组对象/集合转换成一个新数组?
slice()介绍
我们知道,slice() 方法返回一个从开始到结束(不包括结束)选择的数组的一部分浅拷贝到一个新数组对象。且原始数组不会被修改。
基本语法为:
1 | arr.slice(); |
参数:
begin 可选:
- 从该索引处开始提取原数组中的元素(从0开始)。
- 如果该参数为负数,则表示从原数组中的倒数第几个元素开始提取,slice(-2)表示提取原数组中的倒数第二个元素到最后一个元素(包含最后一个元素)。
- 如果省略 begin,则 slice 从索引 0 开始。
end 可选:
- 在该索引处结束提取原数组元素(从0开始)。slice会提取原数组中索引从 begin 到 end 的所有元素(包含begin,但不包含end)。
- slice(1,4) 提取原数组中的第二个元素开始直到第四个元素的所有元素 (索引为 1, 2, 3的元素)。
- 如果该参数为负数, 则它表示在原数组中的倒数第几个元素结束抽取。 slice(-2,-1)表示抽取了原数组中的倒数第二个元素到最后一个元素(不包含最后一个元素,也就是只有倒数第二个元素)。
- 如果 end 被省略,则slice 会一直提取到原数组末尾。
- 如果 end 大于数组长度,slice 也会一直提取到原数组末尾。
返回值为一个含有提取元素的新数组
slice 不修改原数组,只会返回一个浅复制了原数组中的元素的一个新数组。原数组的元素会按照下述规则拷贝:
- 如果该元素是个对象引用 (不是实际的对象),slice 会拷贝这个对象引用到新的数组里。两个对象引用都引用了同一个对象。如果被引用的对象发生改变,则新的和原来的数组中的这个元素也会发生改变。
- 对于字符串、数字及布尔值来说(不是 String、Number 或者 Boolean 对象),slice 会拷贝这些值到新的数组里。在别的数组里修改这些字符串或数字或是布尔值,将不会影响另一个数组。
如果向两个数组任一中添加了新元素,则另一个不会受到影响。
slice() 转换类数组
slice 方法可以用来将一个类数组(Array-like)对象/集合转换成一个新数组。你只需将该方法绑定到这个对象上。 一个函数中的 arguments 就是一个类数组对象的例子:
1 | function list() { |
除了使用 Array.prototype.slice.call(arguments),你也可以简单的使用 [].slice.call(arguments) 来代替。另外,你可以使用 bind 来简化该过程:
1 | var unboundSlice = Array.prototype.slice; |
需要注意的是,IE9以下的IE浏览器的节点集合(因为ie8及之前版本的dom对象是以com对象的形式实现的,js对象与com对象不能进行转换):
1 | var a={length:2,0:'first',1:'second'}; |
针对于 slice() 方法的语法和参数规则,可能很难理解这句为什么能实现以上的转换功能,我就是一个。
首先,slice有两个用法,一个是String.slice,一个是Array.slice,第一个返回的是字符串,第二个返回的是数组,这里我们看第2个。
Array.prototype.slice.call(arguments) 能够将arguments转成数组,那么就是 arguments.toArray().slice();到这里,是不是就可以说 Array.prototype.slice.call(arguments) 的过程就是先将传入进来的第一个参数转为数组,再调用slice?
再看call的用法,如下例子:
1 | var a = function(){ |
可以看出,call了后,就把当前函数推入所传参数的作用域中去了,不知道这样说对不对,但反正this就指向了所传进去的对象就肯定的了。
到这里,基本就差不多了,我们可以大胆猜一下slice的内部实现,如下:
1 | Array.prototype.slice = function(start,end){ |
1 | /** |
我们看看到第23行至24行的描述:
1 | // IE < 9 gets unhappy with an undefined end argument |
到这里,可以证明我们上面的猜测是对的,我相信,你应该和我一样加深了对 slice() 的理解,同时也解决了之前的疑惑了吧~
完~