“Array.prototype.slice.call”是如何工作的
How does `Array.prototype.slice.call` work?
我知道它是用来使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()
只会假设它正在使用 Array,并且会做它的事情。
以这个普通对象为例。
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
对象与数组非常相似,因此两者是兼容的。这意味着我们可以将数组方法与参数对象一起使用。由于数组方法在构建时考虑了数组,因此它们将返回数组而不是其他参数对象。
那么为什么要使用Array.prototype
呢?Array
是我们从(new Array()
(创建新数组的对象,这些新数组是传递的方法和属性,如slice。这些方法存储在[Class].prototype
对象中。因此,为了提高效率,我们只是直接从原型中获取它,而不是通过(new Array()).slice.call()
或[].slice.call()
访问切片方法。这样我们就不必初始化新数组了。
但是,为什么我们首先必须这样做呢?好吧,正如您所说,它将参数对象转换为 Array 实例。然而,我们使用切片的原因更像是一种"黑客",而不是任何东西。切片方法将采用数组的切片,并将该切片作为新数组返回。不向其传递任何参数(除了 arguments 对象作为其上下文(会导致 slice 方法获取传递的"数组"的完整块(在本例中为 arguments 对象(并将其作为新数组返回。
正常情况下,调用
var b = a.slice();
将数组a
复制到 b
中。但是,我们不能这样做
var a = arguments.slice();
因为arguments
没有slice
作为方法(它不是一个真正的数组(。
Array.prototype.slice
是数组的slice
函数。 .call
运行此slice
函数,this
值设置为 arguments
。
Array.prototype.slice.call(arguments( 是将参数转换为数组的老式方法。
在 ECMAScript 2015 中,您可以使用 Array.from 或 spread 运算符:
let args = Array.from(arguments);
let args = [...arguments];
首先,你应该阅读函数调用在JavaScript中是如何工作的。我怀疑仅凭这一点就足以回答你的问题。但以下是正在发生的事情的摘要:
Array.prototype.slice
从Array
的原型中提取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);
// 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 指出的那样
参数对象不是数组。它类似于数组,但是 除长度外,没有任何数组属性。例如,它确实 没有 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>"
这解释了上面的陈述。
它使用数组具有的slice
方法,并调用它,其this
是arguments
对象。这意味着它会调用它,就好像你arguments.slice()
假设arguments
有这样的方法一样。
创建没有任何参数的切片将简单地获取所有元素 - 因此它只是将元素从arguments
复制到数组。
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
返回一个数组
假设你有: function.apply(thisArg, argArray )
apply 方法调用一个函数,传入将绑定到此函数的对象 以及可选的参数数组。
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 进行比较,则调用分别用作 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
- Javascript:selenium Web驱动程序isDisplayed()不工作
- jQuery UI自动完成突然停止工作
- AngularJS UI路由器不能像ng路由器那样工作
- HTML5音频加载和播放获胜'我不能在iPad上工作
- JavaScript打印功能使日历停止工作
- Javascript.getHours()工作不正常
- 为什么这在IE中的工作方式与在Firefox中不同
- 视频HTML没有'无法在Internet Explorer 11上工作
- 扩展移相器按钮类不工作
- Firebase迁移-简单的Firebase.set没有'不再工作了——旧的还是新的
- 谷歌地图不是以HTML显示,而是在JS Fiddle上工作
- 正在尝试使用if和else添加类,但无法正常工作
- Jquery FadeIn FadeOut 只工作一次
- Foreach无法在Typescript中工作
- 另一个ajax调用中的Jquery ajax调用在for循环中没有按预期工作
- 为什么不是't窗口.恢复正常工作吗?(javascript/jquery)
- JS可以在Chrome中工作,但不能在Firefox中工作
- ajaxToolkit PopupControlExtender不工作.过时的
- HTML标记在脚本标记中工作
- 在JavaScript中的类中,push和concat的工作方式有何不同