一直以来见过好多的代码都应用到Array.prototype.push.call,一直模棱两可,不知甚解,最近再看JavaScript设计模式与开发实践,稍微对其有了一点深入的了解。
**引子:**一个对象未必只能使用它自身的方法,有什么办法能够是一个对象借用不属于它的方法呢?
答案就是使用 call 和apply都能达到这个需求:
var obj1 = {
name: 'dylan'
}
var obj2 = {
getName: function() {
return this.name
}
}
console.log(obj2.getName.call(obj1)) // 输出: dylan
// 浅析:obj2.getName.call(obj1) 把 obj2.getName 中的 this 指向了 obj1
同理,我们也常常让类数组对象去借用 Array.prototype 方法,这是 call 和 apply 最常见的应用场景之一:
(function() {
Array.prototype.push.call(arguments, 4); // arguments 借用 Array.prototype.push 方法
console.log(arguments); // 输出:[1,2,3,4]
})(1, 2, 3)
浅析1:Array.prototype上的方法原本只能用来操作array对象,但是用call和apply可以把任意对象当做this传入某个方法,这样一来,方法中用到this的地方就不再局限于原来规定的对象,而是加以泛华并得到更广的应用
浅析2:看看Array.prototype.push在V8引擎中内部实现:
functionArrayPush() {
var n = TO_UNIT32(this.length); //
var m = %_ArgumentsLength(); // 逐个复制元素
for(var i = 0; i < m; i++){ //
this[i + n ]= %_Arguments(i);
}
this.length = n + m; // 修改数组的 length
return this.length;
}
从以上的源码看出,push操作只是简单的复制元素,只要对象具备以下两个特性,就可以使用 Array.prototype.push()
- 1、可读写的length属性
- 2、对象本身可以读取属性
// 举个例子
var obj = {};
[].push.call(obj, 'a');
console.log(obj); // {0: 'a', length: 1}
由此可见,对象不仅多了键值对0: 'a',还多了键值对length: 1,从源码可以看出,push方法复制元素使用的是指针this, 所以可以很清楚的了解到 push 是浅复制
// 再看一个例子
var array=[];
var item={};
for(var i=0;i<5;i++){
item.a=i;
array.push(item);
}
console.log(item); // {a: 4}
console.log(array);// [{a: 4},{a: 4},{a: 4},{a: 4},{a: 4},{a: 4}]
浅析:因为push为浅复制,复制的只是引用地址,若是item发生变化,则复制的值也会发生变化