# bind call apply 的实现

# bind

bind() 方法创建一个新的函数,在 bind() 被调用时,这个新函数的 this 被指定为 bind() 的第一个参数,而其余参数将作为新函数的参数,供调用时使用。bind() 函数会 创建一个新的绑定函数(bound function,BF)。绑定函数是一个 exotic function object(怪异函数对象,ECMAScript 2015 中的术语),它包装了原函数对象。调用绑定 函数通常会导致执行包装函数。 --MDN

bind函数一般用于手动绑定this,该函数可以以一种强绑定的方式来绑定 this。也就 是说改变 this 指向后不会立即执行,而是返回一个永久改变 this 值的函数。

实现 bind:

Function.prototype._bind = function (context) {
    if (typeof this !== 'function') {
        throw new Error(
            'Function.prototype.bind - what is trying to be bound is not callable'
        )
    }
    var self = this
    // 获取传入参数
    var args = Array.prototype.slice.call(arguments, 1)

    var fNOP = function () {}

    var fBound = function () {
        // 此时的argumenty已经是bind返回的函数中传入的参数
        var bindArgs = Array.prototype.slice.call(arguments)
        // 是否为new调用,该方式调用bind返回函数this会丢失
        return self.apply(
            this instanceof fNOP ? this : context,
            args.concat(bindArgs)
            // 函数参数连接起来,柯里化的感觉,延迟执行计算
        )
    }
    // 模拟Object.create
    fNOP.prototype = this.prototype
    fBound.prototype = new fNOP()
    return fBound
}
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

# call

call() 方法使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数。

call 实现:

Function.prototype._call = function (context) {
    // 在非严格模式下,call不指定第一个参数时this绑定到全局对象上
    var context = context || window
    context.fn = this //获取调用函数
    // 获取调用参数 不使用es6
    var argus = []
    for (var i = 1; i < arguments.length; i++) {
        args.push('arguments[' + i + ']')
    }
    // 执行调用函数
    var result = eval('context.fn(' + args + ')')
    delete context.fn
    return result
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# apply

apply 实现:

Function.prototype.apply = function (context, arr) {
    // 严格模式下
    var context = Object(context) || window
    context.fn = this

    var result
    if (!arr) {
        result = context.fn()
    } else {
        var args = []
        for (var i = 0, len = arr.length; i < len; i++) {
            args.push('arr[' + i + ']')
        }
        result = eval('context.fn(' + args + ')')
    }

    delete context.fn
    return result
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

call 和 apply 的主要作用,是改变对象的执行上下文,并且是立即执行的。它们在参数上 的写法略有区别。

bind 也能改变对象的执行上下文,它与 call 和 apply 不同的是,返回值是一个函数,并 且需要稍后再调用一下,才会执行。