Array.prototype.slice.call() 方法详解
在很多时候经常看到
Array.prototype.slice.call()
方法,比如Array.prototype.slice.call(arguments)
,本文讲一下其原理。
一、slice 方法
在 JavaScript 中 Array 是一个类,slice 是这个类里的一个方法,那么这个方法的调用方式应该是:Array.prototype.slice
。
slice 从字面意思上理解就是截取,它的用法是:arrayObj.slice(begin, [end])
,很显然是截取数组的一部分(包括 begin
,不包括 end
)。调用后将返回一个新的数组对象,是一个浅拷贝,原始数组不会被改变。
二、call 方法
call 方法使用一个指定的 this
值和单独给出的一个或多个参数来调用一个函数,基本用法如下:
// call()
// obj: 这个对象将代替 func 里 this 对象
// param1 ~ paramN: 这是一个参数列表
func.call(obj, param1, …, paramN)
三、Array.prototype.slice.call(arguments)
在 ES5 标准中,我们经常需要把 arguments
对象转换成真正的数组。
什么叫真正的数组呢?实际上,typeof arguments
打印出的结果是 object
,它并不是一个真正的数组,而是与数组类似对象。
Array.prototype.slice.call(arguments)
方法能够将一个具有 length
属性的对象转换为数组。比如我自己定义一个具有 length
属性的对象:
let obj = { 0: 'bob', 1: '12', 2: 'male', length: 3}
console.log(Array.prototype.slice.call(obj)) // ["bob", "12", "male"]
四、实现原理解析
Array.prototype.slice.call(arguments)
原本调用 slice 的是 Array.prototype
,而 call(arguments)
使得调用 slice 方法的对象改成 arguments
。
我们可能会想 arguments
原型对象是 Object.prototype
,并没有 slice 方法,slice 方法从哪里来?
这是因为 call(arguments)
不仅是改变了 this
的指向,还使得 arguments
对象继承了 Array.prototype
中的 slice 方法。
Array.prototype.slice()
源码:指路 github 地址 587 行。
五、ES5 的其它写法
// 你可以这样写
Array.prototype.slice.call(arguments)
// 你还可以这样写
[].slice.call(arguments)
// 你要是不怕麻烦,你还可以这样写
[].__proto__.slice.call(arguments)
// 当你了解原型链,你就知道
Array.prototype === [].__proto // true
// [].slice 调用的是实例[]的原型对象中的 slice 方法
[].slice === [].__proto__.slice // true
六、ES6 的新写法
1. Array.from
ES6 中提供了一个等价的方法实现上述功能 Array.from(arguments)
。
Array.from
方法用于将两类对象转为真正的数组:类似数组的对象(array-like object)和可遍历(iterable)的对象(包括 ES6 新增的数据结构 Set 和 Map)。
例如:
let bar = new Set(['a', 'b', 'c'])
Array.from(bar ) // ['a', 'b', 'c']
2. 扩展运算符(…)
ES6 的语法中还提供了一个神器,叫做扩展运算符(...)
,它也可以将某些数据结构转换为数组。
例如:
function foo(a, b, c, d) {
// console.log(Array.prototype.slice.call(arguments))
// console.log(Array.from(arguments))
console.log([...arguments]) // ["a", "b", "c", "d"]
}
foo("a", "b", "c", "d")