一、call、apply、bind的作用和区别
JavaScript中call、apply、bind函数的作用都一样,都是改变函数中的this指向。它们之间只有一些细小的差别:
- call,第一个参数为执行上下文,参数通过后续参数传递,会自动调用函数
- apply,第一个参数为执行上下文,参数需要写成一个数组,然后通过第二个参数传递,会自动调用函数
- bind,与call几乎一模一样,只是不会自动调用函数,需要手动调用。
二、实现思路
两个函数A、B。当A调用call、apply、bind函数时需要传入函数B的执行上下文B和参数。
想要将B的this指向A只需要
- 在B的执行上下文this中创建一个函数,函数保存着A中的执行上下文this。
- 这样只要通过B的执行上下文调用这个函数,就达到了改变this的效果,B可以读取A中的变量
- 最后再删除这个函数
三、代码实现
上面看不懂没关系,直接上代码 + 注释
手写call
Function.prototype.myCall = function(context,...args){
// 如果调用者不是函数,直接返回
if(typeof this !== "function"){
return "Type Error"
}
// context为传入的执行上下文
// 判断是否有传入context上下文,如果有就取传入的上下文,否则就指向window
//例如:A.myCall(this),这里this为B函数的this。
//此时context就是B的this
context = context || window;
// 在传入的执行上下文内创建调用call的函数,这样就改变了this指向
//此时context是B的this,this是A的this。在B的执行上下文中创建了一个函数fn,保存A的this
context.fn = this
// 判断是否有传入参数,如果有参数,就放入函数执行一遍,否则就直接执行一次
//此时通过B的this调用这个函数,由于this会指向调用者,fn的this就会指向B
//由于fn中的内容和A中的内容是一样的,相当于B的this指向了A
context.fn(...args)
// 执行完后,将创建的函数删除
delete context.fn;
}
手写apply
Function.prototype.myApply = function(context,arr = []){
// 如果调用者不是函数,直接返回
if(typeof this !== "function"){
return "Type Error"
}
context = context || window;
context.fn = this;
context.fn(...arr)
delete context.fn
}
手写bind
Function.prototype.myBind = function(context,...args){
// 如果调用者不是函数,直接返回
if(typeof this !== "function"){
return "Type Error"
}
// context为传入的执行上下文
// 判断是否有传入context上下文,如果有就取传入的上下文,否则保持当前上下文
context = context || window;
// 由于bind函数不会立即调用函数,所以直接返回一个回调函数,当调用的时候才改变this指向
return(args)=>{this.call(context,...args)}
}
四、测试
function A(age, like) {
this.name = "喵喵";
this.age = age;
this.like = like;
}
function B() {
A.call(this, 5, "鱼");
A.myCall(this, 5, "鱼");
console.log("call", this.name, this.age, this.like);
console.log("myCall", this.name, this.age, this.like);
A.apply(this, [5, "鱼"]);
A.myApply(this, [5, "鱼"]);
console.log("apply", this.name, this.age, this.like);
console.log("myApply", this.name, this.age, this.like);
A.bind(this, 5, "鱼")();
A.myBind(this, 5, "鱼")();
console.log("bind", this.name, this.age, this.like);
console.log("myBind", this.name, this.age, this.like);
}
B();