咱們先來看看MDN上給出的簡介markdown
❝bind() 方法建立一個新的函數,在 bind() 被調用時,這個新函數的 this 被指定爲 bind() 的第一個參數,而其他參數將做爲新函數的參數,供調用時使用。app
❞
是否是看不太懂,不要緊我翻譯一下,說人話就是 bind 函數執行完會返回一個新的函數,後續咱們都稱爲綁定函數,執行這個綁定函數時, this 的指向就會變成 bind 函數的第一個參數,其餘參數會做爲返回的綁定函數的參數,在執行綁定函數的時候再傳入到這個函數中。 這下明白了吧,什麼?你沒聽明白,管你聽沒聽懂,看就完事。函數
舉個例子:oop
const module = {
x: 42,
getX: function (y = 0) {
return this.x + y;
}
};
const unboundGetX = module.getX;
console.log(unboundGetX()); // NaN 該函數在全局做用域中被調用,此時this指向的是window,
const boundGetX = unboundGetX.bind(module); // 這個時候咱們將新的函數的this指向經過bind改爲了module
console.log(boundGetX()); // 42
console.log(boundGetX(3)); // 45
複製代碼
經過上面的例子咱們已經明白了 bind 的功能了,能夠咱們動手實現 bind 函數了。post
在寫代碼前咱們須要瞭解下 bind 有哪些特性,而後咱們能夠根據它的特性來進行實現ui
Function.prototype.selfBind = function (context, ...bindArgs) {
const self = this;
return function (...args) {
return self.apply(context, bindArgs.concat(args)); // 利用apply修改指向傳入的第一個參數,同時參數拼接給新的函數
}
}
複製代碼
代碼寫完了,咱們來執行上面的例子來看下是否知足要求this
const module = {
x: 42,
getX: function (y = 0) {
return this.x + y;
}
};
Function.prototype.selfBind = function (context, ...bindArgs) {
const self = this;
return function (...args) {
return self.apply(context, bindArgs.concat(args));
}
}
const unboundGetX = module.getX;
const boundGetX = unboundGetX.selfBind(module); // 使用本身自定義的bind修改this指向
console.log(boundGetX()); // 42,輸出的值和原生bind同樣
console.log(boundGetX(3)); // 45,輸出的值和原生bind同樣
複製代碼
這樣咱們就實現...實現好了嗎?spa
咱們來看看MDN的原話吧。prototype
❝綁定函數自動適應於使用 new 操做符去構造一個由目標函數建立的新實例。當一個綁定函數是用來構建一個值的,原來提供的 this 就會被忽略。不過提供的參數列表仍然會插入到構造函數調用時的參數列表以前。翻譯
❞
什麼意思呢?就是咱們能夠把 bind 返回的函數當作構造函數去用 new 操做符建立實例的時候,bind 傳入的 this 指向會失效,可是傳入的參數仍是有效的。
咱們先來看看原生的 bind 效果吧
const module = {
x: 42,
getX: function (y = 0) {
console.log('this.x: ', this.x);
console.log('y :', y);
this.property = '我是getX自帶的屬性';
return this.x + y;
}
};
module.getX.prototype.prototypeProperty = '我是getX原型上的屬性'; // 給getX原型掛載數據
const unboundGetX = module.getX;
const boundGetX = unboundGetX.bind(module); // 注意這裏是用的原生的bind
const instance = new boundGetX(3);
// this.x: undefined
// y: 3
console.log(instance.property);
// 我是getX自帶的屬性
console.log(instance.prototypeProperty);
// 我是getX原型上的屬性
複製代碼
這裏咱們能夠看到 this 指向的不是傳入的 module 並且 getX ,這實際上是 new 操做符帶來的影響,下週我會繼續出一篇關於 new 操做符的內幕,有興趣的小夥伴能夠點個關注。我知道大家這時候都是
那麼咱們如今來處理下被 new 的狀況
Function.prototype.selfBind = function (context, ...bindArgs) {
const self = this;
let fBound = function (...args) {
// 若是綁定函數做爲構造函數使用,經過判斷this是否繼承原函數,this指向當前實例,self指向須要綁定的函數
return self.apply(this instanceof self ? this : context, bindArgs.concat(args));
}
// 修改綁定函數的prototype爲要綁定的函數的prototype,實例就能繼承函數的原型,這樣上面才能用instanceof判斷this是否繼承self
function Fn() {};
Fn.prototype = this.prototype;
fBound.prototype = new Fn();
return fBound;
}
複製代碼
結果驗證
const module = {
x: 42,
getX: function (y = 0) {
console.log('this.x: ', this.x);
console.log('y :', y);
this.property = '我是getX自帶的屬性';
return this.x + y;
}
};
module.getX.prototype.prototypeProperty = '我是getX原型上的屬性'; // 給getX原型掛載數據
const unboundGetX = module.getX;
const boundGetX = unboundGetX.selfBind(module); // 注意這裏是用的原生的bind
const instance = new boundGetX(3);
// this.x: undefined
// y: 3
console.log(instance.property);
// 我是getX自帶的屬性
console.log(instance.prototypeProperty);
// 我是getX原型上的屬性
複製代碼
和原生的 bind 返回同樣,這樣咱們就完整的實現了一個原生的 bind 方法???no no no,這還不夠嚴謹,咱們還須要對調用 bind 的對象進行一個類型校驗
爲了不一些特殊狀況的發生,好比:
let obj = {};
obj.__proto__ = Function.prototype;
obj.selfBind(module);
複製代碼
咱們就須要對調用者進行一個類型校驗了,判斷 this 類型是不是 function 便可
if (typeof this !== 'function') {
throw new TypeError('Function.prototype.selfBind - what is trying to be bound is not callable');
}
複製代碼
Function.prototype.selfBind = function (context, ...bindArgs) {
if (typeof this !== 'function') {
// closest thing possible to the ECMAScript 5
// internal IsCallable function
throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
}
const self = this;
let fBound = function (...args) {
// 若是綁定函數做爲構造函數使用,經過判斷this是否繼承原函數,this指向當前實例,self指向須要綁定的函數
return self.apply(this instanceof self ? this : context, bindArgs.concat(args));
}
// 修改綁定函數的prototype爲要綁定的函數的prototype,實例就能繼承函數的原型,這樣上面才能用instanceof判斷this是否繼承self
function Fn() {};
Fn.prototype = this.prototype;
fBound.prototype = new Fn();
return fBound;
}
複製代碼
很是感謝各位能閱讀到這裏,以爲有幫助的話不妨點個贊,你的支持是對我對最大的鼓勵。
新一篇的 new 已經更新了,歡迎各位看官捧場!!