本文共 1100 字,讀完只需 4 分鐘javascript
前一篇文章咱們嘗試模擬實現了 call 和 apply 方法,其實 bind 函數也能夠用來改變 this 的指向。bind 和 call和 apply 二者的區別在於,bind 會返回一個被改變了 this 指向的函數。java
本文介紹如何模擬實現 bind 函數:數組
首先觀察 bind 函數有什麼特色:閉包
var person = {
name: 'jayChou'
}
function say(age, sex) {
console.log(this.name, age, sex);
}
var foo = say.bind(person, '男', 39);
foo(); // jayChou 男 39
複製代碼
既然 bind 內部也要用改變 this 指向,咱們能夠用現成的 call 函數來實現這一功能。app
Function.prototype.newBind = function(context) {
if(typeof this !== 'function') {
throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
}
var self = this;
return function () {
return self.call(context)
}
}
複製代碼
說明一下:
if 判斷是爲了校驗,只能讓函數來調用此方法,並拋出錯誤。 第二個 return
是爲了讓返回的函數有返回值的功能。函數
測試一下:post
var person = {
name: 'jayChou'
};
var say = function() {
console.log(this.name);
}
var foo = say.newBind(person);
foo(); // jayChou
複製代碼
成功啦。測試
剛纔的函數是沒有傳遞參數,固然不行,因此咱們想辦法把函數的參數也傳遞進去。ui
bind 函數有個特色,就是在綁定的時候能夠傳參,返回的函數還能夠繼續傳參。this
var person = {
name: 'jayChou'
};
var say = function(p1, p2) {
console.log(this.name, p1, p2);
}
var foo = say.bind(person, 18);
foo(20); // jayChou 18 20
複製代碼
還能夠寫成:
say.bind(person, 18)(20); // jayChou 18 20
複製代碼
好,進入正題,考慮傳參的事。在前面的文章,咱們講過 arguments 類數組對象的一些特性,不能直接調用數組的方法,可是能夠用原型方法間接來調用,咱們採用 apply 的方式來傳遞參數。
Function.prototype.newBind = function(context) {
if(typeof this !== 'function') {
throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
}
var self = this;
var args = Array.prototype.slice.call(arguments, 1); // 間接調用數組方法,獲取第一次傳的參數
return function () {
var innerArgs = Array.prototype.slice.call(arguments);
return self.apply(context, args.concat(innerArgs))
}
}
var person = {
name: 'jayChou'
};
var say = function(p1, p2) {
console.log(this.name, p1, p2);
}
var foo = say.newBind(person, 18); // 第一次傳參
foo(20); // 第二次傳參
複製代碼
最後輸出:
jayChou 18 20
結果正確,以上就完成了 bind 函數基本功能的實現。
可是!
bind 函數其實還有一個很是重要的特色:
bind返回的函數若是做爲構造函數,搭配new關鍵字出現的話,咱們的綁定this就須要「被忽略」。
意思就是指:當使用 nuw 關鍵字把 bind 返回的函數做爲構造函數,以前改變了指向的 this 就失效了。返回的函數的 this 就關聯到了構造函數的實例對象上。
針對這個特色,須要再作一些修改:
Function.prototype.newBind = function(context) {
if(typeof this !== 'function') {
throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
}
var self = this;
var args = Array.prototype.slice.call(arguments, 1); // 間接調用數組方法,獲取第一次傳的參數
let tempFn = function {}; // 利用一個空函數做爲中轉
tempFn.prototype = this.prototype; // 修改返回函數的 prototype 爲綁定函數的 prototype,實例就能夠繼承綁定函數的原型中的值
var resultFn = function () {
var innerArgs = Array.prototype.slice.call(arguments);
if (this instanceof tempFn) { // 若是 返回函數被當作構造函數後,生成的對象是 tempFn 的實例,此時應該將 this 的指向指向建立的實例。
return self.apply(this, args.concat(innerArgs));
} else {
return self.apply(context, args.concat(innerArgs))
}
}
resultFn = new tempFn();
return resultFn;
}
複製代碼
本文嘗試模擬實現了 bind 函數,bind 函數與 call,apply 函數的區別在於,bind 函數返回一個指定了 this 的函數,函數並未執行。其次,當返回的函數做爲構造函數時,以前綁定的 this 會失效。
歡迎關注個人我的公衆號「謝南波」,專一分享原創文章。