這篇教學會用3個JavaScript中運用到apply()call()的例子,讓你快速了解apply()call()的使用方法。

目錄

範例應用1: Call Forwarding

有時候,我們想要在一個函式原本的功能之上附加額外的功能,這個時候可以使用Call Forwarding的技巧。

Call Forwarding用一句話來說就是:用一個wrapper函式把原本的函式包起來,並呼叫原本的函式。

const wrappedFunction = function wrapper() {
  return anotherFunction.apply(this, arguments)
}

這裡就會需要用到Function.prototype.apply,用wrapper函式的thisarguments去呼叫原本的anotherFunction

基本的使用方法如下:

function wrapper(func) {
  const wrappedFunction = function() {
    const result = func.apply(this, arguments) // (*)
    // Some custom logic...
    return result
  }

  return wrappedFunction
}

const wrappedFoo = wrapper(foo)
wrappedFoo(bar, baz)

wrapper 函式的引數是一個函式func,回傳值是另一個函式 wrappedFunction

wrappedFunction 被呼叫的時候,會執行原本的func,並且加上你想要的邏輯,最後回傳func被執行的結果。

核心在執行func的這一行(*),也就是func.apply(this, arguments)

補充說明:arguments

arguments是一個函式內部特別的變數,對應到函式被呼叫時傳進來的引數。

我們需要用到wrappedFunctionarguments變數,因為我們會用和func一樣的參數呼叫wrappedFunction,但是我們事先不知道func會有幾個參數。

補充說明:apply()

apply()是函式物件的一個方法,用法是

someFunction.apply(context, arguments)

以下兩者效果大概相同,除了用Function.prototype.apply呼叫someFunction時,this的值會是context。:

someFunction(1, 2, 3)
someFunction.apply(context, [1, 2, 3])

因此呼叫func時的this等於wrappedFunction被呼叫時的this

我們需要特別注意呼叫時的this,是因為也會有以下的使用情境:

const worker = {
  someMethod() {
    return 1;
  },
  slow(x) {
    return x * this.someMethod()
  }
}

worker.slow = wrapper(worker.slow)
worker.slow()

如此一來wrappedFunction呼叫起來跟原本的函式幾乎一模一樣,但是又加上了自己想要的邏輯。

範例應用2: Cache

假設有個計算量非常費時的函式:

function slow(x) {
  // Some CPU heavy task
  return x
}

我們希望加上cache的功能,如果用相同的參數去呼叫這個函式第二次的話,就直接回傳上一次計算過的結果:

function cachingDecorator(func) {
  let cache = new Map() // The cache

  return function() {
    const key = hash(arguments) // Some hash function
    if (cache.has(key)) {
      return cache.get(key) // Retrieve value from the cache
    }

    const result = func.apply(this, arguments) // Compute result

    cache.set(key, result) // Save value to the cache
    return result
  }
}

slow = cachingDecorator(slow)
slow(1)
slow(1) // Cached!
slow(1) // Cached!

範例應用3: Method Borrowing

上例中的hash()的實作需要注意。

以下寫法有問題,因為arguments不是一個array,沒有join方法:

function hash() {
  return arguments.join(',') // Not working!
}

但是利用Function.prototype.call,就可以借用Array.prototype.join方法:

function hash() {
  return [].join.call(arguments, ',')
}

Reference