Reputation: 35194
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
?
Upvotes: 8
Views: 762
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
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
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:
- 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.
- 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).
- Exit the execution context funcCtx, restoring the previous execution context.
- If result.type is throw then throw result.value.
- If result.type is return then return result.value.
- 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
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
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
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