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")

博文对你有帮助吗?如果有的话,想不想送我一本书呢?
  目录