JavaScript call和apply的实现

9 篇文章 0 订阅

MDN对call方法的定义:

call()方法使用一个指定的 this值和单独给出的一个或多个参数来调用一个函数。

var foo = {
  value: 1
};

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

bar.call(foo); // 1

这段代码一共做了两件事情:

  1. call方法改变了this的指向,指向了foo
  2. 执行了bar函数

我们可以尝试着改变一下foo对象:

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

foo.bar(); // 1

这个时候的this就指向了foo,但是这样foo对象就会多了bar这个属性,因此,我们要删除这个属性,与之前foo对象保持一致。

这样,实现call方法可分为三个步骤:

  1. 将目标函数设置为对象的属性
  2. 执行这个函数
  3. 删除这个函数
// 1
foo.fn = bar
// 2
foo.fn()
// 3
delete foo.fn

根据这个步骤我们可以试着写一下call方法

Function.prototype.call2 = function(context){
    context.fn = this
    context.fn()
    delete context.fn
}

用这个方法测试一下,能看到打印出来的结果依然是1。

那么下一个要解决的问题是:当无法得知传入的参数个数时,要怎么处理呢?

我们知道,arguments是一个对应于传递给函数参数的类数组对象(具有length属性,不具有数组的方法)。这样,我们就可以从argument是对象中取值,取出第二个到最后的一个参数,然后放进一个数组里。

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

解决了不定长的问题,就要把参数放到要执行的函数参数里面去。

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

这样args就会自动调用Array.toString()这个方法。结合参数调整一下我们写的call方法。

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

再次进行测试,也能验证没有问题。

还有最后一点需要考虑的问题,就是this为null的时候,指向window,以及需要返回值。

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

至此,就完成了call的模拟实现。

apply方法跟call方法类似

Function.prototype.apply = function(context,arr){
    var context = Object(context) || window
    context.fn = this
    
    var result
    if(!arr){
       result = context.fn()
    } else {
        var args = []
        for(var i = 0;i < arguments.length;i++){
            args.push('arr[' + i + ']')
        }
        result = eval('context.fn(' + args + ')' )
    }
    
    delete context.fn
    return result
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值