# 手写 apply/call/bind

JavaScript 中 call()、apply()、bind() 的用法——菜鸟教程 (opens new window)

这是 Function 的原型方法, 每一个函数实例都拥有这些方法。方法的作用是重新定义现有函数的执行环境,即函数执行过程中的 this 指向,以实现函数复用。

三个方法的第一个参数都是重新指定的函数调用对象。如果未指定,非严格模式下指向 window

call 方法和 apply 方法都是立即执行,区别在于传参形式的不同:

  • call 方法传入参数列表
  • apply 方法传入参数数组

bind 方法返回一个重新定义了执行环境的函数,其传参方式与 call 方法相同。

bind 方法使用到了闭包,把定义时传入的 this 对象存在外包函数里,然后返回一个使用了该 this 对象的匿名函数。

在调用一个存在的函数时,你可以为其指定一个 this 对象。 this 指当前对象,也就是正在调用这个函数的对象。 使用 apply, 你可以只写一次这个方法然后在另一个对象中继承它,而不用在新对象中重复写该方法。

Function.prototype.myCall = function (context, ...args) {
    /* 
        手写思路就是给待绑定的环境添加一个独一无二的属性,指向当前的函数实例;
        然后在该环境下调用函数实例,则函数内部的 this 指向就变成了该环境
        注意使用完毕后要将其删除,以免造成内存泄漏
    */
    if (typeof context === 'undefined' || context === null) {
        context = window
    }
    const fnSymbol = Symbol()
    // 这里的 this 是这个函数实例对象
    context[fnSymbol] = this
    
    const res = context[fnSymbol](...args)
    delete context[fnSymbol]
    return res
}

/* 
    myApply 与 myCall 写法基本相同,唯一不同在传参差异
*/
Function.prototype.myApply = function (context, args) {
    if (typeof context === 'undefined' || context === null) {
        context = window
    }
    const fnSymbol = Symbol()
    context[fnSymbol] = this
    const res = context[fnSymbol](...args)
    delete context[fnSymbol]
    return res
}

/* 
    bind 函数不直接执行,而是返回一个可执行的函数
    (这里取个巧,直接调用前面写好的 apply / call 函数)
*/
Function.prototype.myBind = function(context, ...args) {
    return () => {
        return this.apply(context, args)
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
最后更新时间: 7/29/2021, 9:30:27 PM