在node.js中手工实现apply 和 bind方法

这两天看sf,偶然看到一些讨论js中原生实现apply和bind方法等帖子,觉得挺好玩儿,自己也照着思路试了一下,发现果然还是要自己写代码跑跑看,本来觉得思路已经有了,自己在实现的时候还是遇到了一些思路卡顿的状况,所以就记录一下啦。先上一个MDN上对Function.prototype.applyFunction.prototype.bind的解释:

apply(thisArg[, argsArray]) 方法在指定 this 值和参数(参数以数组或类数组对象的形式存在)的情况下调用某个函数.
bind(thisArg[, arg1[, arg2[, …]]])方法创建一个新的函数, 当被调用时,它的this关键字被设置为提供的值 ,在调用新函数时,提供任何一个给定的参数序列

原生apply用法例子

apply_test.js源代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
var obj_a = {
name:'obj_a',
show:function(a,b) {
console.log('name:',this.name);
console.log('param a:',a,'param b:',b);
}
};
obj_a.show(22,33);
console.log('apply start:');
var obj_b = {
name:'obj_b'
};
obj_a.show.apply(obj_b,['aaa','dddd']);

输出结果:

1
2
3
4
5
name: obj_a
param a: 22 param b: 33
apply start:
name: obj_b
param a: aaa param b: dddd

分析:
被apply的show方法会立即执行,但是执行的时候,show函数中的this对应的上下文不再是obj_a,而是obj_b;

原生bind用法例子

bind_test.js源代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var obj_a = {
name:'obj_a',
show:function(a,b) {
console.log('name:',this.name);
console.log('param a:',a,'param b:',b);
}
};
obj_a.show(22,33);
var obj_b = {
name:'obj_b'
};

//此处返回函数dd是一个 obj_a.show同样功能的函数,但是函数的this被指定为obj_b
console.log('start bind test here:');
let dd = obj_a.show.bind(obj_b);
dd('22bind','33bind');

输出结果:

1
2
3
4
5
name: obj_a
param a: 22 param b: 33
start bind test here:
name: obj_b
param a: 22bind param b: 33bind

分析:此处bind返回值是一个函数,该函数具有和obj_a.show()具有同样的功能,
但是新函数中的this对应的上下文被指定为obj_a,即返回函数在创建时便被指定了this。
这里我们也可以看出bind和apply函数的区别,一个是函数立即执行,一个是返回一个新函数。

手工实现apply

思路

func_a.apply(obj_new,params_arr)函数是要在this指向obj_new的上下文中中执行目标函数func_a,同时传入参数数组params_arr,
所以我们只要在obj_new下建立一个新方法temp_func,并赋值为func_a,然后使用传入的参数params_arr执行tem_func即可。

own_apply.js源代码:

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
//apply函数的模拟实现
Function.prototype.own_apply = function() {
console.log(arguments);
//let fn = this;
let context = arguments[0];
let params = arguments[1];
var temp_func = Symbol();
context[temp_func]= this;
eval('context[temp_func](' + params.join(',') + ')');
delete context.dddd

};
var obj_a = {
name:'obj_a',
show:function(a,b) {
console.log('name:',this.name);
console.log('param a:',a,'param b:',b);
}
};

var obj_b = {
name: "obj_b"
}

obj_a.show.own_apply(obj_b,['2','3'])

输出结果:

1
2
3
{ '0': { name: 'obj_b' }, '1': [ '2', '3' ] }
name: obj_b
param a: 2 param b: 3

手工实现bind

own_bind实现思路

与own_apply不同的是,own_bind不是立即执行目标函数,而是生成一个指定了this上下文的新函数,所以与手工实现apply类似,只是不在own_bind中立即执行,
而是返回一个匿名函数,把执行放在该匿名函数中

own_bind.js源代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//bind函数的模拟实现
Function.prototype.own_bind = function() {
let context = arguments[0];
let params = arguments[1];
var temp_func = Symbol();
context[temp_func]= this;
return function() {
let args = Array.prototype.slice.call(arguments);
return eval('context[temp_func](' + args.join(',') + ')');
}
};
var obj_a = {
name:'obj_a',
show:function(a,b) {
console.log('name:',this.name);
console.log('param a:',a,'param b:',b);
}
};

var obj_b = {
name: "obj_b"
}
// own_bind 测试
obj_a.show.own_bind(obj_b)('2','3')

结果输出

1
2
name: obj_b
param a: 2 param b: 3