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

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

.slice()会发生什么,当.slice()被正常调用时, this是一个数组,然后它只是遍历该数组,并完成其工作。

.slice()函数中的数组是如何工作的? 因为当你这样做时:

 object.method(); 

object自动成为method() 。 所以:

 [1,2,3].slice() 

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


但是如果你可以用别的东西代替this值呢? 只要你替代的东西有一个数字的.length属性,和一堆属性是数字的索引,它应该工作。 这种types的对象通常被称为类似数组的对象

.apply() .call().apply()方法可以让你在一个函数中手动设置这个值。 因此,如果我们将.slice()的值设置为类似数组的对象 ,则.slice()假定它正在与一个数组一起工作,并将执行它的操作。

以这个普通对象为例。

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

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

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

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

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

 ['three','four']; 

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

arguments对象实际上不是一个数组的实例,也没有任何数组方法。 所以, arguments.slice(...)将不起作用,因为arguments对象没有slice方法。

数组确实有这个方法,而且由于arguments对象与数组非常相似,所以两者是兼容的。 这意味着我们可以使用参数对象的数组方法。 而且由于数组方法是用数组构build的,他们将返回数组而不是其他的参数对象。

那么为什么要使用Array.prototypeArray是我们从( new Array() )创build新数组的对象,并且这些新数组通过方法和属性(如slice)传递。 这些方法存储在[Class].prototype对象中。 所以,为了效率的缘故,我们不是通过(new Array()).slice.call()[].slice.call()来访问slice方法,而是直接从原型中获取它。 这是因为我们不需要初始化一个新的数组。

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

通常,打电话

 var b = a.slice(); 

将数组a复制到b 。 但是,我们做不到

 var a = arguments.slice(); 

因为arguments不是真正的数组,并且没有slice作为方法。 Array.prototype.slice是数组的slice函数, call运行函数并将this设置为arguments

 // 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] 

首先,你应该阅读函数调用如何在JavaScript中工作 。 我怀疑这一点足以回答你的问题。 但是,下面是发生了什么的总结:

Array.prototype.sliceArray的原型中提取slice 方法 。 但是直接调用将不起作用, 因为它是一个方法(不是函数) ,因此需要一个上下文(一个调用对象, this ),否则会抛出Uncaught 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); 

正如MDN所指出的那样

参数对象不是一个数组。 它与数组类似,但除长度外没有任何数组属性。 例如,它没有pop方法。 但是它可以转换成一个真正的数组:

在这里,我们正在调用本地对象Array slice ,而不是在其实施 ,这就是为什么额外的。 .prototype

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

不要忘记,这种行为的底层基础是整合到JS引擎中的types转换。

切片只需要对象(感谢现有的arguments.length属性),并在完成所有操作之后返回数组对象。

如果您尝试使用INT值处理String-method,则可以testing相同的逻辑:

 String.prototype.bold.call(11); // returns "<b>11</b>" 

这解释了上面的说法。

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

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

 let args = Array.from(arguments); let args = [...arguments]; 

它使用slice方法数组,并用它作为arguments对象调用它。 这意味着它会像调用arguments.slice()那样调用它,假设arguments具有这种方法。

创build没有任何参数的切片将简单地采用所有元素 – 因此它只是将arguments的元素复制到数组中。

假设你有: function.apply(thisArg, argArray )

apply方法调用一个函数,传递将绑定到这个对象和一个可选的参数数组。

slice()方法select一个数组的一部分,并返回新的数组。

所以当你调用Array.prototype.slice.apply(arguments, [0]) ,数组切片方法被调用(bind)参数。

也许有点晚,但所有这些混乱的答案是,在JS中使用call()inheritance。 例如,如果我们将它与Python或PHP进行比较,则分别使用call作为super()。 init ()或parent :: _ construct()。

这是一个用法的例子,阐明了一切:

 function Teacher(first, last, age, gender, interests, subject) { Person.call(this, first, last, age, gender, interests); this.subject = subject; } 

参考: https : //developer.mozilla.org/en-US/docs/Learn/JavaScript/Objects/Inheritance

当.slice()被正常调用时,这是一个数组,然后它只是迭代该数组,并完成其工作。

  //ARGUMENTS function func(){ console.log(arguments);//[1, 2, 3, 4] //var arrArguments = arguments.slice();//Uncaught TypeError: undefined is not a function var arrArguments = [].slice.call(arguments);//cp array with explicity THIS arrArguments.push('new'); console.log(arrArguments) } func(1,2,3,4)//[1, 2, 3, 4, "new"] 

我只是写这个来提醒自己…

  Array.prototype.slice.call(arguments); == Array.prototype.slice(arguments[1], arguments[2], arguments[3], ...) == [ arguments[1], arguments[2], arguments[3], ... ] 

或者只是使用这个方便的函数$ A把大部分东西转换成一个数组。

 function hasArrayNature(a) { return !!a && (typeof a == "object" || typeof a == "function") && "length" in a && !("setInterval" in a) && (Object.prototype.toString.call(a) === "[object Array]" || "callee" in a || "item" in a); } function $A(b) { if (!hasArrayNature(b)) return [ b ]; if (b.item) { var a = b.length, c = new Array(a); while (a--) c[a] = b[a]; return c; } return Array.prototype.slice.call(b); } 

示例用法…

 function test() { $A( arguments ).forEach( function(arg) { console.log("Argument: " + arg); }); }