JavaScript中Array.prototype.slice.call(arguments)的疑问

今天看到这个用法的时候产生了疑问,

slice 方法可以用来将一个类数组(Array-like)对象/集合转换成一个数组。你只需将该方法绑定到这个对象上。

mdn上给出了一个例子:

function list() {
  return Array.prototype.slice.call(arguments);
}

var list1 = list(1, 2, 3); // [1, 2, 3]

我的问题是:

  • 平时更容易遇到fun.call (null, arguments)这样的用法,因为Function.prototype.call()的用法是

fun.call(thisArg[, arg1[, arg2[, ...]]])

所以遇到把Array.prototype.slice.call(arguments)这样的把arguments作为在函数运行时指定的this值感觉很别扭,总是想成Array.prototype.slice.call(null, arguments)这样的,又不知道这样为什么不对?

  • 除了Array.prototype.slice.call(arguments)这样的用法,那么Array.prototype上其他的方法是不是也可以这样使用呢?比如Array.prototype.splice.call(arguments,1)

阅读 8.2k
8 个回答

谢邀。以下是我个人的理解。如果有错误请指正。

首先一点,你可能已经知道了,.slice 是数组方法,可以用来深拷贝数组。如果直接赋值就是浅拷贝

然后,看下 arguments 长啥样儿的就懂了。。JS 中,两种常见的 "array-like object"(像数组一样的对象)一个是 arguments,一个就是 nodeList

他们长这样:

var obj = {
    '0': 'a',
    '1': 'b',
    '2': 'c',
    '3': 'd',
    length: 4
}

如果一个东西满足这两点:

  • 有数字形式的 key,从 0 开始

  • 有一个 length 属性

那么这个东西就是 "array-like object"。"array-like object" 至少有一个好处,就是可以直接构建一个 Array 实例。可以通过 Array.from() 或者 ES6 的 "Spread Operator"(也就是 ...)来构建。

注意:JS 中只允许字符串作为对象的 key,因此才会是上面的写法。就算你尝试着用数字去做 key,它也会帮你转成字符串的。

由于这个东西本身还是 object,或者说它不是 Array 的一个 instance(实例),因此,没法直接调用 .slice 方法。

Array.prototype.slice 返回的是一个 Function 实例,调用它的 call 方法是为了改变 .slice 方法的 this,也就是说,改变一下谁来调用 slice

因此,.call(arguments) 这个写法并不比别扭。你如果第一个参数传入 null 是肯定不行的,因为 null 既没有 .slice 方法,也不能构建成一个 array。"array-like object" 虽然也没有 slice 方法,但至少它有上面说到的 "数字key" 和 "length" 属性,因此可以构建一个 array

个人看来,不妨把它简单理解为:

Array.prototype.slice(arguments) 做的事儿就是:

var temp = Array.from(arguments)
temp.slice()

至于你提到的 Array.prototype.splice.call(arguments,1),没有问题。把 arguments 作为 this,就用 arguments 构建一个 Array 实例,也就有了 splice 方法。不妨也把它理解成:

var temp = Array.from(arguments);
temp.splice(1)

我分享一下我的想法吧,仅供参考:

第一个问题:
在js中用call去掉用函数传的的一个参数就是调用这个函数的对象(即被调用函数的this指向),应了这句this谁调用它就指向谁。然后第二个参数开始就是传入函数里的参数。
在你上面的代码中:Array.prototype.slice.call(null, arguments)相当于是用null去调用slice函数,把arguments作为函数的参数。null是调用不了这个slice函数的因为null既不是数组也不是类数组,所以会报错。
第二个问题:
可以的,如下图:
clipboard.png

最后附上个链接(翻到文章最下面的小拓展部分)

Array.prototype.slice.call(arguments)这里其实是把arguments设置成函数slicethis,也就是对arguments进行slice操作,从而达到把传入的参数变成array的目的。而Array.prototype.slice.call(null, arguments)其实是null.slice(arguments), 这个就会throw TypeError了吧

