javascript bind函數(shù)的實現(xiàn)

既然講到bind,我們就不得不說call 和 apply 。在Javascript中,涉及到函數(shù)式語言風(fēng)格的代碼,都離不開call 和apply。那么我們在講bind之前,就先好好分析一下call 和 apply。

callapply 方法是ECMAScript 3 給Function 的原型定義的2個方法。分別是Function.prototype.call 和 Function.prototype.apply。

1: call 和apply 的作用

call 的mdn定義: call() 方法使用一個指定的 this 值和單獨(dú)給出的一個或多個參數(shù)來調(diào)用一個函數(shù)。

apply的mdn定義:apply()方法調(diào)用一個具有給定this值的函數(shù),以及作為一個數(shù)組(或類似數(shù)組對象提供的參數(shù)。

通過以上的定義,我們可以看出,call 和 apply 的相同點(diǎn)都是執(zhí)行一個函數(shù),并指定this。
唯一的區(qū)別就是:

  • apply 函數(shù)第二個參數(shù)為一個帶下標(biāo)的集合,這個集合可以是數(shù)組,也可以是類數(shù)組。apply 把這個集合中的元素作為參數(shù)傳遞給被調(diào)用的函數(shù)。
  • call 函數(shù)的參數(shù)數(shù)量不固定,從第一個參數(shù)以后, 每一個參數(shù)被依次傳入函數(shù)。

請看下面2個??

var func = function (a,b,c) {
 console.log([a,b,c]) // 輸出 [1,2,3]
}

func.apply(null, [1,2,3])
var func = function (a,b,c) {
  console.log([a,b,c]) // 輸出[1,2,3]
}
func.call(null, 1,2,3)

其實Javascript參數(shù)在內(nèi)部就是用一個類數(shù)組來表示的。我們可以通過arguments類數(shù)組來接受。call其實是包裝了apply上的一個語法糖。如果我們明確的知道函數(shù)接受多少個參數(shù),而且想一目了然的表達(dá)形參和實參的對應(yīng)關(guān)系,可以使用call 來傳遞參數(shù)。
有一點(diǎn)需要注意的是:當(dāng)使用call 或apply時,第一個參數(shù)為null, 函數(shù)體內(nèi)的this會指向默認(rèn)的宿主對象。

// 在瀏覽器環(huán)境下
var func = function (a,b,c) {
  console.log(this === window)  // true
}
func.apply(null, [1,2,3])

Math.min.apply(null, [1,2,3,34,4]) // 1

2: 說清楚apply和call ,我們開始講講bind。

bind 用來改變this的指向,并返回一個改變了this指向的新函數(shù)。
我們首先實現(xiàn)一個比較簡單的bind。

Function.prototype.bind = function (context) {
  // this  指的是需要綁定的函數(shù)
  let self = this
  return function () {
    // context 指需要綁定的this
    return self.apply(context, arguments)
   }
}

來一個??演示一下

let obj = {name: 'jack'}

var func = function () {
  console.log(this.name) // jack
}.bind(obj)

func() 

以上代碼的實現(xiàn)原理是,我們先把func函數(shù)的引用保存了起來。然后返回一個新函數(shù)。當(dāng)我們將來執(zhí)行func函數(shù)時。實際上是執(zhí)行這個返回的新函數(shù)。在新函數(shù)內(nèi)部,我們執(zhí)行了self.apply(context, arguments)這一句才是執(zhí)行了原來func函數(shù)。并指定了context為func函數(shù)體內(nèi)的this。

說清楚了原理,我們實現(xiàn)一個更完善的bind實現(xiàn):

Function.prototype.bind  = function () {
 let self = this
  // 獲取bind第一個參數(shù),即需要綁定的對象。
 context = [].shift.call(arguments)
 // 獲取bind剩余的參數(shù),并轉(zhuǎn)換為數(shù)組
 args = [].slice.call(arguments)
 // 返回一個函數(shù),當(dāng)執(zhí)行的時候,執(zhí)行self.apply
 return function () {
     // arguments 是執(zhí)行返回的函數(shù)時,傳入的參數(shù)。
     return self.apply(context, [].concat.call(args, [].slice.call(arguments)))
 }
}

我們再給一個??演示下:

var obj = {
 name: 'jack',
}
var func = function (a,b,c,d) {
 console.log(this.name) // jack
 console.log([a,b,c,d])  // 輸出 [1,2,3,4]
}.bind(obj, 1,2)

func(3,4)

我們用es6 來改造下這個寫法:

Function.prototype.bind  = function (context, ...res) {
  let self = this
   // 獲取bind第一個參數(shù),即需要綁定的對象。
  context = context
  // 獲取bind剩余的參數(shù),并轉(zhuǎn)換為數(shù)組
  args = res
  // 返回一個函數(shù),當(dāng)執(zhí)行的時候,執(zhí)行self.apply
  return function () {
      // arguments 是執(zhí)行返回的函數(shù)時,傳入的參數(shù)。
      return self.apply(context, [...args, ...arguments])
  }
}

幾行代碼就可以解決,是不是簡潔了許多??

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容