JavaScript call 与 apply 实现

143 阅读2分钟

call

call 是在指定一个this 值与若干个参数值的情况下去调用一个函数(冴羽大大说的)

call 方法实现

    思路: 首先我们想调用一个函数func 在this指向一个对象的情况下
    //需要被绑定的对象
        var obj = {
            name: 'van'
        }
    那么我们可以尝试将func绑定在obj上,然后去调用它,最后无情删除
        obj.func = func; //绑定
        obj.func(); //调用
        delete obj.func; //删除


有了思路之后第一版call乞丐版就出来了

    Function.prototype.call2 = function(context){
        context.fn = this; //第一步绑定
        context.fn(); //第二步 调用
        delete context.fn; //第三步删除
    }
    
    var obj = {
        value: 'van'
    };
    
    function test () {
        console.log(this.value);
    }
    
    test.call2(obj); // van


到了这一步,我们已经实现了基本的使用call将this指向obj对象
但是我们知道,call是可以传参的,那么我们再来试一下传参这个功能实现

    Function.prototype.call2 = function(context){
        context.fn = this; //第一步绑定
        var args = [];
        for(var i=1;i < arguments.length;i++){
            args.push('arguments[' + i + ']');
        }
        //此时args中的内容为 ['arguments[1]','arguments[2]']
        //但是我们调用fn时怎么把args传进去呢,我们想到es6的eval函数了
        //此时在eval中,args 自动调用 args.toString()方法,eval的效果如 jawil所说,最终的效果相当于
        //context.fn(arguments[1],arguments[2],.....)
        eval('context.fn(' + args + ')') //第二步 调用
        delete context.fn; //第三步删除
    }
    
    var obj = {
        value: 'van'
    };
    
    function test (name,age) {
        console.log(name);
        console.log(age);
        console.log(this.value);
    }
    
    test.call2(obj,'van','20'); // van


到了现在我们已经接近成功了,最后我们想到了call 好像是可以传一个null值的,并且 还可以有返回值???
那我们再来修改一下代码

    Function.prototype.call2 = function(context){
        //判断context是否为null,如果为null则将他指向window
        context = context || window;
        context.fn = this; //第一步绑定
        var args = [];
        for(var i=1;i < arguments.length;i++){
            args.push('arguments[' + i + ']');
        }
        //此时args中的内容为 ['arguments[1]','arguments[2]']
        //但是我们调用fn时怎么把args传进去呢,我们想到es6的eval函数了
        //此时在eval中,args 自动调用 args.toString()方法,eval的效果如 jawil所说,最终的效果相当于
        //context.fn(arguments[1],arguments[2],.....)
        var value = eval('context.fn(' + args + ')') //第二步 调用
        delete context.fn; //第三步删除
        return value;
    }
    
    var obj = {
        value: 'van'
    };
    
    function test (name,age) {
        console.log(name);
        console.log(age);
        console.log(this.value);
    }
    
    test.call2(obj,'van','20'); // van

此时我们终于完成了call的所有内容,淦,终于写完了

apply

apply 与call十分相似,他们的区别就是,call后面跟一大堆参数,而apply则只接收一个数组
直接上最终代码
    Function.prototype.apply2 = function(context,arr){
        context = context || window;
        context.fn = this;
        var result;
        if(!arr){
            //没有参数的情况下
            result = context.fn();
        }
        else{
            var args = [];
            for(var i = 1;i<arr.length;i++){
                args.push('arr[' + i + ']');
            }
            result = eval('context.fn(' + args + ')');
        }
        delete context.fn;
        return result;
    }