call,apply,bind是做什么的
call、apply、bind是函数的一种特殊形式,它们的作用是调用函数,并且把函数的参数传递给它。call、apply、bind的实现原理是一样的,都是通过构造函数来实现的。这三种形式的第一个参数,都是函数的调用者,即谁调用了这个函数,从而决定this指向谁。
下面通过一个例子来说明call、apply、bind的用法。
function foo(a, b, c) {
console.log(this, a, b, c);
}
foo(1, 2, 3); // window 1 2 3
foo.call({ name: 'bar' });, 1, 2, 3); // { name: 'bar' }, 1, 2, 3;
foo.call(null, 1, 2, 3); // null, 1, 2, 3
foo.apply({ name: 'bar' }, [1, 2, 3]); // { name: 'bar' }, 1, 2, 3;
foo.apply(null, [1, 2, 3]); // null, 1, 2, 3
foo.bind({ name: 'bar' })(1, 2, 3); // { name: 'bar' }, 1, 2, 3;
foo.bind(null)(1, 2, 3); // null, 1, 2, 3
call、apply、bind的实现原理
通过上面的例子我们大概可以了解call、apply、bind的实现方法,即this指向第一个函数,剩余的参数绑定到函数的arguments上。知道了这些我们就可以很方便地手写call、apply、bind的实现了。
首先我们先来实现call的方法
Function.prototype.mycall = function(thisArg, ...args) {
// 在这里可以去执行调用的那个函数(foo)
// 问题: 得可以获取到是哪一个函数执行了mycall
// 1.获取需要被执行的函数
var fn = this
// 2.对thisArg转成对象类型(防止它传入的是非对象类型)
thisArg = (thisArg !== null && thisArg !== undefined) ? Object(thisArg): window
// 3.调用需要被执行的函数
thisArg.fn = fn
var result = thisArg.fn(...args)
delete thisArg.fn
// 4.将最终的结果返回出去
return result
}
简单的验证一下
function testmycall(a, b) {
console.log(this, a + b);
}
testmycall(1, 2); // window 3
testmycall.mycall('mycall', 1 , 2); // mycall 3
testmycall.call('call', 1 , 2); // call 3
弄懂了call的实现方法,我们就可以很方便的手写apply、bind的实现了。其实主要的区别就是call的其他参数就是函数的参数,而apply的第二个参数是函数的参数转成数组,bind的回调函数就是函数的参数。
Function.prototype.myapply = function(thisArg, args) {
var fn = this;
thisArg = (thisArg !== undefined && thisArg !== null) ? Object(thisArg) :window;
thisArg.fn = fn;
args = args || [];
var res = thisArg.fn(...args);
delete thisArg.fn;
return res;
}
testmycall.myapply('myapply', [111, 222]); // myapply 333
testmycall.apply('apply', [111, 222]); // apply 333
Function.prototype.mybind = function(thisArg, ...args) {
var fn = this;
thisArg = (thisArg !== undefined && thisArg !== null) ? Object(thisArg) :window;
return function proxyFn(...newArgs) {
thisArg.fn = fn;
var res = thisArg.fn(...args, ...newArgs);
delete thisArg.fn;
}
}
var testbind = testmycall.bind('bind', 123, 456);
testbind(111, 222); // bind 333
var testmybind = testmycall.mybind('mybind', 111);
testmybind(222); // mybind 333