Array.prototype.slice.call()如何工作?

ily*_*lyo 457 javascript prototype-programming

我知道它用于使参数成为一个真正的数组,但我不明白使用时会发生什么 Array.prototype.slice.call(arguments)

use*_*716 848

在幕后发生的事情是,当.slice()正常调用时,this是一个数组,然后它只是遍历该数组,并完成它的工作.

如何this.slice()函数中使用数组?因为当你这样做时:

object.method();
Run Code Online (Sandbox Code Playgroud)

...该object自动成为价值thismethod().所以:

[1,2,3].slice()
Run Code Online (Sandbox Code Playgroud)

...将[1,2,3]数组设置为thisin 的值.slice().


但是,如果你可以用其他东西替代this价值呢?只要你替换的任何东西都有一个数字.length属性,并且有一堆属性是数字索引,它应该可以工作.这种类型的对象通常称为类似数组的对象.

使用.call().apply()方法可以手动设置this函数的值.因此,如果我们设定的值,this.slice()一个类似数组的对象,.slice()将只承担它的工作与Array,并会做它的东西.

以此普通对象为例.

var my_object = {
    '0': 'zero',
    '1': 'one',
    '2': 'two',
    '3': 'three',
    '4': 'four',
    length: 5
};
Run Code Online (Sandbox Code Playgroud)

这显然不是一个数组,但是如果你可以将它设置为this.slice(),那么它就可以正常工作,因为它看起来就像一个数组,.slice()可以正常工作.

var sliced = Array.prototype.slice.call( my_object, 3 );
Run Code Online (Sandbox Code Playgroud)

示例: http ://jsfiddle.net/wSvkv/

正如您在控制台中看到的,结果是我们所期望的:

['three','four'];
Run Code Online (Sandbox Code Playgroud)

