溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

如何使用JS簡單實(shí)現(xiàn)apply、call和bind方法

發(fā)布時間:2022-02-21 09:11:05 來源:億速云 閱讀:145 作者:iii 欄目:開發(fā)技術(shù)

這篇文章主要講解了“如何使用JS簡單實(shí)現(xiàn)apply、call和bind方法”,文中的講解內(nèi)容簡單清晰,易于學(xué)習(xí)與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學(xué)習(xí)“如何使用JS簡單實(shí)現(xiàn)apply、call和bind方法”吧!

1.方法介紹

apply、call和bind都是系統(tǒng)提供給我們的內(nèi)置方法,每個函數(shù)都可以使用這三種方法,是因?yàn)閍pply、call和bind都實(shí)現(xiàn)在了Function的原型上(Function.prototype),而他們的作用都是給我們函數(shù)調(diào)用時顯式綁定上this。下面先介紹一下它們的基本用法:

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

使用語法:func.apply(thisArg, [argsArray])

thisArg:在func函數(shù)調(diào)用時綁定的this值;[argsArray]:一個數(shù)組或者類數(shù)組對象,其中的數(shù)組元素將作為單獨(dú)的參數(shù)傳給func函數(shù);

使用效果:

function foo(x, y ,z) {
  console.log(this, x, y, z)
}

const obj = { name: 'curry', age: 30 }
/**
 * 1.將obj對象綁定給foo函數(shù)的this
 * 2.數(shù)組中的1 2 3分別傳遞給foo函數(shù)對應(yīng)的三個參數(shù)
 */
foo.apply(obj, [1, 2, 3])

如何使用JS簡單實(shí)現(xiàn)apply、call和bind方法

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

使用語法:func.call(thisArg, arg1, arg2, ...)

thisArg:在func函數(shù)調(diào)用時綁定的this值;arg1, arg2, ...:指定的參數(shù)列表,將作為參數(shù)傳遞給func函數(shù);

使用效果:

function foo(x, y ,z) {
  console.log(this, x, y, z)
}

const obj = { name: 'curry', age: 30 }
/**
 * 1.將obj對象綁定給foo函數(shù)的this
 * 2.call剩余參數(shù)中的a b c分別傳遞給foo函數(shù)對應(yīng)的三個參數(shù)
 */
foo.call(obj, 'a', 'b', 'c')

如何使用JS簡單實(shí)現(xiàn)apply、call和bind方法

bind方法:創(chuàng)建一個新的函數(shù),在bind()被調(diào)用時,這個新函數(shù)的this被指定為bind()的第一個參數(shù),而其余參數(shù)將作為新函數(shù)的參數(shù),供調(diào)用時使用。

使用語法:func.bind(thisArg[, arg1[, arg2[, ...]]])

thisArg:調(diào)用func函數(shù)時作為this參數(shù)傳遞給目標(biāo)函數(shù)的值;arg1, arg2, ...:當(dāng)目標(biāo)函數(shù)被調(diào)用時,被預(yù)置入func函數(shù)的參數(shù)列表中的參數(shù);

使用效果:

function foo(...args) {
  console.log(this, ...args)
}

const obj = { name: 'curry', age: 30 }
/**
 * 1.將obj對象綁定給foo函數(shù)的this
 * 2.bind剩余參數(shù)中的1 2 3分別傳遞給foo函數(shù)中參數(shù)
 * 3.也可在newFoo調(diào)用時傳入?yún)?shù),這時bind傳遞的參數(shù)會與newFoo調(diào)用時傳遞的參數(shù)進(jìn)行合并
 */
const newFoo = foo.bind(obj, 1, 2, 3)
newFoo()
newFoo('a', 'b', 'c')

如何使用JS簡單實(shí)現(xiàn)apply、call和bind方法

總結(jié):

apply和call主要用于在函數(shù)調(diào)用時給函數(shù)的this綁定對應(yīng)的值,兩者作用類似,主要區(qū)別就是除了第一個參數(shù),apply方法接受的是一個參數(shù)數(shù)組,而call方法接受的是參數(shù)列表。

bind也是給函數(shù)指定this所綁定的值,不同于apply和call的是,它會返回一個新的函數(shù),新函數(shù)中的this指向就是我們所指定的值,且分別傳入的參數(shù)會進(jìn)行合并。

2.apply、call和bind方法的實(shí)現(xiàn)

為了所有定義的函數(shù)能夠使用我們自定義的apply、call和bind方法,所以需要將自己實(shí)現(xiàn)的方法掛在Function的原型上,這樣所有的函數(shù)就可以通過原型鏈找到自定義的這三個方法了。

2.1.apply的實(shí)現(xiàn)

