持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第14天,点击查看活动详情
前言
如果我们对JavaScript中的this有了解,会不会有一些疑问,我们能不能改变this的指向,而不是由调用处的上层作用域决定。那么其实是有这样的方法的,就是js内部的call、bind、apply方法。
call方法
这个方法是函数对象Function上提供的一个方法,参数列表如下
call(this: Function, thisArg: any, ...argArray: any[]): any;
我们先来使用一下
function foo(){
console.log(this)
}
console.log('-------------------------');
const rookie = {
name:'the rookie',
pay:function(){
console.log('pay: ', this);
}
}
foo.call(rookie)
可以看到foo函数里this的指向已经变了,this指向的是传入的第一个参数。接着我们可以给方法加上一些参数
function foo(name,age){
console.log(this,name,age)
}
console.log('-------------------------');
const rookie = {
name:'the rookie',
pay:function(){
console.log('pay: ', this);
}
}
foo.call(rookie,'shy',23)
那么也同样完成改变了this的效果,同时参数也可以传递过去,即相当于通过rookie对象调用了foo方法。
call方法源码实现
//源码实现
Function.prototype.myCall=function(target){
let context = target || window;
context.fn = this;//为对象添加方法(this指向调用myCall的函数)
let args =[...arguments].slice(1);// 剩余的参数
console.log('args: ', args);
const result = context.fn(...args); //调用该方法,传递参数
delete context.fn; //调用该方法,该方法this指向context
return result;
}
这里我们使用foo.myCall(rookie,'shy',23)和前面达到同样的效果。
apply方法
这个方法和call方法类似,只是第二个参数变成了要传一个数组。
function foo(name,age){
console.log(this,name,age)
}
console.log('-------------------------');
const rookie = {
name:'the rookie',
pay:function(){
console.log('pay: ', this);
}
}
foo.apply(rookie,['apply',23])
接下来看看其源码实现
Function.prototype.myApply=function(){
//找出第一个参数,判断第一个参数是否为空,如果为空this还是指向window
let target = arguments[0] || window
//获取传递的参数
var _arg = arguments[1]
target.fn = this; //为该对象添加方法,this指向调用myApply对象
target.fn(..._arg);//调用方法
//删除fn属性
delete target.fn;
}
bind方法
这个方法稍微跟前面2个有点不太一样,bind()函数会创建一个新的绑定函数,这个绑定函数包装了原函数的对象。调用绑定函数通常会执行包装函数。
const bindNew=foo.bind(rookie)
bindNew('shy',23)
apply方法源码实现
Function.prototype.myBind = function(){
const target = arguments[0] || window
const fn = this // 调用bind的函数
const args = [...arguments].slice(1) // myBind的参数
const bind = function(){
const args1 = [...arguments].slice() // bind的参数
return fn.apply(target,args.concat(args1))
}
return bind
}
从源码也可以很好的理解,bind其实返回了一个新的函数,同样的执行
const bindNew=foo.myBind(rookie)
bindNew('bind方法实现w',23)
也是同样的效果,如果我们不传要绑定到的对象,则默认是window。
总结
从源码角度去理解这几个方法的使用,感觉更能深入其中,同样的让我们手写一个这样的方法改变this的指向,我们也可以按源码的思路去完成了。