Johan
Johan

Reputation: 35194

How to check if a function is called using call/apply

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

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

Is there any way to know if foo() was called using a normal invoke or call/apply?

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

Upvotes: 8

Views: 762

Answers (6)

iimos
iimos

Reputation: 5037

Dirty, dirty hack:

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()

It's just food for thought. Don't use it on production.

Upvotes: 1

Alexander Cheprasov
Alexander Cheprasov

Reputation: 21

Try something like this:

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 }]);

Upvotes: 1

Qantas 94 Heavy
Qantas 94 Heavy

Reputation: 16020

Unless you redefine Function.prototype.call and Function.prototype.apply (and pass another argument to your function), there is no way of doing so - the actual internal mechanics (the [[Call]] internal function) do not provide any method of signalling that it is called using ().

Compare the specification for a general function call and for Function.prototype.apply - each call the internal code of the function in exactly the same manner, and there is no external property set that is able to give you whether is called using that or not.

See the specification for the internal function [[Call]]:

13.2.1 [[Call]]

When the [[Call]] internal method for a Function object F is called with a this value and a list of arguments, the following steps are taken:

  1. Let funcCtx be the result of establishing a new execution context for function code using the value of F's [[FormalParameters]] internal property, the passed arguments List args, and the this value as described in 10.4.3.
  2. Let result be the result of evaluating the FunctionBody that is the value of F's [[Code]] internal property. If F does not have a [[Code]] internal property or if its value is an empty FunctionBody, then result is (normal, undefined, empty).
  3. Exit the execution context funcCtx, restoring the previous execution context.
  4. If result.type is throw then throw result.value.
  5. If result.type is return then return result.value.
  6. Otherwise result.type must be normal. Return undefined.

There is no provision to change the running of a function from whether it is called using call/apply or not - the only thing that changes what it does are the arguments for the function itself and what is this meant to be within the function.

Upvotes: 2

jcamelis
jcamelis

Reputation: 1309

I hope this solve your problem:

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 }]);

Upvotes: 1

Benjamin Gruenbaum
Benjamin Gruenbaum

Reputation: 276286

No. You can't detect if a function is called from call/apply or normally.

They're not magical beings, all they do is set the arguments and this value. There is a subtle difference when it comes to undefined/undeclared values but that's it.

All .apply and .call do in ES5 is:

Return the result of calling the [[Call]] internal method of func, providing thisArg as the this value and argList as the list of arguments.

It sets the internal caller attribute correctly so something naive like this would not work.

Upvotes: 3

Philip
Philip

Reputation: 779

I can't think of a reason you should be checking for this but, you could check by comparing this === window, as that's the default scope (assuming browser based Javascript), but this could be faked by simply calling foo like so foo.call(window) which is basically what calling foo() is doing normally.

This also probably won't work using window for functions that are properties of other objects or prototypes.

Upvotes: -1

Related Questions