Function.prototype.myApply = function(thisArg, argArray) {
  // 1.獲取當(dāng)前需要被執(zhí)行的函數(shù)
  // 因?yàn)閙yApply是需要被當(dāng)前函數(shù)進(jìn)行調(diào)用的,根據(jù)this的隱式綁定,此處的this就是指向當(dāng)前需要被執(zhí)行的函數(shù)
  const fn = this

  // 2.對傳入的thisArg進(jìn)行邊界判斷
  if (thisArg === null || thisArg === undefined) {
    // 當(dāng)傳入的是null或者undefined是,被執(zhí)行函數(shù)的this直接指向全局window
    thisArg = window
  } else {
    // 將傳入的thisArg對象化,方便后面在thisArg添加屬性
    thisArg = Object(thisArg)
  }
  // 也可簡單寫成三元運(yùn)算符:
  // thisArg = (thisArg === null || thisArg === undefined) ? window : Object(thisArg)

  // 3.將獲取的fn添加到thisArg對象上
  // 這里使用Symbol的原因是避免外部傳入的thisArg中的屬性與添加fn有沖突
  const fnSymbol = Symbol()
  Object.defineProperty(thisArg, fnSymbol, {
    enumerable: false,
    configurable: true,
    writable: false,
    value: fn
  })
  // 也可簡單寫成
  // thisArg[fnSymbol] = fn

  // 4.對argArray進(jìn)行判斷
  // 看是否有傳入值,沒有值傳入就默認(rèn) []
  argArray = argArray || []

  // 5.調(diào)用獲取的fn函數(shù),并將對應(yīng)傳入的數(shù)組展開傳遞過去
  const result = thisArg[fnSymbol](...argArray)
  // 調(diào)用完后刪除添加的屬性
  delete thisArg[fnSymbol]

  // 6.將結(jié)果返回
  return result
}

測試:雖然打印出來的對象中還存在Symbol屬性,實(shí)際上已經(jīng)通過delete刪除了,這里是對象引用的問題。

function foo(x, y, z) {
  console.log(this, x, y, z)
}

foo.myApply({name: 'curry'}, [1, 2, 3])

如何使用JS簡單實(shí)現(xiàn)apply、call和bind方法

2.2.call的實(shí)現(xiàn)

call方法的實(shí)現(xiàn)和apply方法的實(shí)現(xiàn)差不多,主要在于后面參數(shù)的處理。

Function.prototype.myCall = function(thisArg, ...args) {
  // 1.獲取當(dāng)前需要被執(zhí)行的函數(shù)
  const fn = this

  // 2.對傳入的thisArg進(jìn)行邊界判斷
  thisArg = (thisArg === null || thisArg === undefined) ? window : Object(thisArg)

  // 3.將獲取的fn添加到thisArg對象上
  const fnSymbol = Symbol()
  thisArg[fnSymbol] = fn

  // 4.調(diào)用獲取的fn函數(shù),并將對應(yīng)傳入的args傳遞過去
  const result = thisArg[fnSymbol](...args)
  // 調(diào)用完后刪除添加的屬性
  delete thisArg[fnSymbol]

  // 5.將結(jié)果返回
  return result
}

測試:

function foo(x, y, z) {
  console.log(this, x, y, z)
}

foo.myCall({name: 'curry'}, 1, 2, 3)

如何使用JS簡單實(shí)現(xiàn)apply、call和bind方法

2.3.bind的實(shí)現(xiàn)

bind方法的實(shí)現(xiàn)稍微復(fù)雜一點(diǎn),需要考慮到參數(shù)合并的問題。

Function.prototype.myBind = function(thisArg, ...argsArray) {
  // 1.獲取當(dāng)前的目標(biāo)函數(shù),也就是當(dāng)前使用myBind方法的函數(shù)
  const fn = this

  // 2.對傳入的thisArg進(jìn)行邊界判斷
  thisArg = (thisArg === null || thisArg === undefined) ? window : Object(thisArg)

  // 3.將獲取的fn添加到thisArg對象上
  const fnSymbol = Symbol()
  thisArg[fnSymbol] = fn

  // 4.定義一個新的函數(shù)
  function newFn(...args) {
    // 4.1.合并myBind和newFn傳入的參數(shù)
    const allArgs = [...argsArray, ...args]
    // 4.2.調(diào)用真正需要被調(diào)用的函數(shù),并將合并后的參數(shù)傳遞過去
    const result = thisArg[fnSymbol](...allArgs)
    // 4.3.調(diào)用完后刪除添加的屬性
    delete thisArg[fnSymbol]

    // 4.4.將結(jié)果返回
    return result
  }

  // 6.將新函數(shù)返回
  return newFn
}

測試:

function foo(x, y, z) {
  console.log(this, x, y, z)
}

const newFoo = foo.myBind({ name: 'curry' }, 1, 2)
newFoo(3)

如何使用JS簡單實(shí)現(xiàn)apply、call和bind方法

感謝各位的閱讀,以上就是“如何使用JS簡單實(shí)現(xiàn)apply、call和bind方法”的內(nèi)容了,經(jīng)過本文的學(xué)習(xí)后,相信大家對如何使用JS簡單實(shí)現(xiàn)apply、call和bind方法這一問題有了更深刻的體會,具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是億速云,小編將為大家推送更多相關(guān)知識點(diǎn)的文章,歡迎關(guān)注!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI