【问题标题】:How to check if a function is called using call/apply如何使用 call/apply 检查函数是否被调用
【发布时间】:2013-11-08 15:16:38
【问题描述】:
function foo(){
    console.log('foo', this);
}

foo();
foo.call({ bar: 1 });
foo.apply([{ bar: 1 }]);

有什么方法可以知道foo() 是使用普通调用还是call/apply 调用的?

http://jsfiddle.net/H4Awm/1/

【问题讨论】:

  • 我想说你的意思是foo.call(this, { bar: 1 })); 和foo.apply(this, [{ bar: 1 }])); 对吧?

标签: javascript


【解决方案1】:

肮脏,肮脏的黑客:

function foo() {
    var isNatural = !/foo\.(call|apply)/.test("" + foo.caller)
    console.log(isNatural ? "foo()" : "foo.{call,apply}()")
}

function caller1() {
    foo()
}
function caller2() {
    foo.call(null, { bar: 1 })
}
function caller3() {
    foo.apply(null, [{ bar: 1 }])
}

caller1()
caller2()
caller3()

这只是思考的食物。不要在生产中使用它。

【讨论】:

    【解决方案2】:

    试试这样的:

    function foo(){
    
        console.log('foo', this);
        console.log( arguments );
    }
    Function.prototype._apply =  Function.prototype.apply;
    Function.prototype.apply = function(ths,args){
        args.unshift('apply');
        this._apply(ths,args);
    };
    
    foo();
    foo.call(this, { bar: 1 });
    foo.apply(this, [{ bar: 1 }]);
    

    【讨论】:

      【解决方案3】:

      除非您重新定义 Function.prototype.call 和 Function.prototype.apply(并将另一个参数传递给您的函数),否则无法这样做 - 实际的内部机制([[Call]] 内部函数)不提供任何信号发送方法它是使用() 调用的。

      比较 general function call 和 Function.prototype.apply 的规范 - 每个调用函数的内部代码的方式完全相同,并且没有外部属性集可以告诉您是使用它调用还是不是。

      查看内部函数[[Call]]的规范:

      13.2.1[[Call]]

      当使用 this 值和参数列表调用 Function 对象 F 的 [[Call]] 内部方法时,将执行以下步骤:

      1. 让 funcCtx 成为使用 F 的 [[FormalParameters]] 内部属性的值、传递的参数 List args 和 10.4.3 中所述的 this 值为函数代码建立新执行上下文的结果。
      2. 令 result 为 FunctionBody 的计算结果,它是 F 的 [[Code]] 内部属性的值。如果 F 没有 [[Code]] 内部属性,或者它的值为空 FunctionBody,则结果为(正常、未定义、空)。
      3. 退出执行上下文funcCtx,恢复之前的执行上下文。
      4. 如果 result.type 是 throw 则 throw result.value。
      5. 如果返回result.type,则返回result.value。
      6. 否则result.type必须是正常的。返回未定义。

      没有规定可以改变函数的运行,无论它是否使用call/apply 调用 - 唯一改变它所做的是函数本身的参数以及@987654333 是什么@ 意味着在函数内。

      【讨论】:

      • Unless you hijack Function.prototype.call - 我很想看到这种事情的实现 - 我相信它会比你想象的更困难,因为.call 也在内部使用。
      • @BenjaminGruenbaum:我只是说将函数重新定义为包装器,我应该更清楚地说明这一点。
      【解决方案4】:

      我希望这能解决你的问题:

      function foo(){
          console.log('foo', this);
      
          if (typeof this.length === "number") {
              //function has been apply
          } else {
              //function has been call
          }
      }
      
      foo();
      foo.call({ bar: 1 });
      foo.apply([{ bar: 1 }]);
      

      【讨论】:

      • 问题在于你传递了一个apply 的数组和一个call 的对象。当你为两者传递一个对象时会发生什么?
      【解决方案5】:

      没有。您无法检测到函数是从call/apply 调用还是正常调用。

      他们不是魔法生物,他们所做的只是设置参数和this 值。对于未定义/未声明的值,有一个subtle difference,仅此而已。

      所有 .apply 和 .call 在 ES5 中的作用是:

      返回调用func的[[Call]]内部方法的结果,提供thisArg作为this值,提供argList作为参数列表。

      它正确地设置了内部caller 属性,所以像this 这样的天真的东西不会起作用。

      【讨论】:

        【解决方案6】:

        我想不出你应该检查这个的原因,但你可以通过比较 this === window 来检查,因为这是默认范围(假设基于浏览器的 Javascript),但这可以通过简单地调用 foo 来伪造所以foo.call(window) 这基本上就是调用foo() 的正常操作。

        对于属于其他对象或原型的属性的函数,这也可能无法使用 window。

        【讨论】:

          猜你喜欢
          • 2011-09-08
          • 1970-01-01
          • 1970-01-01
          • 2011-03-22
          • 1970-01-01
          • 1970-01-01
          • 2020-05-31
          • 1970-01-01
          • 2017-04-02
          相关资源
          最近更新 更多