24

我遇到了以下代码:

var f = function () {
    var args = Array.prototype.slice.call(arguments).splice(1);

    // some more code 
};

基本上,结果args是一个数组,它是arguments没有第一个元素的副本。

但是我不能完全理解的是为什么f's arguments(这是一个将函数的输入参数保存到类似数组的对象中的对象)对象被传递给slice方法以及如何slice(1)删除第一个元素(位于索引 0 处) .

谁能为我解释一下?

PS 代码来自这个部分应用函数

4

3 回答 3

41

<注意>
链接答案的实际代码是:

var args = Array.prototype.slice.call(arguments, 1);

即“切片”,而不是“拼接”
</Note>

首先,该slice方法通常用于制作它所调用的数组的副本

var a = ['a', 'b', 'c'];
var b = a.slice();  // b is now a copy of a
var c = a.slice(1); // c is now ['b', 'c']

所以简短的回答是代码基本上是在模拟:

arguments.slice(1); // discard 1st argument, gimme the rest

但是,您不能直接这样做。特殊arguments对象(在所有 JavaScript 函数的执行上下文中可用)虽然类似于Array ,因为它支持通过[]带有数字键的运算符进行索引,但实际上并不是 Array;你不能.push上它,.pop下它,或.slice它,等等。

代码实现这一点的方式是通过“欺骗”函数(在对象slice上再次不可用)在 的上下文中运行,通过:arguments argumentsFunction.prototype.call

Array.prototype.slice // get a reference to the slice method
                      // available on all Arrays, then...
  .call(              // call it, ...
    arguments,        // making "this" point to arguments inside slice, and...
    1                 // pass 1 to slice as the first argument
  )

Array.prototype.slice.call(arguments).splice(1)完成同样的事情,但是对 进行了无关的调用splice(1),这会从返回的数组中删除元素,该数组从Array.prototype.slice.call(arguments)索引开始1并继续到数组的末尾。splice(1)在 IE 中不起作用(从技术上讲,它缺少第二个参数,告诉它删除 IE 和 ECMAScript 需要多少项)。

于 2009-11-22T03:49:12.853 回答
3
var args = Array.prototype.slice.call(arguments).splice(1);

首先获取 (*) 的副本arguments,然后从其中删除除第一个项目之外的所有项目(以非标准方式),并将这些项目分配给args.

产生的额外数组,然后改变和丢弃是非常多余的。最好说 - 正如您链接到的答案中的版本确实如此:

var args = Array.prototype.slice.call(arguments, 1);

偏函数应用也是该function.bind方法的一个特点,由 ECMAScript 第五版标准化。在浏览器实现它之前,您可以从这个答案的底部选择一个后备 JS-native 版本。

*:array.slice()是复制数组和array.slice(1)取尾的常用习语。它可以通过 Array 显式调用,Array.prototype因为arguments它不是一个数组,即使它看起来就像一个数组,所以没有普通的数组方法。这是 JavaScript 的另一个奇怪错误。

你经常看到人们Array.prototype在不是数组的对象上使用方法;ECMAScript 第三版标准特意说这对于arguments类数组是可以的,但并不是说您也可以在可能是宿主对象的其他类数组上这样做,例如 NodeList 或 HTMLCollection。尽管Array.prototype今天在许多浏览器中您可能会在非数组上调用方法,但实际上唯一安全的地方是在arguments.

于 2009-11-22T04:00:03.077 回答
0

拼接的返回值是一个已删除元素的数组,但原始数组(或类似数组的对象)在拼接索引处被截断。

使用 slice 进行复制会保留原始参数数组,大概是为了稍后在函数中使用。

在这种情况下,可以得到相同的结果args = [].slice.call(arguments, 1)

function handleArguments(){
 var A= [].slice.call(arguments).splice(1);
 //arguments is unchanged
 var s= 'A='+A+'\narguments.length='+arguments.length;

 var B= [].splice.call(arguments, 1);
 // arguments now contains only the first parameter
 s+= '\n\nB='+B+'\narguments.length='+arguments.length;
 return s;
}

// test
alert(handleArguments(1, 2, 3, 4));

returned value:
//var A= [].slice.call(arguments).splice(1);
A=2,3,4
arguments.length=4

//var B= [].splice.call(arguments, 1);
B=2,3,4
arguments.length=1
于 2009-11-22T05:16:52.837 回答