Javascript模拟实现call函数

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函数也可以自己模拟实现。

目的不是重复造轮子,而是为了深入了解原生函数实现的原理,写代码时就会心中有数

如果觉得这篇文章对你有帮助,请点个赞 *^_^* 

猜你喜欢

转载自blog.csdn.net/taoerchun/article/details/82080645