美文网首页
模拟call apply

模拟call apply

作者: 别过经年 | 来源:发表于2019-06-02 23:24 被阅读0次

    1. call

    call用于指定函数调用时候上下文对象this,比如

    var a = 9;
    const obj = {
      a: 2,
      show: function() {
        console.info(this.a);
      }
    };
    
    obj.show(); //2
    
    const other = {
      a: 99
    };
    
    obj.show.call(other); //99
    

    利用对象直接调用其函数的时候,该函数的this指向该对象,

    Function.prototype.call2 = function(context, ...args) {
      context.prop = this; //将 call2的调用者 object.show 赋值给给上下文对象
      context.prop(...args); // 通过上下文对象调用被call的函数
      delete context.prop; // 不能改变context对象,所以调用完 要删除额外属性
    };
    
    obj.show(78); //2
    obj.show.call2(other, 34); //99
    

    但是这种写法扩展操作符是ES6的,所以可以改进,prop属性可能跟context已有的属性冲突,所以可以用symbol去改进,

    • 函数可能有返回值,改进如下
    Function.prototype.call2 = function(context, ...args) {
      context.prop = this; //将 call2的调用者 object.show 赋值给给上下文对象
      const ret = context.prop(...args); // 通过上下文对象调用被call的函数
      delete context.prop; // 不能改变context对象,所以调用完 要删除额外属性
      return ret; // 在此返回函数执行结果
    };
    
    var a = 9;
    const obj = {
      a: 2,
      show: function() {
        console.info(this.a);
        return this.a + 1;
      }
    };
    
    const other = {
      a: 99
    };
    
    const ret = obj.show(78); //2
    console.info(ret); // 3
    const result = obj.show.call2(other, 34); //99
    console.info(result); // 100
    
    • 用ES6的扩展运算符去模拟ES5代码不合适吧,继续改进
      利用eval函数执行js字符串
    Function.prototype.call2 = function() {
      var args = [];
      var context = arguments[0];
      for (let i = 1; i < arguments.length; i++) {
        args.push(arguments[i]);
      }
    
      context.prop = this; //将 call2的调用者 object.show 赋值给给上下文对象
    
      var str = "context.prop(";
      str += args.join(",") + ")";
      var ret = eval(str); //使用eval执行拼接好的函数调用字符串
      delete context.prop; // 不能改变context对象,所以调用完 要删除额外属性
      return ret;
    };
    
    var a = 9;
    const obj = {
      a: 2,
      show: function(x) {
        console.info(this.a, x);
        return this.a + 1;
      }
    };
    
    const other = {
      a: 99
    };
    
    const ret = obj.show(78); //2 78
    console.info(ret); // 3
    const result = obj.show.call2(other, 34); //99 34
    console.info(result); // 100
    
    

    2. apply

    apply 的第二个参数是数组或者类数组

    var a = 9;
    const obj = {
      a: 2,
      show: function(age, name, sex) {
        console.info(this.a, age, name, sex);
      }
    };
    
    obj.show(34, 'geek', 'male'); //2
    
    const other = {
      a: 99
    };
    
    obj.show.call(other, 25, 'bill', 'jin'); //99
    obj.show.apply(other, [25, 'bill', 'jin']); //99
    

    上面的测试得到如下的结果

    2 34 'geek' 'male'
    99 25 'bill' 'jin'
    99 25 'bill' 'jin'
    说明在模拟apply的时候需要将apply的第二个参数展开给调用apply的函数

    Function.prototype.apply2 = function(context, args) {
      context.prop = this;
      context.prop(...args);// 需要展开
      delete context.prop;
    };
    
    
    obj.show.apply2(other, [9, 8, 7]);
    

    相关文章

      网友评论

          本文标题:模拟call apply

          本文链接:https://www.haomeiwen.com/subject/zxnfxctx.html