7
function foo(){
    console.log('foo', this);
}

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

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

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

4

6 回答 6

3

不,您无法检测到函数是否被调用call/apply或正常调用。

他们不是魔法生物,他们所做的只是设定论点和this价值。当涉及到未定义/未声明的值时,存在细微差别,仅此而已。

ES5 中的.apply所有功能是:.call

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

它正确地设置了内部caller属性,所以像这样天真东西是行不通的。

于 2013-10-29T12:54:53.560 回答
2

除非您重新定义Function.prototype.calland Function.prototype.apply(并将另一个参数传递给您的函数),否则没有办法这样做 - 实际的内部机制([[Call]]内部函数)不提供任何方法来表明使用().

比较一般函数调用的规范和Function.prototype.apply- 每次调用函数的内部代码的方式完全相同,并且没有外部属性集可以告诉您是否使用该函数调用。

请参阅内部功能的规范[[Call]]

13.2.1[[Call]]

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

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

没有任何规定可以改变函数的运行是否使用call/apply或不调用 - 唯一改变它所做的是函数本身的参数以及函数中的this含义。

于 2013-10-29T12:56:28.673 回答
1

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

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 }]);
于 2013-10-29T13:06:14.297 回答
1

尝试这样的事情:

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 }]);
于 2013-10-29T13:33:17.700 回答
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()

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

于 2013-10-29T13:43:15.837 回答
-1

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

这也可能不适window用于作为其他对象或原型属性的函数。

于 2013-10-29T12:57:43.480 回答