JS模拟call,apply,bind

一、call

1.开始

我们先看下原生 call 做了什么事

1
2
3
4
5
6
7
8
9
10
11
12
13
function bar(name,age){
this.hobby = 'shopping';
console.log(this.value);
console.log(this.hobby);
console.log(name);
console.log(age);
}
bar.prototype.friend = 'kevin';
var obj = {
hobby: 'gamming'
}

var hah = bar.call(obj,'jack',18);

输出是:
call.png
这说明 call 只是修改了 this 的指向,没有返回对象。

2.模拟实现

所以我们只要尝试着去修改 this 的指向并执行就行了,不需要有返回值:

1
2
3
4
5
6
7
8
9
10
11
Function.prototype.calls = function(){
var dir = arguments[0];
var args = [];
for(var i = 1; i < arguments.length; i++){
args.push(arguments[i])
};
dir.pau = this;
// 这里用到了ES6的新特性,不好
dir.pau(...args);
delete dir.pau;
}

eval 实现

1
2
eval('dir.pau(' + args +')');
delete dir.pau;

二、apply

apply 的功能和 call 一样只不过是参数是由一个数组传递

1
2
3
4
5
6
7
8
Function.prototype.applys = function(){
var dir = arguments[0];
var args = [];
dir.pau = this;
// 这里用到了ES6的新特性,不好
dir.pau(...arguments[1]);
delete dir.pau;
}

三、bind

1.开始

假设我们有一个函数,一个对象

1
2
3
4
5
6
7
8
9
10
var foo = {
value:1
}
function bar(name,age){
this.hobby = 'shopping';
console.log(this.value);
console.log(name);
console.log(age);
}
bar.prototype.friend = 'kevin';

我们试一试用原生的 bind 可以输出什么:

1
2
var bindFoo = bar.bind(foo, 'daisy');
var obj = new bindFoo('18');

bind.png
可以看到指定原型链,指定了 this,bind 的同时可以传参数。

① 指定 this,bind 的同时穿参数:
我们知道 bind 返回一个函数并绑定了 this,这个模拟起来比较简单

1
2
3
4
5
6
7
8
Function.prototype.binds = function(dir){
var self = this;
var _args = [].slice.call(arguments,1);
return function(){
var args = _args.cancat([].slice.call(arguments));
self.apply(dir,args);
}
}

② 指定原型链的指向,即 prototype 的指向:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Function.prototype.binds = function(dir){
var self = this;
var _args = [].slice.call(arguments,1);

var inn = function(){
var args = _args.concat([].slice.call(arguments));
self.apply(dir,args);
}

inn.prototype = new dir();
inn.prototype.constructor = inn;

return inn;
}

到这里,如果不去 new 出一个实例的话,都可以了,如果需要 new 出一个实例来,我们就需要重新指定下 this,因为如果使用 new 操作符会把 this 指向新创建的对象,但我们还是需要他指向原本指向的对象,所以,我们要判断当前调用对象是否为原对象的一个实例属性,修改后的代码为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Function.prototype.binds = function(dir){
var self = this;
var _args = [].slice.call(arguments,1);

var inn = function(){
var args = _args.concat([].slice.call(arguments));
self.apply(this instanceof self ? this : dir,args);
}

inn.prototype = new self();
inn.prototype.constructor = inn;

return inn;
}

到这里,我们应该已经完成了对 bind 的模拟,输出看下结果:
bind.png
多出的几个 undefined 是在 new 时产生。