您好,登錄后才能下訂單哦!
本篇內容介紹了“怎么手寫實現bind函數”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!
前面發了一篇文章,面試題目之原生實現call、apply、bind,這篇文章介紹了如何手動實現call、apply、bind,但是前不久重讀這篇文章時發現了實現bind的代碼不是很完善,我們看一段代碼:
function Person(){
this.name="zs";
this.age=18;
this.gender="男"
}
var obj={
hobby:"看書"
}
// 將構造函數的this綁定為obj
var changePerson = Person.bind(obj);
// 直接調用構造函數,函數會操作obj對象,給其添加三個屬性;
changePerson();
// 1、輸出obj
console.log(obj);
// 用改變了this指向的構造函數,new一個實例出來
var p = new changePerson();
// 2、輸出obj
console.log(p);
代碼輸出結果:
1、輸出:
2、輸出:
仔細觀察上面的代碼,再看輸出結果。
我們對Person類使用了bind將其this指向obj,得到了changeperson函數,此處如果我們直接調用changeperson會改變obj,若用new調用changeperson會得到實例 p,并且其__proto__指向Person,我們發現bind失效了。
我們得到結論:用bind改變了this指向的函數,如果用new操作符來調用,bind將會失效。
再看我們這篇文章(面試題目之原生實現call、apply、bind)中bind實現的代碼:
Function.prototype.myBind = function(ctx, ...argv1) {
return (...argv2) => {
return this.call(ctx, ...argv1, ...argv2)
}
}
如果看不太習慣,將其轉化為es5的執行方式:
Function.prototype.mybind = function(){
// 1、保存函數
var _this = this;
// 2、保存目標對象
var context = arguments[0]||window;
// 3、保存目標對象之外的參數,將其轉化為數組;
var rest = Array.prototype.slice.call(arguments,1);
// 4、返回一個待執行的函數
return function F(){
// 5、將二次傳遞的參數轉化為數組;
var rest2 = Array.prototype.slice.call(arguments)
//6、用apply調用第一步保存的函數,并綁定this,傳遞合并的參數數組
_this.apply(context,rest.concat(rest2));
}
}
我們用自己實現的mybind函數,來實現文章最上面的例子,測試一下如果,用mybind函數改變了構造函數的this,然后用new來執行生成的新函數,能否得到和原生bind一樣的效果,測試代碼如下:
function Person(){
this.name="zs";
this.age=18;
this.gender="男"
}
var obj={
hobby:"看書"
}
// 將構造函數的this綁定為obj ,此處調用上面開發的mybind方法;
var changePerson = Person.mybind(obj);
// 直接調用構造函數,函數會操作obj對象,給其添加三個屬性;
changePerson();
// 1、輸出obj
console.log(obj);
// 用改變了this指向的構造函數,new一個實例出來
var p = new changePerson();
// 2、輸出obj
console.log(p);
查看輸出結果:
1、輸出:
2、輸出:
我們用上面實現的mybind改變函數的this,然后調用new方法,發現并未實現和原生bind一樣的效果,我們實現的mybind方法和原生的bind實現的功能還有些差距,那么我們如何修正呢?
仔細觀察代碼,發現突破點再這里: new changeperson()。
這里我們只需要在調用 new changeperson()時候,判斷一下,是否是通過new操作符調用的,如果是new 操作符調用的話,我們就用new直接調用未改變this之前的函數,并返回其結果。
那么如何判斷是否是通過new操作符來調用一個函數呢?這里我們就要用到instanceof了,看看官方文檔對其解釋:
| instanceof運算符用于測試構造函數的prototype屬性是否出現在對象的
| 原型鏈中的任何位置。
翻譯成大白話,就是判斷某個實例是否由某個類或者構造函數生成。
回歸正文,我們知道,我們在用new操作符調用一個構造函數時,或者普通函數,都會在函數內部執行如下步驟:
1、生成一個空對象,
2、然后將this指向這個空對象,
3、最后將這個對象返回。
而這個對象就是這個構造函數的實例,那么只要在函數內部執行 this instanceof 構造函數 來判斷其結果是否為true,就能判斷函數是否是通過new操作符來調用了,若結果為true則是用new操作符調用的,代碼修正如下:
Function.prototype.mybind = function(){
// 1、保存函數
var _this = this;
// 2、保存目標對象
var context = arguments[0]||window;
// 3、保存目標對象之外的參數,將其轉化為數組;
var rest = Array.prototype.slice.call(arguments,1);
// 4、返回一個待執行的函數
return function F(){
// 5、將二次傳遞的參數轉化為數組;
var rest2 = Array.prototype.slice.call(arguments)
if(this instanceof F){
// 6、若是用new操作符調用,則直接用new 調用原函數,并用擴展運算符傳遞參數
return new _this(...rest2)
}else{
//7、用apply調用第一步保存的函數,并綁定this,傳遞合并的參數數組
_this.apply(context,rest.concat(rest2));
}
}
}
此時,測試在運行上面的測試案例,打印結果為:
完美實現了和原生bind一樣的效果,對一個知識點進行比較深入的研究確實不容易,越深入發現涉及的知識越廣泛,就像這篇文章,雖然說得是bind的手動實現,但是其實涉及了new操作符調用的原理,instanceof 的用法。
“怎么手寫實現bind函數”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。