It is well known that Function.prototype.call.call(func) is the same as Function.call.call(func) which is the same as func()
This technique has been working since the first browsers. Hence the algorithm for "double call" can be easily derived, say, from ES5 specification
Function.prototype.call (thisArg [ , arg1 [ , arg2, … ] ] )
When the call method is called on an object func with argument thisArg and optional arguments arg1, arg2 etc, the following steps are taken:
- If IsCallable(func) is false, then throw a TypeError exception.
- Let argList be an empty List.
- If this method was called with more than one argument then in left to right order starting with arg1 append each argument as the last element of argList
- Return the result of calling the [[Call]] internal method of func, providing thisArg as the this value and argList as the list of arguments.
Consider a simple example:
var func = function(){ console.log(42) };
Function.prototype.call.call(func);
So .call.call(func) is a call of internal method [[Call]] of Function.prototype.call providing func as this value. How come this leads to the call of func?
Looks like [[Call]] internal method of .call actually calls its this value (as in case of func.call()). But I didn't get it from the spec.
[[Call]] is an internal method of all function objects. It performs the actual function call by setting up the context and evaluating the body of the function.
From the ES5 Specification:
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.
However, do note that [[Call]] is also used by the fn() function call syntax (look at step 8):
11.2.3 Function Calls
The production CallExpression : MemberExpression Arguments is evaluated as follows:
- Let ref be the result of evaluating MemberExpression.
- Let func be GetValue(ref).
- Let argList be the result of evaluating Arguments, producing an internal list of argument values (see 11.2.4).
- If Type(func) is not Object, throw a TypeError exception.
- If IsCallable is false, throw a TypeError exception.
- If Type(ref) is Reference, then
- If IsPropertyReference(ref) is true, then
- Let thisValue be GetBase(ref).
- Else, the base of ref is an Environment Record
- Let thisValue be the result of calling the ImplicitThisValue concrete method of GetBase(ref).
- Else, Type(ref) is not Reference.
- Let thisValue be undefined.
- Return the result of calling the
[[Call]]internal method on func, providing thisValue as the this value and providing the list argList as the argument values.The production CallExpression : CallExpression Arguments is evaluated in exactly the same manner, except that the contained CallExpression is evaluated in step 1.