call函数实现
首先先知道call函数实现了什么功能
- 函数在调用call方法时会执行当前函数
- call方法的第一个参数可以显示绑定this
- call方法的剩余参数可以作为调用函数的参数传入到调用函数的执行体中进行执行,并且返回结果
1.在实现自定义的call函数之前,考虑到自定义call是需要被所有函数调用的,那么这里的自定义call必须放到函数的原型链上
Function.prototype.yqcall = function () {
};
function foo() {
}
foo.yqcall()
2.现在来实现自定义call方法的第一个功能
Function.prototype.yqcall = function () {
//1.获取需要执行的函数
var fn = this; //隐式绑定,利用this拿到当前调用yqcall的函数
fn() //执行调用的函数
};
function foo() {
console.log(1111);
}
foo.yqcall()
如果直接这样执行fn,是可以看到控制台打印结果的,但是在考虑第二个功能的情况下,需要将传入的第一个参数(thisArgs)作为绑定的this,直接执行是不合适的
2.实现自定义call第二个功能
Function.prototype.yqcall = function (thisArgs) {
//1.获取需要执行的函数
var fn = this; //隐式绑定,利用this拿到当前调用yqcall的函数
//2.执行当前调用yqcall的函数
//fn();
thisArgs.fn = fn; //将fn函数放到thisArgs对象中
thisArgs.fn(); //通过隐式绑定将this绑定到yqcall的第一个参数上
delete thisArgs.fn; //执行完成之后将fn函数进行删除
};
function foo() {
console.log(1111,this);
}
foo.yqcall({name:"jack"})
输出结果
result.png
从输出的结果看对象类型中不仅仅有传入的对象键值对还有一个fn函数,这里主要是因为js的运行机制导致的,如果用c语言来实现就不会有这种情况了,如果你在这一步上有更好的想法,欢迎下面留言
但是这样还是不太完善,在我这里传入的参数是一个对象类型,这里就需要考虑传入的是其他的数据类型该这么转化为对象类型
thisArgs = Object(thisArgs);
在执行第二步的操作前可以用Object将参数转化为对象类型,这里考虑一些边界状况,如果传入的null和undefined,在call函数中是直接指向的是window,所以这里需要进行进一步的处理
thisArgs = thisArgs ? Object(thisArgs):window
但是如果传入的是0,这里的指向会有一点问题
foo.yqcall('adc');
foo.yqcall(123);
foo.yqcall(null);
foo.yqcall(undefined);
foo.yqcall(0);
结果
result
再来做一步处理
thisArgs =(thisArgs !== null && thisArgs !== undefined) ? Object(thisArgs) : window;
这里就不在考虑其他边界问题了,接下来实现第三个功能
3.实现自定义call的第三个功能
第三个功主要是将传入的剩余参数做一个转换然后返回出去,第三个功能用到了es6的展开运算符,将剩余参数传入,然后将参数展开传入到执行的fn函数上,并且将结果进行返回
//实现call方法
//1.每一个函数都需要使用call方法,这里需要将call方法放到函数的原型链上
//2.调用call方法后需要让函数执行
Function.prototype.yqcall = function (thisArgs, ...args) { //...args剩余参数传入
//1.获取需要执行的函数
var fn = this; //隐式绑定,利用this拿到当前调用yqcall的函数
//2.转化第一个参数
//由于无法确定传入的参数是否是对象类型的,这里需要统一将thisArgs转换为对象类型
//还需要考虑到如果传入的参数是null和undefined或者是0的情况下
// thisArgs = thisArgs ? Object(thisArgs):window
thisArgs =
thisArgs !== null && thisArgs !== undefined ? Object(thisArgs) : window;
//3.执行当前调用yqcall的函数
//fn();
thisArgs.fn = fn; //将fn函数放到thisArgs对象中
var result = thisArgs.fn(...args); //通过隐式绑定将this绑定到yqcall的第一个参数上,并且将参数展开传入到函数中
delete thisArgs.fn; //执行完成之后将fn函数进行删除
//4.返回结果
return result;
};
function foo(num1, num2) {
console.log(1111,this);
return num1 + num2;
}
var res = foo.yqcall(0, 20, 30);
console.log(res);
最终结果
result
apply实现
apply的功能和call的功能是非常相似的,只有传入的第二个参数参数有区别,call的传入的剩余参数,apply传入的是数组
Function.prototype.yqapply = function (thisArgs, arrArgs) {
//1.获取到调用的函数
var fn = this;
//2.处理thisArgs的边界判断
thisArgs =
thisArgs !== null && thisArgs !== undefined ? Object(thisArgs) : window;
//3.执行当前调用的函数
thisArgs.fn = fn;
arrArgs = arrArgs || []; //如果第二个参数没有传入数组,给一个空的数组
var result = thisArgs.fn(...arrArgs);
delete thisArgs.fn;
//4.返回结果
return result;
};
function foo(num1, num2) {
console.log(111, this, num1, num2);
return num1 + num2;
}
var res = foo.yqapply('adc', [20, 30]);
console.log(res);
结果
result
bind的实现
bind和其他两个最大的不同时函数调用完之后返回一个函数,返回的函数在执行时可以传入新的参数
function foo(num1, num2, num3, num4) {
console.log(num1, num2, num3, num4, this);
return num1 + num2 + num3 + num4;
}
var bar = foo.bind('adc', 20, 30);
var result = bar(40, 50);
console.log(result);
image.png
所以实现bind方法在上面的基础上需要实现的是返回一个函数,返回的函数的参数要加在一起进行合并
Function.prototype.yqbind = function (thisArgs, ...arrArry) {
//1.获取到调用的函数
var fn = this;
//2.thisArgs边界判断
thisArgs =
thisArgs !== null && thisArgs !== undefined ? Object(thisArgs) : window;
function ProxyFn(...args) { //使用函数代理
thisArgs.fn = fn;
var FinallyArgs = [...arrArry, ...args]; //将两次的传递的参数进行合并
var result = thisArgs.fn(...FinallyArgs);
delete thisArgs.fn;
return result;
}
return ProxyFn; //将代理函数返回
};
执行的结果是一样的
image.png
还有其他的边界判断实现可以在下面留言