手写 call apply bind 详细解读

231 阅读2分钟

前言

先说明每个方法如何使用

  • 三个方法都是为了改变方法执行的 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))
    }
}

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