bind()
方法和apply()
、call()
類似,均可以用來改變某個函數運行時this
的指向。bash
而且一樣接受的第一個參數做爲它運行時的this
,以後的參數都會傳入做爲它的參數。
閉包
可是bind()
還有一個最大的特色就是它會建立一個新的函數,以便於咱們稍後做調用,這也是它區別於apply()
和call()
的地方。
app
先來看下bind
的使用函數
var foo = {
value: 1
};
function bar() {
return this.value;
}
var bindFoo = bar.bind(foo);
console.log(bindFoo()); // 1
複製代碼
模擬實現一
post
Function.prototype.bind2 = function(context) {
// 將this做保存,表明被綁定的函數
var self = this;
return function() {
// 綁定函數可能會有返回值,因此這裏要return一下
return self.apply(context);
}
}
複製代碼
bind的傳參:須要注意咱們在bind的時候能夠進行傳參,而且在執行bind返回的函數的時候依然能夠傳參。以下例子:ui
var foo = {
value: 1
};
function bar(name, age) {
console.log(this.value);
console.log(name);
console.log(age);
}
var bindFoo = bar.bind(foo, 'xiao');
bindFoo('18');
// 1
// xiao
// 18
複製代碼
傳參效果的實現this
Function.prototype.bind2 = function (context) {
var self = this;
// 獲取 bind2 函數從第二個參數到最後一個參數
var args = Array.prototype.slice.call(arguments, 1);
return function () {
// 這裏的arguments是指bind返回的函數傳入的參數
var bindArgs = Array.prototype.slice.call(arguments);
return self.apply(context, args.concat(bindArgs));
}
}
複製代碼
bind還有一個特色:就是 bind 返回的函數能夠被做爲構造函數來使用,此時 bind 指定的this值會失效,但傳入的參數依然生效。以下例子:spa
var value = 2;
var foo = {
value: 1
};
function bar(name, age) {
this.habit = 'shopping';
console.log(this.value);
console.log(name);
console.log(age);
}
bar.prototype.friend = 'kevin';
var bindFoo = bar.bind(foo, 'daisy');
var obj = new bindFoo('18');
// undefined
// daisy
// 18
console.log(obj.habit);
console.log(obj.friend);
// shopping
// kevin
複製代碼
能夠看到因爲這裏使用了new
操做符,this
已經指向了obj
,所以this.value
打印出來爲undefined
。prototype
構造函數效果的實現
code
爲了讓this
指向new
出來的對象,咱們能夠經過修改返回的函數的原型來實現
Function.prototype.bind2 = function (context) {
var self = this;
var args = Array.prototype.slice.call(arguments, 1);
var fBound = function () {
var bindArgs = Array.prototype.slice.call(arguments);
// 判斷是否做用構造函數
// 看成爲構造函數時,將綁定函數的 this 指向 new 建立的實例,可讓實例得到來自綁定函數的值
// 看成爲普通函數時,將綁定函數的 this 指向 context
return self.apply(this instanceof fBound ? this : context, args.concat(bindArgs));
}
// 修改返回函數的 prototype 爲綁定函數的 prototype,實例就能夠繼承綁定函數的原型中的值
fBound.prototype = this.prototype;
return fBound;
}
複製代碼
不過上面的寫法還存在點問題,fBound.prototype = this.prototype
這一句代碼直接修改了 fBound.prototype
,也會直接修改綁定函數的 prototype。以下例子:
function bar() {}
var bindFoo = bar.bind2(null);
// 修改 bindFoo 的值
bindFoo.prototype.value = 1;
// 致使 bar.prototype 的值也被修改了
console.log(bar.prototype.value) // 1
複製代碼
所以能夠經過一個空函數做一箇中轉,避免綁定函數的 prototype 的屬性被修改:
Function.prototype.bind2 = function (context) {
var self = this;
var args = Array.prototype.slice.call(arguments, 1);
var fNOP = function () {};
var fBound = function () {
var bindArgs = Array.prototype.slice.call(arguments);
return self.apply(this instanceof fNOP ? this : context, args.concat(bindArgs));
}
fNOP.prototype = this.prototype;
fBound.prototype = new fNOP();
return fBound;
}
複製代碼
當調用bind的不是函數還得作一下錯誤處理,完整實現以下:
Function.prototype.bind2 = function (context) {
if (typeof this !== "function") {
throw new Error("Function.prototype.bind - what is trying to be bound is not callable");
}
var self = this;
var args = Array.prototype.slice.call(arguments, 1);
var fNOP = function () {};
var fBound = function () {
var bindArgs = Array.prototype.slice.call(arguments);
return self.apply(this instanceof fNOP ? this : context, args.concat(bindArgs));
}
fNOP.prototype = this.prototype;
fBound.prototype = new fNOP();
return fBound;
}
複製代碼
結尾
對於這篇文章理解起來有難度的話,建議先回顧一下原型,做用域和閉包相關的知識,能夠看看我以前的文章,這些知識點都是相互關聯的。連接在下方:
系列文章: