ES6模拟call,apply,bind

ES6自己实现

call

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Function.prototype.call2 = function(context,...list) {
context = context || window;
context.fn = this;
let result = context.fn(...list);
delete context.fn
return result;
}

var value = "JS";
var foo = {
value: "Node"
};

function bar(...list) {
console.log(this.value);
console.log(list)
}

bar.call2(foo,1,2,3);
bar.call2(null,4,5,6);
bar.call2(undefined,4,5,6);

apply

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Function.prototype.apply2 = function(context,arr) {
context = context || window;
context.fn = this;
let result = context.fn(...arr);
delete context.fn
return result;
}

var value = "JS";
var foo = {
value: "Node"
};

function bar(...list) {
console.log(this.value);
console.log(list)
}

bar.apply2(foo,[1,2,3]);
bar.apply2(null,[4,5,6]);
bar.apply2(undefined,[4,5,6]);

bind1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Function.prototype.bind1 = function(context, ...arr1) {
let func = this;
return function(...arr2) {

//func.apply(context, [...arr1, ...arr2]);

func.call(context, ...arr1, ...arr2);
}
}

var value = "JS";
var foo = {
value: "Node"
};

function bar(...list) {
console.log(this.value);
console.log(list)
}

bar.bind1(foo, 1, 2)(3, 4);
bar.bind1(null, 5, 6)(7, 8);
bar.bind1(null)(5, 6, 7, 8);
bar.bind1(undefined)(5, 6, 7, 8);

bind2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Function.prototype.bind2 = function(context, ...arr1) {
context = context || window;
context.fn = this;
return function(...arr2) {
return context.fn(...arr1, ...arr2);
};
}

var value = "JS";
var foo = {
value: "Node"
};

function bar(...list) {
console.log(this.value);
console.log(list)
}

bar.bind2(foo, 1, 2)(3, 4);
bar.bind2(null, 5, 6)(7, 8);
bar.bind2(null)(5, 6, 7, 8);
bar.bind2(undefined)(5, 6, 7, 8);

有什么问题?

前面我们用的 eval 方式可以用 ES6 的解决还存在的一些问题,有没有注意到,这段代码是有问题的。

1
context.fn = this;

假如对象在被 call 调用前,已经有 fn 属性怎么办?

ES6 中提供了一种新的基本数据类型,Symbol,表示独一无二的值,另外,Symbol 作为属性的时候,不能使用点运算符。所以再加上 ES 的 rest 剩余参数替代 arguments 遍历的工作就有:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Function.prototype.newCall = function (context,...params) {
if (typeof context === 'object') {
context = context || window
} else {
context = Object.create(null);
}
let fn = Symbol();
context[fn] = thisvar result = context[fn](...params);

delete context.fn;
return result;
}

var person = {
name: "jayChou"
};

functionsay(age, sex) {
console.log(`name: ${this.name},age: ${age}, sex: ${sex}`);
return age + sex;
}

var check = say.newCall(person, 18, '男');
console.log(check); // 18男

apply 和 call 的实现原理,基本类似,区别在于 apply 的参数是以数组的形式传入

1
2
3
4
5
6
7
8
9
10
11
12
Function.prototype.newApply = function(context, parameter) {
if (typeof context === 'object') {
context = context || window
} else {
context = Object.create(null)
}
let fn = Symbol()
context[fn] = this;
var result = context[fn](...parameter);
delete context[fn];
return result;
}