call、apply、bind
三者异同
相同点
都可以改变this指向
不同点
call(this指向,args1,args2,...) : 在call中传入函数参数,参数以参数列表的形式传入,函数调用call方法改变this指向后会直接执行;
apply(this指向,[args1,args2...]) : 在apply中传入函数参数,参数以数组的形式传入,函数调用apply方法改变this指向后会直接执行;
bind(this指向,args1,args2...) : 在bind中传入函数参数,参数以参数列表的形式传入,函数调用bind方法后不会直接执行,而是会返回一个永久改变this指向的函数(记为newFun),后续可调用newFun,并在此函数中传入参数(bind的这种参数传入方式,是函数柯里化的一个应用)。
拓展
在计算机科学中,柯里化(Currying)是把 接受多个参数的函数变换成 接受一个单一参数(最初函数的第一个参数)的函数,并且 返回接受余下的参数且返回结果的新函数的技术。
手写call、apply、bind
call
function myCall(_this){
// this指向myCall函数的调用者
//_this为改变后的this指向
// 如果未传_this,则默认为window
_this=_this||window
//获取传入参数,并去除第一项,即为要传入调用函数的参数
let args=[...arguments].filter((value,index)=>index!==0)
_this.fun=this
let res=_this.fun(...args)
delete _this.fun
return res
}
//绑定到函数原型上
Function.prototype.myCall=myCall
apply
function myApply(_this){
_this=_this||window
let args=[...arguments][1]
_this.fun=this
let res=_this.fun(...args)
delete _this.fun
return res
}
Function.prototype.myApply=myApply
bind
function myBind(_this){
_this=_this||window
//this指向myBind函数的调用者
let that=this
let args=[...arguments].filter((value,index)=>index!==0)
let newFun=function(){
//this指向newFun函数的调用者
args.push(...arguments)
_this.fun=that
let res=_this.fun(...args)
delete _this.fun
return res
}
return newFun
}
Function.prototype.myBind=myBind
测试
function test(name,age) {
console.log(name,age,this.name1,this.age)
}
let obj={
name1:'zs',
age:18
}
//myCall的调用者为test,所以在myCall中,this指向test
test.myCall(obj,'hh',30) //hh 30 zs 18
test.myApply(obj,'ls',19) //ls 19 zs 18
let bindres=test.myBind(obj,'bind')
bindres(20) //bind 20 zs 18