我知道它用于使 arguments
成为真正的 Array
,但我不明白使用 Array.prototype.slice.call(arguments);
时会发生什么。
幕后发生的事情是,当 .slice()
正常调用时,this
是一个数组,然后它只是迭代该数组并完成它的工作。
.slice()
函数中的 this
如何是一个数组?因为当你这样做时:
object.method();
... object
自动成为 method()
中 this
的值。所以:
[1,2,3].slice()
... [1,2,3]
数组设置为 .slice()
中 this
的值。
但是,如果您可以将其他东西替换为 this
值呢?只要您替换的任何内容都具有数字 .length
属性和一堆数字索引的属性,它就应该可以工作。这种类型的对象通常称为类数组对象。
.call()
和 .apply()
方法让您手动在函数中设置 this
的值。因此,如果我们将 .slice()
中的 this
的值设置为 类数组对象,.slice()
只会假设它正在使用一个数组,并且会这样做它的东西。
以这个普通的对象为例。
var my_object = {
'0': 'zero',
'1': 'one',
'2': 'two',
'3': 'three',
'4': 'four',
length: 5
};
这显然不是一个数组,但是如果您可以将它设置为 .slice()
的 this
值,那么它就可以正常工作,因为它看起来很像一个数组,可以让 .slice()
正常工作。
var sliced = Array.prototype.slice.call( my_object, 3 );
示例: http://jsfiddle.net/wSvkv/
正如您在控制台中看到的,结果是我们所期望的:
['three','four'];
因此,当您将 arguments
对象设置为 .slice()
的 this
值时,就会发生这种情况。因为 arguments
有一个 .length
属性和一组数字索引,所以 .slice()
就像在处理一个真正的数组一样工作。
arguments
对象实际上不是 Array 的实例,并且没有任何 Array 方法。因此,arguments.slice(...)
将不起作用,因为 arguments 对象没有 slice 方法。
数组确实有这种方法,并且因为 arguments
对象与数组非常相似,所以两者是兼容的。这意味着我们可以将数组方法与 arguments 对象一起使用。而且由于数组方法是在考虑数组的情况下构建的,因此它们将返回数组而不是其他参数对象。
那么为什么要使用 Array.prototype
? Array
是我们从 (new Array()
) 创建新数组的对象,这些新数组传递方法和属性,如 slice。这些方法存储在 [Class].prototype
对象中。所以,为了效率,我们直接从原型中获取,而不是通过 (new Array()).slice.call()
或 [].slice.call()
访问 slice 方法。这样我们就不必初始化一个新数组。
但是为什么我们必须首先这样做呢?好吧,正如您所说,它将参数对象转换为 Array 实例。然而,我们使用 slice 的原因更多的是“hack”。 slice 方法将获取一个数组的切片,并将该切片作为新数组返回。不向它传递任何参数(除了作为其上下文的 arguments 对象)会导致 slice 方法获取传递的“数组”的完整块(在本例中为 arguments 对象)并将其作为新数组返回。
通常,调用
var b = a.slice();
将数组 a
复制到 b
。然而,我们做不到
var a = arguments.slice();
因为 arguments
没有 slice
作为方法(它不是真正的数组)。
Array.prototype.slice
是数组的 slice
函数。 .call
运行此 slice
函数,并将 this
值设置为 arguments
。
prototype
? slice
不是本机 Array
方法吗?
Array
是一个构造函数,对应的“类”是 Array.prototype
。您也可以使用 [].slice
slice
是每个 Array
实例的方法,但不是 Array
构造函数。您使用 prototype
访问构造函数的理论实例的方法。
Array.prototype.slice.call(arguments) 是将参数转换为数组的老式方法。
在 ECMAScript 2015 中,您可以使用 Array.from 或扩展运算符:
let args = Array.from(arguments);
let args = [...arguments];
首先,您应该阅读how function invocation works in JavaScript。我怀疑仅此一项就足以回答您的问题。但这里是正在发生的事情的摘要:
Array.prototype.slice
从 Array
的 prototype 中提取 slice
method。但是直接调用它是行不通的,as it's a method (not a function),因此需要一个上下文(调用对象,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);
// 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]
这是因为,作为 MDN notes
arguments 对象不是数组。它类似于数组,但除了长度之外没有任何数组属性。例如,它没有 pop 方法。但是它可以转换为一个真正的数组:
在这里,我们在原生对象 Array
上调用 slice
,而不是在其实现上,这就是为什么额外的 .prototype
var args = Array.prototype.slice.call(arguments);
不要忘记,这种行为的底层基础是完全集成在 JS 引擎中的类型转换。
Slice 只接受对象(感谢现有的 arguments.length 属性)并返回在对其进行所有操作后转换的数组对象。
如果您尝试使用 INT 值处理字符串方法,则可以测试相同的逻辑:
String.prototype.bold.call(11); // returns "<b>11</b>"
这解释了上面的陈述。
Array.prototype.slice=function(start,end){
let res=[];
start=start||0;
end=end||this.length
for(let i=start;i<end;i++){
res.push(this[i])
}
return res;
}
当你这样做时:
Array.prototype.slice.call(arguments)
arguments
成为 slice
中 this
的值,然后 slice
返回一个数组
它使用数组拥有的 slice
方法,并以 this
作为 arguments
对象来调用它。这意味着假设 arguments
有这样的方法,它就好像你做了 arguments.slice()
一样调用它。
创建一个不带任何参数的切片将简单地获取所有元素 - 所以它只是将元素从 arguments
复制到一个数组。
假设您有:function.apply(thisArg, argArray )
apply 方法调用一个函数,传入将绑定到 this 的对象和一个可选的参数数组。
slice() 方法选择数组的一部分,并返回新数组。
因此,当您调用 Array.prototype.slice.apply(arguments, [0])
时,会在参数上调用(绑定)数组切片方法。
当 .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"]
也许有点晚了,但所有这些混乱的答案是 call() 在 JS 中用于继承。例如,如果我们将其与 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
/*
arguments: get all args data include Length .
slice : clone Array
call: Convert Object which include Length to Array
Array.prototype.slice.call(arguments):
1. Convert arguments to Array
2. Clone Array arguments
*/
//normal
function abc1(a,b,c){
console.log(a);
}
//argument
function: function abc2(){
console.log(Array.prototype.slice.call(arguments,0,1))
}
abc1('a','b','c');
//a
abc2('a','b','c');
//a
不定期副业成功案例分享
Array.prototype.slice
方法描述。for-in
语句。.slice()
使用的算法定义了一个从0
开始并以给定对象(或数组或其他)的.length
结束(不包括)的数字顺序。因此,保证顺序在所有实现中都是一致的。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
开始并以升序循环访问属性。