起因
Array.prototype.push.call( arguments, 3 );
当你看到这句代码时有没有同我一样感到疑惑这句代码到底是什么意思?
PS:call和apply使用的场景有很多这里目前只是介绍其中的一部分
借用其他对象的方法
第一种场景是“借用构造函数”
利用mdn上的例子
function Product(name, price) {
this.name = name;
this.price = price;
}
function Food(name, price) {
Product.call(this, name, price);
this.category = 'food';
}
// 上面的函数等价于这个函数
// function Food(name, price) {
// this.name = name;
// this.price = price;
// this.category = 'food';
// }
console.log(new Food('cheese', 5).name);
Product的构造函数是:
function Product(name, price) {
this.name = name;
this.price = price;
}
Product.call(this, name, price);
是吧Product的构造函数放在了Food函数的中,有了类似“继承”的效果。
可以在浏览器中console中测试一下:
> let a = new Food('cheese', 5)
> a
< Food {name: "cheese", price: 5, category: "food"}
category: "food"
name: "cheese"
price: 5
__proto__:
constructor: ƒ Food(name, price)
__proto__: Object
a的__proto__并没有变化,但在Food函数中多了name和price两个属性,所以才说类似“继承”。
第二种借用方法
我们再说回上面的例子Array.prototype.push.call( arguments, 3 );
其实是借用了Array.prototype.push
的方法使arguments对象有了类似array的push功能,为什么可以这样使用呢?首先我们来看v8中array的push功能的代码:
function ArrayPush() {
var n = TO_UINT32( this.length ); // 被push的对象的length
var m = %_ArgumentsLength(); // push的参数个数
for (var i = 0; i < m; i++) {
this[ i + n ] = %_Arguments( i ); // 复制元素 (1)
}
this.length = n + m; // 修正length属性的值 (2)
return this.length;
};
假如let arguments = {}
(PS:IE中必须显示生命lengthlet arguments = { length: 0 }
)arguments是这个对象上面的函数也能正常运行,所以不管对象是否是array都可以使用ArrayPush函数但需要满足(1)和(2)处的条件否则会报错。
var a = {};
Array.prototype.push.call( a, 'first' );
alert ( a.length ); // 输出:1
alert ( a[ 0 ] ); // first
总结
原来没有搞清楚call和apply的点是:
- 没有搞懂“继承”的关系(现在知道是假继承)
-
Array.prototype.push.call( arguments, 3 );
为什么会这样执行,其实也就是call和apply的真正的含义。
其实call和apply还有一个功能是可以改变this的指向,想必大家在了解this的时候就已经知道这一点了,这里就不展开讲了。
华丽谢幕!