js-手写bind

84 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第9天,点击查看活动详情

前言

昨天重新梳理了一下手写 call 的基本思路和实现,今天重新整理一下 bind 的基本思路和实现。

bindcall 十分相似,不同的是,bind 会返回一个被改变 this 指向的函数,而且不会立即执行该函数,需要使用者手动调用。

还有一处很大的不同,就是 thisArg 参数,本人习惯将它称呼为上下文(ctx),说白了,就是函数新的 this 指向,在 call 中,如果没有传入该参数,则默认指向 window;而在 bind 中,如果没有传入该参数,返回函数的执行作用域的 this 将被视为新函数的 thisArg

基本实现

首先,依旧是先创建自定义的 bind 函数,并且 bind 函数是返回一个新的函数,所以整体代码如下:

// 参数分为两个部分,一个是新的 `this` 指向,另一部分是执行函数的参数
Function.prototype.myBind = function (thisArg, ...arg1) {
    // 返回新的函数,不能使用箭头函数,可接受参数
    return funtcion (...arg2) {}
}

注意:不论是定义函数获取返回的函数,都不能使用箭头函数,因为箭头函数无法获取到需要的 this;如果返回的函数使用了箭头函数,则无法实现该功能:如果没有传入 thisArg,返回函数的执行作用域的 this 将被视为新函数的 thisArg
因为箭头函数的 this 在定义时就已经确定了,所以不能使用。

记录下被执行的函数,也就是定义时的 this :

Function.prototype.myBind = function (thisArg, ...arg1) {
    // 记录需要被执行的函数,也就是这里的 this
    const targetFun = this

    return funtcion (...arg2) {}
}

然后在返回的函数中判定新的 this 指向,如果存在 thisArg,就是使用 thisArg,没有,就是使用返回函数执行时的 this;然后执行函数,返回结果,这部分与 call 基本一致:

Function.prototype.myBind = function (thisArg, ...arg1) {
    const targetFun = this

    return funtcion (...arg2) {
        // 确定作用域
        const ctx = thisArg || this
        // 挂载函数
        const key = Symbol()
        ctx[key] = targetFun
        // 接受全部参数,执行函数,获取结果
        const res = ctx[key](...arg1, ...arg2)
        // 删除函数挂载
        delete ctx[key]
        // 返回结果
        return res
    }
}

测试一下:

const bear_1 = {
  name: 'Bear_1',
  write (writer = 'unkonw', time = 'now') {
    console.log(`My name is ${this.name} --by ${writer} in ${time}`)
  }
}

const bear_2 = {
  name: 'Bear_2'
}

const bear_3 = {
  name: 'Bear_3'
}

Function.prototype.myBind = function (thisArg, ...arg1) {
  const targetFun = this
  return function (...arg2) {
    const ctx = thisArg || this
    const key = Symbol()
    ctx[key] = targetFun
    const res = ctx[key](...arg1, ...arg2)
    delete ctx[key]
    return res
  }
}

// 不指定新的作用域
bear_3.bear_2_write = bear_1.write.myBind()
bear_3.bear_2_write('writerB', '2023-03-15')
// 输出:My name is Bear_3 --by writerB in 2023-03-15

// 指定新的作用域
bear_3.bear_2_write = bear_1.write.myBind(bear_2)
bear_3.bear_2_write('writerB', '2023-03-15')
// 输出:My name is Bear_2 --by writerB in 2023-03-15

至此,就基本完成了。