日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 >

实现一个bind函数

發布時間:2025/3/8 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 实现一个bind函数 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

目前的打算還是繼續深入前端基礎知識,所以打算從polyfill開始做起。

bind函數

bind函數最常見的用法是綁定函數的上下文,比如在setTimeout中的this一般都是指向window,如果我們想改變上下文,這里可以使用bind函數來實現。

var a = 10; var test = function() {console.log(this.a); } // 如果直接執行test,最終打印的是10. var bindTest = test.bind({a: "111"}) bindTest(); // 111 復制代碼

從上面這個例子可以看出來,bind函數改變了test函數中this的指向。 除此之外,bind函數還有兩個特殊的用法,一個是柯里化,一個是綁定構造函數無效。

柯里化

bind函數的柯里化其實是不完全的,其實只做了一次柯里化,看過MDN的polyfill實現后也就理解了。

var test = function(b) {return this.a + b; } // 如果直接執行test,最終打印的是10. var bindTest1 = test.bind({a: 20}); bindTest1(10); // 30 // 這里的bind是個柯里化的函數 var bindTest2 = test.bind({a: 20}, 10); bindTest2(); // 30; 復制代碼

構造函數無效

其實準確的來說,bind并不是對構造函數無效,只是對new的時候無效,如果直接執行構造函數,那么還是有效的。

var a = 10; var Test = function(a) {console.log(this.a); } var bindTest = Test.bind({a: 20}); bindTest(); // 20 // 在new的時候,Test中的this并沒有指向bind中的對象 new bindTest(); // undefined 復制代碼

實現一個bind

我們可以先實現一個簡易版本的bind,再不斷完善。由于是在函數上調用bind,所以bind方法肯定存在于Function.prototype上面,其次bind函數要有改變上下文的作用,我們想一想,怎么才能改變上下文?沒錯,就是call和apply方法。

然后還要可以柯里化,還好這里只是簡單的柯里化,我們只要在bind中返回一個新的函數,并且將前后兩次的參數收集起來就可以做到了。

Function.prototype.bind = function() {var args = arguments;// 獲取到新的上下文var context = args[0];// 保存當前的函數var func = this;// 獲取其他的參數var thisArgs = Array.prototype.slice.call(args, 1);var returnFunc = function() {// 將兩次獲取到的參數合并Array.prototype.push.apply(thisArgs, arguments)// 使用apply改變上下文return func.apply(context, thisArgs);}return returnFunc; } 復制代碼

這里實現了一個簡單的bind函數,可以支持簡單的柯里化,也可以改變上下文作用域,但是在new一個構造函數的時候還是會改變上下文。

這里我們需要考慮一下,怎么做才能讓在new的時候無效,而其他時候有效?

所以我們需要在returnFunc里面的apply第一個參數進行判斷,如果是用new調用構造函數的時候應該傳入函數本身,否則才應該傳入context,那么該怎么判斷是new調用呢?

關于在new一個構造函數的時候,這中間做了什么,建議參考這個問題:在js里面當new了一個對象時,這中間發生了什么?

所以我們很容易得出,由于最終返回的是returnFunc,所以最終是new的這個函數,而在new的過程中,會執行一遍這個函數,所以這個過程中returnFunc里面的this指向new的時候創建的那個對象,而那個新對象指向returnFunc函數。

但是我們希望調用后的結果只是new的func函數,和我們正常new func一樣,所以這里猜想,在returnFunc中,一定會將其this傳入func函數中執行,這樣才能滿足這幾個條件。

Function.prototype.bind = function() {var args = arguments || [];var context = args[0];var func = this;var thisArgs = Array.prototype.slice.call(args, 1);var returnFunc = function() {Array.prototype.push.apply(thisArgs, arguments);// 最關鍵的一步,this是new returnFunc中創建的那個新對象,此時將其傳給func函數,其實相當于做了new操作最后一步(執行構造函數)return func.apply(this instanceof returnFunc ? this : context, thisArgs);}return returnFunc } function foo(c) {this.b = 100;console.log(c);return this.a; }var func = foo.bind({a:1}); var newFunc = new func() // undefined 復制代碼

但是這樣還是不夠的,如果foo函數原型上面還有更多的方法和屬性,這里的newFunc是沒法獲取到的,因為foo.prototype不在newFunc的原型鏈上面。 所以這里我們需要做一些改動,由于傳入apply的是returnFunc的一個實例(this),所以我們應該讓returnFunc繼承func函數,最終版是這樣的。

Function.prototype.bind = function() {var args = arguments || [];var context = args[0];var func = this;var thisArgs = Array.prototype.slice.call(args, 1);var returnFunc = function() {Array.prototype.push.apply(thisArgs, arguments);// 最關鍵的一步,this是new returnFunc中創建的那個新對象,此時將其傳給func函數,其實相當于做了new操作最后一步(執行構造函數)return func.apply(this instanceof func ? this : context, thisArgs);}returnFunc.prototype = new func()return returnFunc } 復制代碼

這樣我們就完成了一個bind函數,這與MDN上面的polyfill實現方式大同小異,這里可以參考一下MDN的實現:Function.prototype.bind()

參考鏈接:

  • MDN:Function.prototype.bind()

  • 手寫bind()函數,理解MDN上的標準Polyfill

  • 轉載于:https://juejin.im/post/5c0cdcb9e51d4504a02eb5bb

    總結

    以上是生活随笔為你收集整理的实现一个bind函数的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。