`Array.prototype.slice.call` 是如何工作的?

IT技术 javascript arrays
2021-02-07 23:07:42

我知道它用于制作arguments真实的Array,但我不明白使用Array.prototype.slice.call(arguments);.

6个回答

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

如何this.slice()功能的阵列?因为当你这样做时:

object.method();

...该object自动成为valuethismethod()所以用:

[1,2,3].slice()

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


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

.call().apply()方法让你手动设置的值this的函数。因此,如果我们的值设置this.slice()一个阵列状物体.slice()只会认为它的工作与Array,并会做它的东西。

以这个普通对象为例。

var my_object = {
    '0': 'zero',
    '1': 'one',
    '2': 'two',
    '3': 'three',
    '4': 'four',
    length: 5
};

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

var sliced = Array.prototype.slice.call( my_object, 3 );

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

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

['three','four'];

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

很好的答案!但遗憾的是,如果您的对象键是字符串值,就像在实际单词中一样,那么您不能以这种方式转换任何对象。这将失败,因此将您的对象内容保留为 '0':'value' 而不是像 'stringName' :'value'。
2021-03-16 23:07:42
@vsync:这不是假设。如果您强制执行它,您可以从任何对象获得命令。假设我有var obj = {2:"two", 0:"zero", 1: "one"}如果我们使用for-in枚举对象,则无法保证顺序。但是如果我们使用for,我们可以手动强制执行顺序:for (var i = 0; i < 3; i++) { console.log(obj[i]); }现在我们知道对象的属性将按照我们for循环定义的数字升序到达这就是.slice()它的作用。它并不关心它是否有一个实际的数组。它只是开始0并以升序循环访问属性。
2021-03-21 23:07:42
由于对象键没有顺序,这个特定的演示在其他浏览器中可能会失败。并非所有供应商都按创建顺序对其对象的键进行排序。
2021-03-27 23:07:42
@Michael:阅读开源 JS 实现的源代码是可能的,但参考“ECMAScript”语言规范更简单。这里有一个链接Array.prototype.slice方法的说明。
2021-03-30 23:07:42
@vsync:这for-in是不保证顺序声明。使用的算法.slice()定义了以给定对象(或数组或其他)的开始0和结束(不包括)的数字顺序.length所以保证所有实现的顺序是一致的。
2021-04-02 23:07:42

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

数组确实有这个方法,而且由于arguments对象数组非常相似,所以两者是兼容的。这意味着我们可以将数组方法与参数对象一起使用。并且由于数组方法是在考虑数组的情况下构建的,因此它们将返回数组而不是其他参数对象。

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

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

由于此处描述的原因,您可能希望使用切片:jspatterns.com/arguments-thinked-harmful
2021-03-18 23:07:42

通常,调用

var b = a.slice();

将数组复制ab. 然而,我们做不到

var a = arguments.slice();

因为arguments没有slice作为方法(它不是真正的数组)。

Array.prototype.sliceslice数组函数。.call运行此slice函数,并将this值设置为arguments

thanx 但为什么要使用prototype不是slice本地Array方法吗?
2021-03-21 23:07:42
注意Array是构造函数,对应的“类”是Array.prototype. 你也可以使用[].slice
2021-03-25 23:07:42
call() 运行什么函数?
2021-03-25 23:07:42
@DavidSpector 我已经编辑以澄清这一点。
2021-03-30 23:07:42
IlyaD,slice是每个Array实例的一个方法,而不是Array构造函数。您用于prototype访问构造函数的理论实例的方法。
2021-04-06 23:07:42

首先,您应该阅读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);

除了前者要求slice方法存在于someObject的原型链中(就像它一样Array),而后者允许将上下文 ( someObject) 手动传递给方法。

此外,后者是以下简称:

var slice = Array.prototype.slice;
slice.call(someObject, 1, 2);

这与以下内容相同:

Array.prototype.slice.call(someObject, 1, 2);
为什么要使用 .call() 而不是 bind()?它们不同吗?
2021-03-20 23:07:42
2021-03-28 23:07:42

Array.prototype.slice.call(arguments) 是将参数转换为数组的老式方法。

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

let args = Array.from(arguments);

let args = [...arguments];
我知道了。Call 的意思是调用函数 slice(),它对数组的作用就像 substr 对字符串的作用一样。是的?
2021-03-16 23:07:42
你的意思是 Array.slice.call(1,2) 返回值 [1,2]?为什么叫“call”而不是“argsToArray”?“调用”不是指调用函数吗?
2021-04-09 23:07:42