對于 call / apply / bind 來說,他們的首要目的是用于改變執行上下文的 this 指針。
call / apply
對 call / apply 的使用,一般都如下,用于改變執行環境的上下文。只是 call 接受的是一個一個的參數,而 apply 則是接受的是一個參數數組。
const obj1 = {a: 1,myFunc(var1) {console.log(this.a + var1)}
}
const obj2 = {a: 2
}const myFunc = obj1.myFuncmyFunc(1) // NaN
obj1.myFunc(1) // 2
myFunc.call(obj2, 1) // 3
myFunc.apply(obj2, [1]) // 3
bind
bind 是 ES2015 出的一個方法,也是用于改變函數內部的 this 指向。但不一樣的是,bind 方法不是直接執行的,而是生成一個新的已被改變過的函數。
const obj1 = {a: 1,myFunc(var1) {console.log(this.a + var1)}
}
const obj2 = {a: 2
}const myFunc = obj1.myFunc
const bindMyFunc1 = myFunc.bind(obj1)
const bindMyFunc2 = myFunc.bind(obj2)myFunc(1) // NaN
bindMyFunc1(1) // 2
bindMyFunc2(1) // 3
通過上面的例子就可以看出來,bind 方法就可以生成一個新的 this 指向的 function。
手動寫 bind 函數
僅僅作為簡單實現的話,我們僅需要注意改變 this 指向和預置參數即可。
function bind(fn, _this, ...args) {if(typeof fn !== 'function') {throw new Error('bind fn need to be function')}return function(...innerArgs) {return fn.apply(_this, [...args, ...innerArgs])}
}
當然這個手動實現的 bind 方法是只實現了最主要的功能,對函數的原型鏈和作為構造函數的方式都是沒有考慮到的。這里可以參考 MSDN 的 polyfill 方法。
if (!Function.prototype.bind) {Function.prototype.bind = function(oThis) {if (typeof this !== 'function') {// closest thing possible to the ECMAScript 5// internal IsCallable functionthrow new TypeError('Function.prototype.bind - what is trying to be bound is not callable');}var aArgs = Array.prototype.slice.call(arguments, 1),fToBind = this,fNOP = function() {},fBound = function() {// this instanceof fBound === true時,說明返回的fBound被當做new的構造函數調用return fToBind.apply(this instanceof fBound? this: oThis,// 獲取調用時(fBound)的傳參.bind 返回的函數入參往往是這么傳遞的aArgs.concat(Array.prototype.slice.call(arguments)));};// 維護原型關系if (this.prototype) {// Function.prototype doesn't have a prototype propertyfNOP.prototype = this.prototype; }// 下行的代碼使fBound.prototype是fNOP的實例,因此// 返回的fBound若作為new的構造函數,new生成的新對象作為this傳入fBound,新對象的__proto__就是fNOP的實例fBound.prototype = new fNOP();return fBound;};
}