关于 JavaScript 的 slice 和 splice 方法的一个问题

Posted

技术标签:

【中文标题】关于 JavaScript 的 slice 和 splice 方法的一个问题【英文标题】:A question about JavaScript's slice and splice methods 【发布时间】:2010-12-19 03:51:43 【问题描述】:

我遇到了以下代码:

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

    // some more code 
;

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

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

谁能帮我解释一下?

附注代码来自这个partial application function

【问题讨论】:

Array.splice(start, deleteCount) 实际上是删除元素,重新索引数组,并更改其长度。 【参考方案1】:

linked answer 的实际代码是:

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

即“切片”,而不是“拼接”注>

首先slice方法常用于make a copy of the array it's called on:

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

但是你不能直接这样做。 special arguments object(在所有 javascript 函数的执行上下文中可用)虽然是 Array-like,因为它支持通过带有数字键的 [] 运算符进行索引,但它实际上并不是一个 Array;你不能.push 上它,.pop 关闭它,或者.slice 它等等。

代码实现这一点的方式是通过“欺骗”slice 函数(在 arguments 对象上同样不可用)以在arguments 的上下文中运行,通过Function.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 需要多少项)。

【讨论】:

对不起代码差异,但我从我在这个问题中链接到的答案中复制了我粘贴在这里的代码***.com/questions/373157/…,但作者同时更改了脚本。再次抱歉 @Crescent Flesh,我将问题中的链接更改为指向原始代码 新月新鲜,在肉体中。 @Andreas,是的,我看到了。我相信我的最后一段也提到了splice(1),是吗? 有些事情困扰着我,我必须把它说出来:slice() 应该接受争论。它在今天的所有浏览器中都是无参数的,这就是为什么我把它留在我的答案中。但从技术上讲,最后一个示例 Array.prototype.slice.call(arguments) 应该是 Array.prototype.slice.call(arguments, 0)(即,将 0 作为第一个参数传递给 slice 函数)。如果省略,它基本上默认为 0(同样,在所有浏览器中)。 这是我最近记忆中代码工作原理的最佳分步详细分类。很棒的东西,谢谢。【参考方案2】:
var args = Array.prototype.slice.call(arguments).splice(1);

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

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

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

部分函数应用也是function.bind 方法的一个特性,由ECMAScript 第五版标准化。在浏览器实现之前,您可以从this answer 底部选择一个备用的 JS-native 版本。

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

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

【讨论】:

ES5 现在明确记录了许多 Array.prototype 方法来处理 any 类似数组的对象,因此依靠它继续工作应该是安全的。跨度> 【参考方案3】:

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

使用 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

【讨论】:

以上是关于关于 JavaScript 的 slice 和 splice 方法的一个问题的主要内容,如果未能解决你的问题,请参考以下文章

javascript中slice和substring的区别

javascript slice字符串切片函数

JavaScript slice() 和 splice()

javascript中不易分清的slice,splice和split三个函数

JavaScript中的Array.prototype.slice.call()方法学习

JavaScript slice() 方法