先来看call的MDN解释
call()
提供新的 this 值给当前调用的函数/方法。你可以使用call
来实现继承:写一个方法,然后让另外一个新的对象来继承它(而不是在新对象中再写一次这个方法)。
总是觉得比较难懂,我的理解是:写在函数原型链上的一个方法,能够改变调用这个方法的函数的this指向。
来个示例:
var value = 2
var foo = {
value: 1
};
function bar() {
console.log(this.value);
}
bar() //2
bar.call(foo) //1
定义了bar并且在windows环境下执行,this指向的是windows下的全局变量,如果我们使用call指向了foo对象,那么this就指向了foo
call改变了this的指向,并且执行了函数
我们要怎么进行模拟呢?我们知道对于this,谁调用了它,它就指向谁,而且会沿着作用域链一层一层往外找,那么我们把bar写在对象foo里面的话效果应该是相同的吧
var foo = {
value: 1,
bar: function() {
console.log(this.value);
}
};
foo.bar() //1
效果是一样的呢,那我们可不可以重写一个call2,它的效果是被调用之后就在调用的对象里面增加一个属性bar,然后再调用之后删除这个属性,是不是可以做到和call一样的效果呢?
Function.prototype.call2 = function(context) {
context.nihaoya = this; //nihaoya是一个要被删除的属性,取什么无所谓
console.log(this) // 方法中,谁调用方法,this就指向谁,这里指向bar
context.nihaoya();
delete context.fn;
console.log(context) //操作下来foo是没有变化的
}
// 测试一下
var foo = {
value: 1
};
function bar() {
console.log(this.value);
}
bar.call2(foo); // 1
我们这里的call只能传入一个对象参数,需要再改造一下,从第二个参数开始就是传入的其他参数了
Function.prototype.call2 = function(context) {
context.fn = this;
var args = Array.from(arguments).slice(1);
context.fn(...args)
delete context.fn;
}
// 测试一下
var foo = {
value: 1
};
function bar(name, age) {
console.log(name)
console.log(age)
console.log(this.value);
}
bar.call2(foo, 'kevin', 18);
//kevin
//18
//1
嗯,没啥问题,应该还要加一点对于传入函数类型的判断。嗨,算了,懒得加了,主体大概就是这样子。
apply的模拟实现
Function.prototype.apply2 = function(context) {
context.fn = this;
var args = arguments[1];
context.fn(...args)
delete context.fn;
}
// 测试一下
var foo = {
value: 1
};
function bar(name, age) {
console.log(name)
console.log(age)
console.log(this.value);
}
bar.apply2(foo, ['kevin', 18]);
//kevin
//18
//1
实现了call再来实现apply真是洒洒水啦