js自带call的用法:fn.call(obj,param1,param2,...);
将fn中的this指向obj,并已逗号隔开形式传入参数
var myobj={name:'peachestao'};
function fn(param){
console.log(this.name);
console.log(param);
}
fn.call(myobj,'hello');
输出结果:peachestao
hello
其实我们我们自定义函数来模拟实现js自带的call方法,有两种方法实现
无论通过何种方法模拟,都需要实现以下几点:
1、替换原函数中的this为目标对象
2、将参数传递给原函数
3、兼容null作为目标对象,自动转换为window对象
方法一:使用eval 将原始函数转换为字符串,再将其中的this替换为目标对象
Function.prototype.myCall=function(context){
var str;
var reg=new RegExp('this','g');
str=this.toString().replace(reg,'context');//A、replace('this','context')只会替换第一处,正则表达式会替换所有
var newArguments = [];//B、实现参数传入,将当前函数中的参数除去第一个将剩余的传入目标函数
for(var i = 1, len = arguments.length; i < len; i++) {
newArguments.push('arguments[' + i + ']');
}
//var newArguments=[].slice.call(arguments,1);//不能用这种方法,否则eval执行时会提示参数变量没有定义,需要跟B处一样,将每个参数名转换为字符串形式,eval执行时动态计算每一个参数值
eval('('+str+')('+newArguments+')'); //C、以js表达式的形式执行字符串
}
调用:fn.myCall(myobj,'hello');
输出结果:peachestao
hello
关于代码中C处实现的原理我这里解释以下:
eval函数:将字符串以js表达式的形式执行,如下:
function TestEvalFn(){
console.log('this is a test method');
}
eval('TestEvalFn()');
函数TestEvalFn将被执行,输出:
this is a test method
再次回到代码C处:我们可以将eval中的参数打印出来,大家可以可得更加直白些:
"(function fn(param){
console.log(context.name);
console.log(param);
})(arguments[1])"
可以看出,定义了一个立即执行的函数,函数中的this已变更为context,也传入了参数,字符串形式的context和arguments执行时会动态绑定变量
方法二、将fn添加到myobj的属性,然后myobj.fn()的形式实现
将上面的myobj改造一下:
var myobj={
name:'peachestao',
fn:function(param)
{
console.log(this.name);
console.log(param);
}
};
myobj.fn('this is a param');
这种形式fn函数中的this已经替换为myobj了。我们可以参照这种模式,实现的代码如下:
Function.prototype.myCall2=function(context){
context=context||window; //兼容传入null的情况
context.fn=this;//将原函数添加到目标对象属性上
var newArguments = [];
for(var i = 1, len = arguments.length; i < len; i++) {
newArguments.push('arguments[' + i + ']');
}
eval('context.fn('+newArguments+')'); //执行属性方法
delete context.fn; //去除属性
}
调用;
fn.myCall2(myobj,'hello');
输出结果:peachestao
hello
类似于call函数,apply函数也可以自己模拟实现。
目的不是重复造轮子,而是为了深入了解原生函数实现的原理,写代码时就会心中有数
如果觉得这篇文章对你有帮助,请点个赞 *^_^*