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

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

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

1: call 和apply 的作用

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

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

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

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

請(qǐng)看下面2個(gè)??

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)

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

// 在瀏覽器環(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: 說(shuō)清楚apply和call ,我們開始講講bind。

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

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

來(lái)一個(gè)??演示一下

let obj = {name: 'jack'}

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

func() 

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

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

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

我們?cè)俳o一個(gè)??演示下:

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 來(lái)改造下這個(gè)寫法:

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

幾行代碼就可以解決,是不是簡(jiǎn)潔了許多??

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

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