前端面试题之JS手写call

前端面试题之JS 手写call

回顾一下官方call的用法:

var value = 'v in window';
function func() {
    console.log(this.value);
}
var obj = {
    value: 'v in obj'
};
func(); //v in window
func.call(obj); // v in obj

call函数在这里修改了func运行时this的指向,相当于console.log(obj.value),所以输出的就是obj内部的value了。
根据这个原理,自己分装一个call:

var value = 'v in window';
function func() {
    console.log(this.value);
}
var obj = {
    value: 'v in obj'
};
Function.prototype.call2 = function (obj) {
    obj.fn = this;
    obj.fn();
    delete obj.fn; //记住最后要删除掉临时添加的方法,否则obj就无缘无故多了个fn
};
func(); //v in window
func.call2(obj); // v in obj

call2方法里的this是指func这个函数,因为是func调用的,obj.fn = this给传进来的obj对象克隆了一个func这个方法,让obj再调用这个方法,此时是obj调用这个方法,就相当于console.log(obj.value),达到了call的效果。(记得最后要delete掉obj临时添加的fn)

官方call是可以传参数的,现在处理一下传参的问题:

var value = 'v in window';
function func() {
	arguments = [].splice.call(arguments, 0); //这一句是把参数转成数组,因为arguments不是数组,输出的时候容易观察
    console.log(arguments);
    console.log(this.value);
}
var obj = {
    value: 'v in obj'
};
Function.prototype.call2 = function (obj) {
    obj.fn = this;
    obj.fn();
    delete obj.fn; //记住最后要删除掉临时添加的方法,否则obj就无缘无故多了个fn
};
func(1,2,3); 
//[1,2,3]
//v in window
func.call2(obj);
//[]
 // v in obj

首先现在call方法里接收参数:

var args = []; //存放参数的数组
for(var i = 1; i < arguments.length; i++) { 
    args.push('arguments[' + i + ']'); //这样存进去是有原因的,后面讲
}

做一个循环把参数存进args数组,因为auguments的第一个参数是obj,所以直接从i=1开始遍历,存放好的数组结构是这样的:

console.log(args); //["arguments[1]", "arguments[2]", "arguments[3]"]

这是因为我们平时传参的习惯是这样的:

func(1,2,3); 

那么我们在call2方法里要做到的效果是这样的:
正确姿势:

obj.fn(arguments[1], arguments[2], arguments[3]); //传参代码正确姿势

错误姿势1:

obj.fn(args.join(',')); //obj.fn('arguments[1], arguments[2], arguments[3]') 

对比正确姿势后你会发现,其实你传的只是一个字符串:‘arguments[1], arguments[2], arguments[3]’
错误姿势2:

obj.fn(args); //相当于obj.fn([1,2,3]),和正确的obj(1,2,3)对比一下就知道错哪了

所以这里要用到eval函数,eval函数可以把string字符串里的内容运行,例如:

扫描二维码关注公众号,回复: 3713387 查看本文章
var str = 'console.log(1)';
eval(str); //输出1
console.log(1); //输出1

那么把obj.fn()和arguments[1], arguments[2], arguments[3]拼接一下就可以变成正确姿势了。
由于+号拼接自动会将arr类型的进行toString(),‘obj.fn(’+args+’)‘里面的args数组[“arguments[1]”, “arguments[2]”, “arguments[3]”]会自动变成’arguments[1],arguments[2],arguments[3]’,最后就变成了:

'obj.fn(arguments[1], arguments[2], arguments[3])' //一个字符串

最后用eval函数运行一下这个string字符串,就得到了这个效果:

obj.fn(arguments[1], arguments[2], arguments[3])

完整代码:

var value = 'v in window';
function func() {
    arguments = [].splice.call(arguments, 0);
    console.log(arguments);
    console.log(this.value);
}
var obj = {
    value: 'v in obj'
};
Function.prototype.call2 = function (obj) {
    var args = [];
    for(var i = 1; i < arguments.length; i++) {
        args.push('arguments[' + i + ']');
    }
    obj.fn = this;
    eval('obj.fn('+args+')');
    delete obj.fn; 
};
func(1,2,3); 
//[1,2,3]
//v in window
func.call2(obj,1,2,3); 
//[1,2,3]
// v in obj

当参数为null时,官方call会返回将this变成window:

    var value = 'v in window';
    function func() {
        console.log(this.value);
    }
    var obj = {
        value: 'v in obj'
    };
    func(); //v in window
    func.call(null); //v in window

需要修改一下call2函数,当obj为null时obj=window:

    Function.prototype.call2 = function (obj) {
    	var obj = obj || window;
        var args = [];
        for(var i = 1; i < arguments.length; i++) {
            args.push('arguments[' + i + ']');
        }
        obj.fn = this;
        eval('obj.fn('+args+')');
        delete obj.fn;
    };

最后处理一下call2返回值的问题,官方call是有返回值的:

    var value = 'v in window';
    function func() {
        return this.value;
    }
    var obj = {
        value: 'v in obj'
    };
    Function.prototype.call2 = function (obj) {
        var obj = obj || window;
        var args = [];
        for(var i = 1; i < arguments.length; i++) {
            args.push('arguments[' + i + ']');
        }
        obj.fn = this;
        eval('obj.fn('+args+')');
        delete obj.fn;
    };
    console.log(func.call(obj)); //v in obj
    console.log(func.call2(obj)); //undifined

这是因为call2函数中并没有return,这里定义一个result用来接收返回值再返回去。
最终代码:

Function.prototype.call2 = function (obj) {
    var obj = obj || window;
    var args = [];
    for(var i = 1; i < arguments.length; i++) {
        args.push('arguments[' + i + ']');
    }
    obj.fn = this;
    var result = eval('obj.fn('+args+')');
    delete obj.fn;
    return result;
};

猜你喜欢

转载自blog.csdn.net/qq593249106/article/details/83115935
今日推荐