这个问题分成两部分解释

  1. 为什么使用call
    你已经了解到

Function.prototype.call()的用法是

/*
 * 分析传入参数的作用
 * this: 设置func中的上下文/this值
 * args*: 传入的参数
 */
func.call(this[, args1[, args2[, args3]]]);

另外还知道slice的相关用法

slice 方法可以用来将一个类数组(Array-like)对象/集合转换成一个数组。你只需将该方法绑定到这个对象上。

OK,(敲黑板)(划重点)这里使用call并不是那么符合语境的(摔),应该使用绑定

Array.prototype.slice.~~绑定~~bind(arguments)

其本质和

Array.prototype.slice.call(arguments);

是一样的,之所以这样使用,是因为

arguments.slice();// 这样写报错。。。arguments并没有slice方法
// 那么我们如何令arguments能够调用slice方法呢?
// 正常情况下是一个数组对象var arr = [1, 2, 3]来进行调用
// arr.slice() // [1, 2, 3] 正确,然而没什么用
// 我们知道,是数组arr调用的slice方法,这个调用方,我们称他为上下文
// 接下来就简单了,这就是为什么要用call/bind的原因
  1. arguments是什么,什么叫类数组对象
    arguments: 全称Function.prototype.arguments(有的地方说是有区别,求哪位大神明示。)

arguments完全不是一个数组,虽然多多少少有点像。在很多情况下,尽管不是,我们还是希望把它当作数组来处理。把arguments转换成一个数组
apply: 第二个参数可以直接使用类数组对象
callee: 可以通过arguments的callee方法找到arguments的所在方法(如果觉得这句话比较晕,可以忽略)

其实除去arguments外,还有一些类数组对象,举个栗子

var arr_like = {'0':'Mike', '1': 'Joy', '2': 'Brown', lenght: 3};// lenght属性很重要,计数要从0开始,记得要加引号,纯数字或以数字开头的属性都要写成字符串的形式,取值时也要加引号(纯数字可以不加)
Array.prototype.slice.call(arr_like); // ['Mike', 'Joy', 'Brown'];
Array.prototype.join.call(arr_like, ' love ');// "Mike love Joy love Brown"

这就是一个简单的类数组对象啦。
说的比较杂,希望能帮到你一二

谢邀,感觉楼上兄弟们回答的都很好。

先说一下[].slice(start, end)接收的参数,start为开始索引,end为结束索引,而且取前不取后,如果省略参数,则拷贝一个数组的副本。

前面你也说了call的用法,fn.call(thisArgthisArg[, arg1[, arg2[, ...]]]);

下面我们用call调用slice方法:对应开始填参数————第一个为要截取的对象(arguments对象),第二个为开始索引,第三个参数为结束索引。

var slice = Array.prototype.slice;
slice.call(arguments, start, end);

下面我们省略start和end

Array.prototype.slice(arguments);

类似的,我们使用document.getElementsByTagName获得的也是一个类数组,我们可以把他转为原生数组

var list = [].slice.call(documengetElementsByTagName('div'));

我感觉你是自己对于数组的方法没有深入的理解,而不是call的用法,理解好数组方法的各个参数的意义,对应着填到call或者apply的参数位置,也就好理解了。

我也来说说把。

Array.prototype.slice.call(arguments); 这样子 其实你可以理解为 arguments.slice(); 这样 slice 的 this 指向了 arguments 。

而又因为 arguments 具备了数组的基本特征:

具有名字为 '0' '1' '2' 这样的属性, 而且 也具备 length 属性。

因此 slice 可以在 arguments 上正常运行 (this 指向 arguments ) 因此能返回数组

新手上路,请多包涵

Array.prototype.slice.call(arguments)相当于类数组arguments继承了slice方法拥有了数组的属性,即arguments.slice()

[].slice.call(arguments)或者Array.from(arguments)都可以将类数组转换成数组。

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