# bind、call、apply的实现
这3个方法,其实都在Function
的原型链上。
# bind实现
很简单,第一个参数是作用域,后面如果还有的话,就是函数前面的参数。其实它应该也算是柯里化的一种应用。
这是ES5
的写法:
Function.prototype.bind = function(scope){
var fn = this;
var args = [].slice.call(arguments);
args.shift();
return function(){
return fn.apply(scope, args.concat([].slice.call(arguments)));
};
};
这是ES6
的写法:
Function.prototype.bind = function(scope, ...args){
var fn = this;
return function(...args2){
return fn.apply(scope, args.concat(args2));
};
};
测试:
function Foo(age) {
console.log(this.name);
console.log(age);
}
var nf = Foo.bind({name: 'abc'});
nf(18);
var nf2 = Foo.bind({name: 'abc'}, 22);
nf2();
# apply实现
这是用ES6
和call
的实现:
Function.prototype.apply = function (scope, args) {
return this.call(scope, ...args);
};
如果能使用bind
的话,也很简单:
Function.prototype.apply = function(scope, args){
var fn = this;
return fn.bind(scope)(...args);
};
在ES5
里,如果不能用call
,则要麻烦多了。思路是使用eval
函数,把函数本身赋给scope
,再执行,最终删除新加的属性。
Function.prototype.apply = function (scope, arr) {
scope = scope || window;
var args = [];
for (var i = 0; i < arr.length; i++) {
var arg = arr[i];
if (typeof arg === 'string') {
args.push('"' + arg + '"');
} else if (typeof arg === 'object') {
scope['arg' + i] = arg;
args.push('scope["arg' + i + '"]');
} else {
args.push(arg);
}
}
scope.fn = this;
var res = eval('scope.fn(' + args.join(',') + ')');
delete scope.fn;
for (var key in scope) {
if (key.startsWith('arg')) {
delete scope[key];
}
}
return res;
};
测试:
function Foo(age, sex, options) {
console.log(this.name);
console.log(age);
console.log(sex);
console.log(options)
return 'abcdefg'
}
var res = Foo.apply({ name: 'def' }, [45, 'man', { email: 'ss' }]);
console.log(res);
# call实现
实现了apply
,call
就容易了。
如果能使用bind
的话,call
很简单:
Function.prototype.call = function(scope, ...args){
var fn = this;
return fn.bind(scope)(...args);
};
使用apply
的方式:
Function.prototype.call = function(scope){
var fn = this;
var args = [].slice.apply(arguments);
args.shift();
return fn.apply(scope, args);
};
如果用ES6
,可以这样:
Function.prototype.call = function(scope, ...args){
scope.fn = this;
var res = scope.fn(...args);
delete scope.fn;
return res;
};
在ES5
里,如果不能用apply
,大致与上面实现差不多,这里就赘述了。
测试:
var res = Foo.call({ name: 'def' }, 45, 'man', { email: 'ss' });
console.log(res);