关于绑定首先要说下this的指向问题。
我们都知道:
函数调用时this指向window
对象调用函数时this指向对象本身
看下面得例子:
// 1 function test(){ const name = ‘test1‘; console.log(this.name) } constname = ‘test2‘ test() // test2 // 当前函数调用指向了window,所以打印的为test2 // 2 var obj = { name:‘test1‘, get(){ console.log(this.name) } } var name = ‘test2‘; obj.get() // test1 // 当前通过对象调用。this指向obj,打印test1 // 3 var obj = { name:‘test1‘, get(){ console.log(this.name) } } var name = ‘test2‘; var fn = obj.get; fn() // test2 // 将obj.get作为值赋值给fn,执行fn,这时候相当于通过函数执行,this重新指向
那如何将this指向到我们指定的对象中呢?
js提供改变this指向的三种方法:call、apply、bind
其中call和apply执行改变this的同时会执行函数,bind是会返回一个函数。
而call与apply的区别在于参数,call可以传多个参数,而apply传一个数组作为参数。下面来看看实现:
模拟实现call
Function.prototype.myCall = function(thisArg, ...argumentArr){ if(typeof this !== ‘function‘) { throw new TypeError(`${this} is not function`) }; if(thisArg === undefined || thisArg === null){ thisArg = window; }
//生成一个对象 var obj = Object.create(thisArg) obj[‘fn‘] = this;
// 通过对象执行函数将this指向该对象 var result = obj[‘fn‘](...argumentArr) delete obj[‘fn‘]; return result } var obj = { name:‘test1‘, get(data1, data2){ console.log(this.name, data1, data2) } } obj.get.myCall({name: ‘test2‘}, 1, 2, 3) // test2,1,2 // 原理就是通过传入的新的对象来执行这个函数,就是通过对象调用函数的方式
模拟实现apply
Function.prototype.myApply = function(thisArg, argumentArr){ if(typeof this !== ‘function‘) { throw new TypeError(`${this} is not function`) }; if(thisArg === undefined || thisArg === null){ thisArg = window; } if(argumentArr === undefined || argumentArr === null){ argumentArr = [] } var obj = Object.create(thisArg) obj[‘fn‘] = this; var result = obj[‘fn‘](...argumentArr) delete obj[‘fn‘]; return result } var obj = { name:‘test1‘, get(data1, data2){ console.log(this.name, data1, data2) } } obj.get.myApply({name: ‘test2‘}, [1, 2, 3]) // test2,1,2 // 我们发现与call唯一的不同就是入参,call使用rest 参数,将多余的参数整合成argument形式,而apply入参直接是数组
模拟实现bind
Function.prototype.myBind = function(thisArg, ...argumentArr){ if(typeof this !== ‘function‘) { throw new TypeError(`${this} is not function`) }; if(thisArg === undefined || thisArg === null){ thisArg = window; } var self = this var bindfn = function(){ return self.call(thisArg, ...argumentArr) } bindfn.prototype = self.prototype return bindfn } var obj = { name:‘test1‘, get(data1, data2){ console.log(this.name, data1, data2) } } const fn = obj.get.myBind({name: ‘test2‘}, 1, 2, 3) fn() // test2,1,2 // 相比于前两个bind会有不一样,bind使用到了闭包, // 我们之前知道函数执行this是指向window,但是这里我们执行却指向了我们的目标对象,实现这样的方式就是闭包
如果我们没有赋值操作执行var self = this,而是直接使用this执行的话
this.call(thisArg, ...argumentArr)
这个时候this是指向window的,这个时候会报this.call is not a function。当我们进行赋值给self ,那么这个时候self 就形成了返回的函数fn的专属背包而不被销毁,每当我们执行fn的时候取到的this都是obj.get方法