Es bien sabido que Function.prototype.call.call(func) es lo mismo que Function.call.call(func) que es lo mismo que func()
Esta técnica ha estado funcionando desde los primeros navegadores. Por lo tanto, el algoritmo para "llamada doble" se puede derivar fácilmente, por ejemplo, de la especificación ES5
Función.prototipo.llamada (thisArg [ , arg1 [ , arg2, … ] ] )
Cuando se llama al método de llamada en un objeto func con el argumento thisArg y los argumentos opcionales arg1, arg2 , etc., se toman los siguientes pasos:
- Si IsCallable ( func ) es falso, lanza una excepción TypeError .
- Sea argList una lista vacía.
- Si se llamó a este método con más de un argumento, en orden de izquierda a derecha comenzando con arg1 agregue cada argumento como el último elemento de argList
- Devuelve el resultado de llamar al método interno [[Call]] de func , proporcionando thisArg como el valor this y argList como la lista de argumentos.
Considere un ejemplo simple:
var func = function(){ console.log(42) }; Function.prototype.call.call(func); Entonces .call.call(func) es una llamada del método interno [[Call]] de Function.prototype.call que proporciona func como este valor. ¿Cómo es que esto lleva a la llamada de func ?
Parece que el método interno [[Call]] de .call en realidad llama a este valor (como en el caso de func.call() ). Pero no lo obtuve de la especificación.
[[Call]] es un método interno de todos los objetos de función. Realiza la llamada de función real configurando el contexto y evaluando el cuerpo de la función.
De la especificación ES5 :
13.2.1
[[Call]]Cuando se llama al método interno
[[Call]]para un objeto Function F con un valor this y una lista de argumentos, se toman los siguientes pasos:
- Sea funcCtx el resultado de establecer un nuevo contexto de ejecución para el código de función usando el valor de la propiedad interna
[[FormalParameters]]de F , los argumentos pasados List args y el valor this como se describe en 10.4.3.- Sea result el resultado de evaluar el FunctionBody que es el valor de la propiedad interna
[[Code]]de F. Si F no tiene una propiedad interna[[Code]]o si su valor es un FunctionBody vacío, el resultado es (normal, indefinido, vacío) .- Salga del contexto de ejecución funcCtx , restaurando el contexto de ejecución anterior.
- Si el resultado .tipo es arrojar, arroja el resultado .valor.
- Si el tipo de resultado se devuelve, se devuelve el valor de resultado .
- De lo contrario, el tipo de resultado debe ser normal. Devolver indefinido .
Sin embargo, tenga en cuenta que [[Call]] también se usa en la sintaxis de llamada de la función fn() (consulte el paso 8):
11.2.3 Llamadas de funciones
La producción CallExpression : MemberExpression Arguments se evalúa de la siguiente manera:
- Sea ref el resultado de evaluar MemberExpression .
- Sea func GetValue( ref ).
- Sea argList el resultado de evaluar Arguments , produciendo una lista interna de valores de argumento (ver 11.2.4).
- Si Type( func ) no es Object, lanza una excepción TypeError .
- Si IsCallable es falso , lanza una excepción TypeError .
- Si Tipo( ref ) es Referencia, entonces
- Si IsPropertyReference( ref ) es verdadero , entonces
- Deje que este valor sea GetBase ( ref ).
- De lo contrario, la base de ref es un registro de entorno
- Sea thisValue el resultado de llamar al método concreto ImplicitThisValue de GetBase( ref ).
- De lo contrario, Type( ref ) no es Reference.
- Deje que thisValue sea indefinido .
- Devuelve el resultado de llamar al método interno
[[Call]]en func , proporcionando thisValue como el valor this y proporcionando la lista argList como los valores del argumento.La producción CallExpression : CallExpression Arguments se evalúa exactamente de la misma manera, excepto que la CallExpression contenida se evalúa en el paso 1.