手摸手带你实现call函数,重在思路

待会实现的自定义call函数,会用下面简单的函数调用系统的call函数进行对比

function foo() {
    console.log('foo函数执行', this);
}

function sum(num1, num2) {
    console.log('sum函数执行', this, num1, num2);
    return num1 + num2;
}

一、给所有的函数添加一个 dycall 方法

系统的call 函数是可以直接被函数调用的,因此,我们自定义的 dycall 方法需要挂载到函数对象的原型上

Function.prototype.dycall = function() {
    console.log('dycall方法执行')
}
foo.dycall();

二、获取被调用的函数

在自定义的 dycall 方法中,我们要获取到调用 dycall 方法的函数对象,然后在 dycall 方法中,进行调用

foo.dycall() 相当于 this 的隐式绑定,直接通过this获取被调用的函数对象

Function.prototype.dycall = function () {
    // 1.获取需要被执行的函数
    var fn = this;
    fn();
}

三、dycall 方法绑定 传入的内容

        传入空对象对比调用

foo.call({}); // foo函数执行 {}
foo.dycall({}); // foo函数执行 Window {...}

对比系统call调用,自定义的 dycall 方法this指向的是 window,因此绑定this,将获取的 fn 函数添加到传入的对象上,相当于this的隐式绑定,this就指向了传入的空对象

// 给所有函数添加一个 dycall 的方法
Function.prototype.dycall = function (thisArgs) {
    // 1.获取需要被执行的函数
    var fn = this;
    thisArgs.fn = fn;
    thisArgs.fn();
};
foo.dycall({}); // foo函数执行 {fn: ƒ}

在此调用后,输出的this就是我们传入的空对象,但是对比系统call,我们多了绑定的 fn 函数,因此需要在dycall调用fn后,删除多余的属性。输出的时候会看到,但,无伤大雅

// 给所有函数添加一个 dycall 的方法
Function.prototype.dycall = function (thisArgs) {
    // 1.获取需要被执行的函数
    var fn = this;
    thisArgs.fn = fn;
    // 2.调用需要被改变this的函数
    thisArgs.fn();
    delete thisArgs.fn;
};

四、对比其他几种case,完善边界情况

  1. 传入对象
  2. 传入简单数据类型
foo.call(123); // foo函数执行 Number {123}
foo.call("abc"); // foo函数执行 String {'abc'}

我们自定义的 dycall 绑定的this是对象,因此可以进行 fn 添加调用,如果传入的是简单数据类型,需要我们转换为对应类型的对象,需要使用到 Objecet(),方法

// 给所有函数添加一个 dycall 的方法
Function.prototype.dycall = function (thisArgs) {
    // 1.获取需要被执行的函数
    var fn = this;
    // 2.将thisArgs转成对象类型(防止传入的是非对象类型),非对象类型不能添加属性
    thisArgs = Object(thisArgs)
    thisArgs.fn = fn;
    // 3.调用需要被改变this的函数
    thisArgs.fn();
    delete thisArgs.fn;
};
foo.dycall(123); // foo函数执行 Number {123, fn: ƒ}
foo.dycall("abc"); // foo函数执行 String {'abc', fn: ƒ}

        3.传入 null 、undefined 或 空参

        foo调用系统call,this指向 Window

foo.call(); // foo函数执行 Window {...}
foo.call(undefined); // foo函数执行 Window {...}
foo.call(null); // foo函数执行 Window {...}
// 给所有函数添加一个 dycall 的方法
Function.prototype.dycall = function (thisArgs) {
    // 1.获取需要被执行的函数
    var fn = this;

    // 2.将thisArgs转成对象类型(防止传入的是非对象类型),非对象类型不能添加属性
    thisArgs = thisArgs ? Object(thisArgs) : window;

    thisArgs.fn = fn;

    // 3.调用需要被改变this的函数
    thisArgs.fn();
    delete thisArgs.fn;
};

     

五、参数赋值以及返回值(最终版)

sum.call({}, 20, 30); // sum函数执行 {} 20 30

tips:用剩余运算符 ...args 接收多个参数,调用的时候使用 扩展运算符 ...args

// 给所有函数添加一个 dycall 的方法
Function.prototype.dycall = function (thisArgs, ...args) {
  // 1.获取需要被执行的函数
  var fn = this;
  // 2.将thisArgs转成对象类型(防止传入的是非对象类型),非对象类型不能添加属性
  thisArgs = thisArgs ? Object(thisArgs) : window;
  thisArgs.fn = fn;
  // 3.调用需要被改变this的函数
  var result = thisArgs.fn(...args);
  delete thisArgs.fn;
  // 4.将调用后的结果返回
  return result;
};

上一篇:根据指定条件对list中元素进行排序


下一篇:ES6---解构赋值(数组、对象、字符串、数值和布尔值、函数参数)