2010-11-07 16 views
15

Soy nuevo en la programación y estoy tratando de aprender JS con el libro Eloquent Javascript.Pregunta de ejemplo de JavaScript: alcance/cierre léxico - Eloquent Javascript

Hasta aquí todo bien, hasta que llegué a un ejemplo con el siguiente código

function makeAddFunction(amount) { 
    function add(number) { 
    return number + amount; 
    } 
    return add; 
} 

var addTwo = makeAddFunction(2); 
var addFive = makeAddFunction(5); 
show(addTwo(1) + addFive(1)); 

nota: espectáculo es como alerta, sólo que muestra las variables en la pantalla de una consola JS el tutorial ha integrado.

El autor dice que este es un ejemplo para mostrar cómo el alcance léxico permite sintetizar funciones. Chapter here

Lo que no entiendo es cómo addTwo y addFive, que supuestamente son variables, pueden enviar parámetros a las funciones makeAddFunction y add, y más específicamente, ¿cómo la función add sabe que el parámetro de las variables están enviando es el parámetro number.

Gracias por su ayuda, muchachos!

+0

Probablemente vale la pena leer esto: http://en.wikipedia.org/wiki/Closure_(computer_science) –

Respuesta

6

Creo que la clave para entender ese ejemplo es entender que las funciones pueden devolver otras funciones (como cualquier otra variable). Documentar ese código será de gran ayuda para comprender ese concepto.

/** 
* Creates an adder function 
* @param {number} amount Amount to add 
* @return {function} Method that adds 'amount' to its argument. 
* See the documentation of add for its signature 
*/ 
function makeAddFunction(amount) {  
    /** 
    * Everytime makeAddFunction is called, a new instance of add is created 
    * (and returned) that holds on to its copy of 'amount' (through the closure) 
    * @param {number} number value to add to 'amount' 
    * @return {number} 'amount' + 'number' 
    */ 
    return function add(number) { 
    return number + amount; 
    }; 
} 

// addTwo now is a reference to a function that when called 
// adds 2 to whatever is passed in 
var addTwo = makeAddFunction(2); 
// addFive Adds 5 to its argument 
var addFive = makeAddFunction(5); 
// addTwo(1) = 3, addFive(1) = 6, therefore, output is 9 
show(addTwo(1) + addFive(1)); 
8

y addFive son variables, pero son variables de función. Mira typeof(addTwo) - es una función. Es como si lo hiciera esto:

var addTwo = function(x) { return x + 2; }; 

Es lo mismo que esto:

function addTwo(x) { return x + 2; } 

(Edit: Como Šime señaló, no son exactamente lo mismo Ver here para una explicación de la. diferencia entre los dos.)

El ejemplo tendrá sentido una vez que lo comprenda. Incluso se puede hacer cosas raras como esta, declarar una función anónima e invocando inmediato:

var seven = function(x) { return x + 2; }(5); 

que está literalmente, a nivel de código máquina física, exactamente el mismo que: lo que equivale a por todas fines pertinentes a esta pregunta:

function addTwo(x) { return x + 2; } 
var seven = addTwo(5); 

Editar:

Tal vez una menos confusa "precuela" de esto es la siguiente:

function makeTheAddTwoFunction() 
{ 
    return function(x) { return x + 2; } 
} 

var addTwo = makeTheAddTwoFunction(); 

Esto es tonto, pero sirve para ilustrar funciones que hacen funciones. Por supuesto, ese tipo de función generalmente aceptará argumentos para que pueda hacer diferentes funciones cada vez, pero ahí está.

+4

@Ian Las dos primeras líneas de código no son equivalentes. Solo son * casi * lo mismo. –

+0

@ Šime ¿Puedes explicar la diferencia? Ambos definen el símbolo 'addTwo' en el alcance actual y establecen su valor para la misma función. Siempre he tratado estos como intercambiables, tal vez en mi perjuicio. –

+0

Lo sentimos, los ejemplos dados NO son iguales. Está comparando la asignación de una variable a una función con un nombre para una función. ¡Ellos no son los mismos! Pueden tener diferentes valores para * this *; la función asignada a una variable se puede reasignar, la función no tanto. ** Recuerde ** que este SO Q se trata de los puntos finos del idioma, por lo que estos temas importan. –

9

En javascript, una función es un objeto de primera clase, es decir, se puede pasar, asignar a una variable, etc. Las variables addTwo y addFive contienen funciones. Esas funciones son generadas por la función "factory" makeAddFunction.

Las funciones que addTwo y addFive contienen llevan consigo el alcance que existía cuando se crearon. Es decir, cuando addTwo, por ejemplo, se creó, el parámetro "amount" fue 2.Así addTwo, en esencia, es la siguiente función:

function addTwo(number) { 
    return number + 2; 
} 

Cuando alguien llama addTwo(), que no es nada pasa de nuevo a makeAddFunction. MakeAddFunction ya se ejecutó y finalizó. Sin embargo, el ámbito creado dentro de makeAddFunction (en el que "amount" equivale a 2) persiste en la función addTwo.

+0

¡Fantástico! Estaba bizco y babeando sobre este ejemplo (no el tuyo, el de Eloquent JavaScript), ¡pero tu explicación lo ha aclarado! La suya es la primera explicación que vi (y comprendí) que mencionó el hecho de que la cantidad se "cocía" cuando el "encargado de la función" construía la función interna que devuelve. ¡¡¡Gracias!!! – mbm29414

+0

Realmente tuve que reflexionar durante unos minutos para entenderlo, pero esta es la explicación más clara aquí. Me ayudó a pensar en 'makeAddFunction()' como un constructor, ya que devuelve nuevas instancias de 'add()'. – gmeben

3

Re: Lo que no entiendo es cómo addTwo y addFive, que supuestamente son variables, pueden enviar parámetros a las funciones makeAddFunction?

addTwo y addFive son variables. Pero sus valores no son simples escalares (números, cadenas, etc.). Más bien, sus valores son funciones. Como sus valores son funciones, está bien invocar esas funciones. Por ejemplo, addTwo(1)

Re: y, más concretamente, ¿cómo la función de complemento sabe que el parámetro de las variables están enviando es el parámetro número?

La función add llama a su primer número de parámetro . Entonces, cuando llama a la función a través de una variable, por ejemplo, addOne, el primer parámetro dado a addOne se convierte en número.

ps Si piensa para sí mismo, "¡Sí, esto es bastante complicado!" Entonces tienes razón, y ese es todo el propósito del ejemplo, para mostrar algo complicado. La frecuencia con la que usará esta técnica puede variar de nunca a cada cierto tiempo.

0

La mejor manera de pensar en una pieza de código como este es sustitutos valores y Interpet en su mente

// when this one is invoked 
var addTwo = makeAddFunction(2); 

// makeAddFunction 
// becomes something like 
function makeAddFunction(2) { 
    function add(number) { 
    return number + 2; 
    } 
    // return a function, that adds 
    // 2 to every number it gets 
    return add; 
} 

// therefore this function call 
// will add 2 to 1 and return 3 
addTwo(1); 
0

Como siempre, aquí están los Jibbring notes on JavaScript Closures. Se analizan los alcances, los contextos de ejecución, de resolución variable/propiedad, etc ...

Mientras que los cierres de JavaScript son léxica, ejecución contextos (que puede contener propiedades) están obligados (piensa en Python o Ruby) y no sólo "variables libres "(como es el caso con C# o Scala). Esta es la razón por la cual nuevas variables solo se pueden introducir en los nuevos ámbitos de función. (Las implementaciones modernas de Mozilla introducen let).

Cuestiones relacionadas