# 22.手写 call、apply、bind

# bind、call、apply 原理和用法

# 用法

apply:
fn.apply(thisObj,数组参数)
定义:应用某一个对象的一个方法,用另一个对象替换当前对象
说明:如果参数不是数组类型的,则会报一个TypeError错误。

call:
fn.call(thisObj, arg1, arg2, argN)
apply与call的唯一区别就是接收参数的格式不同。

bind:
fn.bind(thisObj, arg1, arg2, argN)
bind()方法创建一个新的函数,在bind()被调用时,这个新函数的this被bind的第一个参数指定,其余的参数将作为新函数的参数供调用时使用。
1
2
3
4
5
6
7
8
9
10
11
12

# 原理

apply的实现:
	// apply:参数以数组形式传递,apply之后不会改变this指向
    // apply 原理
	Function.prototype.myCall = function (context, ...args) {
		context = context ? context : window;
		context.fn = this;
		if (!args) {
			// 一定要删除这个this(调用的函数)。
			let res = context.fn();
			delete context.fn;
			delete context.fn;
			return res
		}
		let res = context.fn(...args);
		delete context.fn;
		return res;
	};

call实现:与apply的唯一区别就是参数格式不同

    // apply 原理
    Function.prototype.myCall = function (context) {
        context = context ? Object(context) : window
        context.fn = this
        let obj = [...arguments].splice(1)
        if (!obj) {
             context.fn()
            delete context.fn;
            return
        }
        let r = context.fn(...obj)
        delete context.fn;
        return r
    }

bind 实现:
https://github.com/yygmind/blog/issues/23
	// 这是是原生bind实现的方案也就是博客里面的第四步。但是这样会导致修改原型的时候,也修改了其他对象原型上的属性。也就是说,他们公有同个原型祖先
    Function.prototype.myBind = function () {
        var self = this // this 指向调用者
        const [context, ...parms] = [...arguments]
        var fn = function () {
            //实现函数科里化,这时的arguments是指bind返回的函数传入的参数
            let parms2 = [...arguments]
            //当作为构造函数时,this 指向实例,此时 this instanceof fBound 结果为 true,可以让实例获得来自绑定函数的值,即上例中实例会具有 habit 属性。
            //当作为普通函数时,this 指向 window,此时结果为 false,将绑定函数的 this 指向 context
            return self.apply(this instanceof fn ? this : context, parms.concat(parms2))
        }
        fn.prototype = this.prototype;
        return fn // 返回一个函数。
    }

// 测试用例
var value = 2;
var foo = {
    value: 1
};
function bar(name, age) {
    this.habit = 'shopping';
    console.log(this.value);
    console.log(name);
    console.log(age);
}
bar.prototype.friend = 'kevin';

var bindFoo = bar.bind(foo, 'Jack');
var obj = new bindFoo(20); // 返回正确
// undefined
// Jack
// 20

obj.habit; // 返回正确
// shopping

obj.friend; // 返回正确
// kevin

obj.__proto__.friend = "Kitty"; // 修改原型

bar.prototype.friend; // // Kitty,返回错误,这里被修改了

// 完善的方法就是第五步。
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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
Last Updated: 6/3/2024, 1:08:34 AM