待会实现的自定义call函数,会用下面简单的函数调用系统的call函数进行对比
function foo() {
console.log('foo函数执行', this);
}
function sum(num1, num2) {
console.log('sum函数执行', this, num1, num2);
return num1 + num2;
}
一、给所有的函数添加一个 dycall 方法
系统的call 函数是可以直接被函数调用的,因此,我们自定义的 dycall 方法需要挂载到函数对象的原型上
Function.prototype.dycall = function() {
console.log('dycall方法执行')
}
foo.dycall();
二、获取被调用的函数
在自定义的 dycall 方法中,我们要获取到调用 dycall 方法的函数对象,然后在 dycall 方法中,进行调用
foo.dycall() 相当于 this 的隐式绑定,直接通过this获取被调用的函数对象
Function.prototype.dycall = function () {
// 1.获取需要被执行的函数
var fn = this;
fn();
}
三、dycall 方法绑定 传入的内容
传入空对象对比调用
foo.call({}); // foo函数执行 {}
foo.dycall({}); // foo函数执行 Window {...}
对比系统call调用,自定义的 dycall 方法this指向的是 window,因此绑定this,将获取的 fn 函数添加到传入的对象上,相当于this的隐式绑定,this就指向了传入的空对象
// 给所有函数添加一个 dycall 的方法
Function.prototype.dycall = function (thisArgs) {
// 1.获取需要被执行的函数
var fn = this;
thisArgs.fn = fn;
thisArgs.fn();
};
foo.dycall({}); // foo函数执行 {fn: ƒ}
在此调用后,输出的this就是我们传入的空对象,但是对比系统call,我们多了绑定的 fn 函数,因此需要在dycall调用fn后,删除多余的属性。输出的时候会看到,但,无伤大雅
// 给所有函数添加一个 dycall 的方法
Function.prototype.dycall = function (thisArgs) {
// 1.获取需要被执行的函数
var fn = this;
thisArgs.fn = fn;
// 2.调用需要被改变this的函数
thisArgs.fn();
delete thisArgs.fn;
};
四、对比其他几种case,完善边界情况
- 传入对象
- 传入简单数据类型
foo.call(123); // foo函数执行 Number {123}
foo.call("abc"); // foo函数执行 String {'abc'}
我们自定义的 dycall 绑定的this是对象,因此可以进行 fn 添加调用,如果传入的是简单数据类型,需要我们转换为对应类型的对象,需要使用到 Objecet(),方法
// 给所有函数添加一个 dycall 的方法
Function.prototype.dycall = function (thisArgs) {
// 1.获取需要被执行的函数
var fn = this;
// 2.将thisArgs转成对象类型(防止传入的是非对象类型),非对象类型不能添加属性
thisArgs = Object(thisArgs)
thisArgs.fn = fn;
// 3.调用需要被改变this的函数
thisArgs.fn();
delete thisArgs.fn;
};
foo.dycall(123); // foo函数执行 Number {123, fn: ƒ}
foo.dycall("abc"); // foo函数执行 String {'abc', fn: ƒ}
3.传入 null 、undefined 或 空参
foo调用系统call,this指向 Window
foo.call(); // foo函数执行 Window {...}
foo.call(undefined); // foo函数执行 Window {...}
foo.call(null); // foo函数执行 Window {...}
// 给所有函数添加一个 dycall 的方法
Function.prototype.dycall = function (thisArgs) {
// 1.获取需要被执行的函数
var fn = this;
// 2.将thisArgs转成对象类型(防止传入的是非对象类型),非对象类型不能添加属性
thisArgs = thisArgs ? Object(thisArgs) : window;
thisArgs.fn = fn;
// 3.调用需要被改变this的函数
thisArgs.fn();
delete thisArgs.fn;
};
五、参数赋值以及返回值(最终版)
sum.call({}, 20, 30); // sum函数执行 {} 20 30
tips:用剩余运算符 ...args 接收多个参数,调用的时候使用 扩展运算符 ...args
// 给所有函数添加一个 dycall 的方法
Function.prototype.dycall = function (thisArgs, ...args) {
// 1.获取需要被执行的函数
var fn = this;
// 2.将thisArgs转成对象类型(防止传入的是非对象类型),非对象类型不能添加属性
thisArgs = thisArgs ? Object(thisArgs) : window;
thisArgs.fn = fn;
// 3.调用需要被改变this的函数
var result = thisArgs.fn(...args);
delete thisArgs.fn;
// 4.将调用后的结果返回
return result;
};