手寫 call apply bind 詳細解讀

qwer ?發表於2019-12-30

前言

先說明每個方法如何使用

  • 三個方法都是為了改變方法執行的 this 指向
  • call、apply是呼叫後改變 this 立即執行
  • bind 是返回一個改變 this 執行的函式,在實際呼叫時確定 this 並執行

call 使用

function add (a, b) {
    console.log(this + a + b)
}
add.call(1, 2, 3)
複製程式碼
  1. 需要實現執行 add 函式時改變其 this 指向,如果直接執行 add(2, 3)則 this 指向為 window,確定 this 指向就看誰呼叫了函式,那麼需要實現 1.add(2, 3),this則為1
  2. 而 1 是 number 基本型別不會有方法,所以當傳入的this不是物件型別就先將 this 包裝為物件,然後為 this 新增函式(要改變 this 指向的函式),接著執行 this.f(params) 呼叫時就改變了原函式的 this 指向

call 實現

將要執行的函式新增到this屬性上,然後呼叫 this.f(...params),即實現了f的this執行為傳入的 this的呼叫

Function.prototype.call = function(thisValue, ...params) {
    if (typeof thisValue !== 'object') {
        thisValue = new Object(thisValue)
    }
    let context = this
    thisValue.f = context
    // 定義為不可列舉
    Object.defineProperty(thisValue, 'f', {
        enumerable: false,
        get () {
            return context
        }
    })
    thisValue.f(...params)
    // 刪除為 this 臨時新增的函式
    delete thisValue.f
}
複製程式碼

apply 使用

function add (a, b) {
    console.log(this + a + b)
}
add.apply(1, [2, 3])
複製程式碼

apply 實現

Function.prototype.apply = function (thisValue, params) {
    if (thisValue !== 'object') {
        thisValue = new Object(thisValue)
    }
    let context = this
    thisValue.f = context
    Object.defineProperty(thisValue, 'f', {
        enumerable: false,
        get () {
            return context
        }
    })
    thisValue.f(...params)
    delete thisValue.f
}
複製程式碼

bind 使用

function add (a, b, c) {
    console.log(this)
    console.log(a, b)
    console.log(c)
}
let add2 = add.bind({ value: 1 }) // 返回一個改變 this 指向的函式
add2(2, 3) // 真實執行
複製程式碼

返回一個改變 this 執行的函式

bind 實現

將要執行的函式新增到this的屬性上, 然後返回一個可接受引數的函式,該函式內部執行 this.f(arg1.concat(arg2)),執行函式,引數為繫結時的引數concat執行時的引數

Function.prototype.bind = function(thisValue, ...args) {
    if (typeof thisValue !== 'object') {
        thisValue = new Object(thisValue)
    }
    let context = this
    thisValue.f = context
    Object.defineProperty(thisValue, 'f', {
        enumerable: false,
        get () {
            return context
        }
    })
    return function (...args2) {
        thisValue.f(...args.concat(args2))
    }
}
複製程式碼

✿✿ヽ(°▽°)ノ✿ 完成啦

相關文章