前端面试必问:能否用js模拟实现call函数

前言

要想实现它,就必须先了解它是做什么的,主要功能是什么

MDN:Function.prototype.call() 文档

一句话介绍call:call() 方法在使用一个指定的 this 值和若干个指定的参数值的前提下调用某个函数或方法。

举个例子:

var foo = {
    value: 1
};

function bar() {
    console.log(this.value);
}

bar.call(foo); // 1

从以上代码发现:

1. call改变了this的指向,指向到了foo

2. bar函数执行了

模拟实现第一步

我们把bar方法作为foo对象的属性来调用,这样this不就指向了foo吗? 如下:

var foo = {
    value: 1,
    bar: function() {
        console.log(this.value)
    }
};

foo.bar(); // 1

但是这样我们就改变了foo对象的属性, 所以我们最后还是需要delete来删除它。

所以我们的步骤可以分为:

1. 将函数设为对象的属性

2. 执行该函数

3. 删除该函数

根据这个思路我们就可以写出第一版的call2函数:

// 第一版
Function.prototype.call2 = function(context) {
    // 首先要获取调用call的函数,用this可以获取
    context.fn = this;
    context.fn();
    delete context.fn;
}

测试一下

// 测试一下
var foo = {
    value: 1
};

function bar() {
    console.log(this.value);
}

bar.call2(foo); // 1

模拟实现第二步

call函数还能给定参数执行函数,这里需要注意一下,给定的参数是不确定的,我们需要怎么取呢?

我们可以从Arguments对象中取值,取出第二个到最后一个参数,类似这样

var args = [];
for(var i = 1, len = arguments.length; i < len; i++) {
    args.push('arguments[' + i + ']');
}

// 此时args是类似这样的["arguments[ 1]", "arguments[ 2]"]

但是我们需要怎么将参数传入执行的函数里面呢???  

这里我们使用eval方法,类似这样

eval('context.fn(' + args +')')

eval方法:传入一个字符串,会将该字符串解析为js代码来执行。

这样我们就有了第二版的代码

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

// 测试一下
var foo = {
    value: 1
};

function bar(name, age) {
    console.log(name)
    console.log(age)
    console.log(this.value);
}

bar.call2(foo, 'kevin', 18); 

模拟实现第三步 

写到这里,我们已经完成了80%,现在我们还有2个小细节没有完成:

1. this 参数可以传 null,当为 null 的时候,视为指向 window

2. 函数是可以有返回值的

Function.prototype.call2 = function (context) {
    var context = context || window;
    context.fn = this;

    var args = [];
    for(var i = 1, len = arguments.length; i < len; i++) {
        args.push('arguments[' + i + ']');
    }

    var result = eval('context.fn(' + args +')');

    delete context.fn
    return result;
}

这样我们就完成了call函数的模拟实现,相信你看到这里,已经可以自己实现了call函数,如果有什么不懂的可以私信我或者评论区留言,学无止境,大家加油!!

猜你喜欢

转载自blog.csdn.net/qq_44313091/article/details/106927991
今日推荐