Answers:
在幕后发生的事情是,当.slice()
通常被调用时,它this
是一个Array,然后它仅在该Array上进行迭代并完成其工作。
如何this
在.slice()
功能的阵列?因为当您这样做时:
object.method();
...该object
自动成为价值this
的method()
。因此:
[1,2,3].slice()
...将[1,2,3]
Array设置为this
in 的值.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
};
显然这不是一个Array,但是如果您可以将其设置为的this
值.slice()
,那么它将正常工作,因为它看起来像Array一样.slice()
可以正常工作。
var sliced = Array.prototype.slice.call( my_object, 3 );
示例: http ://jsfiddle.net/wSvkv/
如您在控制台中看到的,结果就是我们所期望的:
['three','four'];
因此,当您将arguments
对象设置为的this
值时,就会发生这种情况.slice()
。因为arguments
具有一个.length
属性和一堆数字索引,所以.slice()
就像在真正的数组上工作一样进行工作。
Array.prototype.slice
方法的说明。
for-in
是不保证顺序的语句。所使用的算法.slice()
定义了以给定对象(或Array或其他对象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
并以递增循环访问属性。
该arguments
对象实际上不是Array的实例,并且不具有任何Array方法。所以,arguments.slice(...)
将不会起作用,因为arguments对象没有slice方法。
数组确实具有此方法,并且由于arguments
对象与数组非常相似,因此两者是兼容的。这意味着我们可以将数组方法与arguments对象一起使用。并且由于数组方法是在考虑数组的基础上构建的,因此它们将返回数组而不是其他参数对象。
那为什么要使用Array.prototype
呢?的Array
是,我们创建从(新阵列的对象new Array()
),而这些新的阵列被传递的方法和属性,像切片。这些方法存储在[Class].prototype
对象中。因此,出于效率的考虑,我们无需从原型中直接获取切片方法,而无需通过(new Array()).slice.call()
or 访问slice方法[].slice.call()
。这样一来,我们不必初始化新的数组。
但是,为什么我们必须首先这样做呢?好了,正如您所说,它将参数对象转换为Array实例。但是,我们使用slice的原因更多是“ hack”。您猜到了,slice方法将获取一个数组的切片,并将该切片作为新数组返回。不向其传递任何参数(以arguments对象作为上下文除外)会使slice方法获取传递的“数组”(在本例中为arguments对象)的完整块,并将其作为新数组返回。
通常,打电话
var b = a.slice();
将数组复制a
到中b
。但是,我们做不到
var a = arguments.slice();
因为arguments
它不是一个真正的数组,也没有slice
作为方法。Array.prototype.slice
是slice
数组的函数,并call
以this
设置运行函数arguments
。
prototype
?不是slice
本机Array
方法吗?
Array
是一个构造函数,而对应的“类”为Array.prototype
。您也可以使用[].slice
slice
是每个Array
实例的方法,但不是Array
构造函数。您用于prototype
访问构造函数的理论实例的方法。
首先,您应该阅读JavaScript中函数调用的工作方式。我怀疑仅此一项就足以回答您的问题。但是,这里是正在发生的事情的摘要:
Array.prototype.slice
提取方法从的原型。但是直接调用它是行不通的,因为它是一个方法(不是函数),因此需要一个上下文(一个调用对象,),否则它将抛出slice
Array
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]
Array.prototype.slice.call(arguments)是将参数转换为数组的老式方法。
在ECMAScript 2015中,可以使用Array.from或传播运算符:
let args = Array.from(arguments);
let args = [...arguments];
也许有点晚了,但是所有这些混乱的答案是,在JS中将call()用于继承。例如,如果将其与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/zh-CN/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);
});
}