所以当你将一个arguments对象设置为this值时会发生这种情况.slice().因为它arguments有一个.length属性和一堆数字索引,所以.slice()就像它在一个真正的数组上工作一样.

  • 很棒的答案!但遗憾的是,你不能用这种方式转换任何对象,如果你的对象键是字符串值,就像实际的话.这将失败,所以保持你的对象内容为'0':'value'而不是像'stringName' :'值'. (7认同)
  • @vsync:这不是假设.如果您强制执行,您可以从任何对象获得订单.假设我有`var obj = {2:"two",0:"0",1:"one"}`.如果我们使用`for-in`来枚举对象,则无法保证顺序.但是如果我们使用`for`,我们可以手动强制执行命令:`for(var i = 0; i <3; i ++){console.log(obj [i]); }`.现在我们知道对象的属性将以我们`for`循环定义的升序数字顺序到达.这就是`.slice()`的作用.它不关心它是否有一个实际的数组.它只是从'0`开始并在升序循环中访问属性. (7认同)
  • @Michael:阅读开源JS实现的源代码是可能的,但仅仅参考"ECMAScript"语言规范更简单.[这里是一个链接](http://es5.github.io/x15.4.html#x15.4.4.10)到'Array.prototype.slice`方法描述. (6认同)
  • @vsync:这是不保证顺序的 `for-in` 语句。`.slice()` 使用的算法定义了一个数字顺序,从 0 开始,以给定对象(或数组或其他)的 .length 结尾(不包括在内)。所以保证所有实现的顺序是一致的。 (3认同)

Ben*_*ing 81

arguments对象实际上不是Array的实例,并且没有任何Array方法.因此,arguments.slice(...)不起作用,因为arguments对象没有slice方法.

数组确实有这种方法,因为arguments对象与数组非常相似,所以两者是兼容的.这意味着我们可以使用带有arguments对象的数组方法.由于数组方法是在构建数组时考虑的,因此它们将返回数组而不是其他参数对象.

为什么要使用Array.prototype?这Array是我们从(new Array())创建新数组的对象,这些新数组是传递方法和属性,如slice.这些方法存储在[Class].prototype对象中.因此,为了提高效率,我们不是通过(new Array()).slice.call()或访问切片方法[].slice.call(),而是直接从原型中获取.这样我们就不必初始化一个新数组了.

但为什么我们必须首先做到这一点?好吧,正如你所说,它将一个arguments对象转换为一个Array实例.然而,我们使用切片的原因更多的是"黑客"而不是任何东西.切片方法将采用,您猜对了,切片的数组并将该切片作为新数组返回.不传递任何参数(除了参数对象作为其上下文)会导致slice方法获取传递的"数组"的完整块(在本例中为arguments对象)并将其作为新数组返回.


Del*_*ani 42

通常,打电话

var b = a.slice();
Run Code Online (Sandbox Code Playgroud)

将数组复制ab.但是,我们做不到

var a = arguments.slice();
Run Code Online (Sandbox Code Playgroud)

因为arguments它不是一个真正的数组,并没有slice作为一种方法.Array.prototype.sliceslice数组的函数,并call使用thisset to 运行函数arguments.

  • IlyaD,`slice`是每个`Array`实例的方法,但不是`Array`构造函数.您使用`prototype`来访问构造函数的理论实例的方法. (3认同)
  • thanx但为什么要使用`prototype`?是不是`slice`本地`Array`方法? (2认同)
  • 注意`Array`是一个构造函数,相应的"class"是`Array.prototype`.你也可以使用`[] .slice` (2认同)

sam*_*sam 21

// We can apply `slice` from  `Array.prototype`:
Array.prototype.slice.call([]); //-> []

// Since `slice` is available on an array's prototype chain,
'slice' in []; //-> true
[].slice === Array.prototype.slice; //-> true

// … we can just invoke it directly:
[].slice(); //-> []

// `arguments` has no `slice` method
'slice' in arguments; //-> false

// … but we can apply it the same way:
Array.prototype.slice.call(arguments); //-> […]

// In fact, though `slice` belongs to `Array.prototype`,
// it can operate on any array-like object:
Array.prototype.slice.call({0: 1, length: 1}); //-> [1]
Run Code Online (Sandbox Code Playgroud)


Mar*_*Roy 18

首先,您应该阅读函数调用在JavaScript中的工作原理.我怀疑只有这一点足以回答你的问题.但这里是对正在发生的事情的总结:

Array.prototype.slice提取方法从的原型.但直接调用它是行不通的,因为它是一个方法(不是一个函数),因此需要一个上下文(一个调用对象),否则就会抛出.slice ArraythisUncaught TypeError: Array.prototype.slice called on null or undefined

call()方法允许您指定方法的上下文,基本上使这两个调用等效:

someObject.slice(1, 2);
slice.call(someObject, 1, 2);
Run Code Online (Sandbox Code Playgroud)

除了前者要求slice方法存在于someObject原型链中(就像它所做的那样Array),而后者允许将context(someObject)手动传递给方法.

此外,后者简称:

var slice = Array.prototype.slice;
slice.call(someObject, 1, 2);
Run Code Online (Sandbox Code Playgroud)

这与以下相同:

Array.prototype.slice.call(someObject, 1, 2);
Run Code Online (Sandbox Code Playgroud)


小智 12

Array.prototype.slice.call(arguments)是将参数转换为数组的传统方式.

在ECMAScript 2015中,您可以使用Array.from或spread运算符:

let args = Array.from(arguments);

let args = [...arguments];
Run Code Online (Sandbox Code Playgroud)


nav*_*een 9

这是因为,正如MDN所说

arguments对象不是数组.它类似于数组,但除了length之外没有任何数组属性.例如,它没有pop方法.但是它可以转换为真实数组:

在这里,我们调用slice本机对象Array而不是它的实现,这就是额外的原因.prototype

var args = Array.prototype.slice.call(arguments);
Run Code Online (Sandbox Code Playgroud)