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.prototype
? Array
是我们从( 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.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);
正如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); }); }