深度解析 call 和 apply 原理

2019-07-18  本文已影响0人  y先生_f18f

call()主要有以下两点特点

  1. 可以改变我们当前函数点this指向
  2. 还会让当前函数执行

我们主要根据这两点进行他的原理探讨

//call的实现原理
Function.prototype.call = function(context){
    //1、调用call方法的时候会传入this指向,但是在传入的时候可能string字符串,所以我们这里需要Object(context)处理
    //2、如果调用的时候没有传入this指向,我们需要指向window

    context = context ? Object(context) : window;

    //将函数设置为对象的属性  
    context.fn = this;

    //接收传递的参数
    let args = [];

    //这里i从1开始是因为arguments的第一项是接收我们传递的this指向
    for (let i = 1; i < arguments.length; i++) {
        args.push('arguments['+i+']');
    }

    //下面代码中args 会自动调用 args.toString() 方法,因为'context.fn(' + args +')'本质上是字符串拼接,会自动调用toString()方法
    var result = eval('context.fn(' + args +')');
    
    //将前面对context额外添加的fn属性删掉
    delete context.fn;

    return result;
}

上面我们用比较老的方式实现了,下面我们尝试用es6的方式实现

Function.prototype.call = function(context){
    
    context = context ? Object(context) : window;
    context.fn = this;

    let args = [...arguments].slice(1);
    let result = context.fn(...args);

    delete context.fn;
    return result;
}

apply的实现跟call是差不多的,区别在于apply接收的参数是一个数组

//apply 的实现原理
Function.prototype.apply = function(context,args){
    context = context ? Object(context) : window;
    context.fn = this;

    if(!args){
        return context.fn();
    }

    //利用数组的toString特性
    let result = eval('context.fn('+args+')');

    delete context.fn;
    return result;
}

最后我们还是用es6的方式简单实现一下

Function.prototype.apply = function(context,args){
    context = context ? Object(context) : window;
    context.fn = this;

    if(!args){
        return context.fn();
    }

    let result = context.fn(...args);
    delete context.fn;
    return result;
}
上一篇下一篇

猜你喜欢

热点阅读