2010-05-10 13 views
16

Digamos que tenemos el código (olvidarse de prototipos para un momento):Son cierres en javascript recompiladas

function A(){ 
    var foo = 1; 
    this.method = function(){ 
    return foo; 
    } 
} 
var a = new A(); 

es la función interna vuelve a compilar cada vez que se ejecute la función A? O es mejor (y por qué) para hacerlo de esta manera:

function method = function(){ return this.foo; } 
function A(){ 
    this.foo = 1; 
    this.method = method; 
} 
var a = new A(); 

o son los motores de JavaScript lo suficientemente inteligente como para no crear una nueva función de 'método' cada vez? Específicamente, v8 de Google y node.js.

Además, cualquier recomendación general sobre cuándo usar qué técnica es bienvenida. En mi ejemplo específico, realmente me conviene usar el primer ejemplo, pero sé que la función externa se instanciará muchas veces.

+1

Interesante pregunta. Sin embargo, estaría dispuesto a apostar que los intérpretes de JavaScript son más inteligentes que eso. –

+0

Para ser honesto, sin embargo, si está preocupado por el rendimiento, este no debería ser su cuello de botella. Yo no me preocuparía por eso. –

+0

¿Desea implementar un getter en una variable privada que no se replique con cada llamada de constructor? – Alsciende

Respuesta

7

Por lo que entiendo, no es tanto una cuestión de "compilar" la función, ya que tiene un "alcance" diferente cada vez que se ejecuta.

El segundo método que utilizó siempre tendrá method del mismo alcance.

El primer método pone method dentro del alcance de la llamada a la función A(). Por lo tanto, cualquier información que esté dentro de ese alcance (var foo, parámetros de función, etc.) se almacena en esa instancia del alcance de las funciones. Por lo tanto, se hará referencia al mismo código de función cada vez, pero estará en un ámbito diferente (y por lo tanto, un "objeto" diferente).

+0

+1, la mejor y más concisa respuesta aquí (¡y de hecho respondiste la pregunta!). –

2

El método no se vuelve a compilar.

El intérprete de Javascript creará un nuevo objeto de cierre que contenga métodos internos y variables locales cada vez que llame a los métodos externos.

La implementación exacta depende del motor de Javascript.

+1

¿Alguna idea sobre Google v8? – disc0dancer

+0

No tengo idea. – SLaks

0

Supongo que se compila solo una vez ... porque la palabra clave "this" hace referencia al contexto de ejecución ... por lo que el compilador no necesita saber mucho sobre una función para interpretarla.

Por otra parte, cuando se declara una variable en la parte inferior de una función, sigue siendo accesible en la parte superior:

function test() 
{ 
    alert(hello); 
    // ... 
    var hello = 2; 
} 

Es lo mismo con las funciones. Creo con esas 2 cosas en mente, que no se vuelve a compilar cada vez que se llama a A.

Mike

+0

En realidad, esto debería alertar 'indefinido'. La declaración de variable se levanta hacia arriba, pero 'hello' no está configurada en' 2' hasta después de la alerta. –

3

Sí, se está creando un nuevo objeto de función en cada instancia de un objeto A. Se puede demostrar esto de la siguiente manera:

function A(){ 
    var foo = 1; 
    this.method = function(){ 
    return foo; 
    } 
} 
var a = new A(); 
var b = new A(); 
alert(a.method == b.method); // Returns false; two different Function objects 

Si desea volver a utilizar el mismo objeto Function, que el método de una propiedad del prototipo, no los casos.

function B() { 
    this.foo = 1; 
} 

B.prototype.method = function() { 
    return this.foo; 
} 

var a = new B(); 
var b = new B(); 
alert(a.method == b.method); // Returns true; it's the same Function object 

Editar: Por lo que yo sé, no hay ninguna razón para hacer algo como la primera versión, excepto para crear variables privadas en un objeto JavaScript. En el ejemplo original, foo es privado. Nada puede acceder directamente desde fuera del objeto.Desafortunadamente, al instanciar una gran cantidad de objetos utilizando esta técnica, puede tener un impacto en el rendimiento y la huella de memoria.

En mi código, utilizo una convención de nomenclatura para distinguir entre propiedades "públicas" y "privadas". Nombro propiedades privadas con un guión bajo como primer personaje. Entonces, si veo algo como myObject._someMethod(), sé que algo está mal.


Edit2: De http://code.google.com/apis/v8/design.html, yo creo que V8 compila el cierre de una vez, cuando se crea la clase oculto que contiene la propiedad method.

+0

Esto es muy interesante. ¿Podría ser que el operador de igualdad deliberadamente diferencie los dos métodos, pero en realidad son el mismo? Sé que puedo usar el prototipo, pero quiero ignorarlo en este caso. – disc0dancer

+2

@Discodancer: los dos métodos tienen el mismo código, pero son objetos diferentes, como si hiciera algo como 'a = {}; b = {}; alerta (a == b); // falso'. – jhurshman

+1

El rendimiento alcanzado por el uso de variables locales ("privadas") es insignificante en los intérpretes modernos. No sé acerca de la memoria, pero apostaría a que no es lo suficientemente grande como para hacer una gran diferencia. –

0

Imagine una función como un objeto más, y luego cuando crea una nueva, es solo una copia de la anterior, con alguna variable de datos modificada. No es necesario volver a analizar la fuente del objeto para que eso suceda. Una buena analogía es functionoids en C++, objetos de función en Lua, y realmente no conozco muchos otros lenguajes.

0
function A(){ 
    var foo = 1; 
    this.method = function(){ 
    return foo; 
    } 
} 

no se puede compilar en

function method = function(){ return this.foo; } 
function A(){ 
    this.foo = 1; 
    this.method = method; 
} 

porque la funcionalidad cambiaría. El primer ejemplo tiene una variable 'foo' que solo es visible para el constructor 'A' y para la función llamada 'método' mientras que en el segundo ejemplo es accesible a todo lo que tiene acceso a la instancia de 'A'.

Es posible compilar en

function method = function(){ return 1; } 
function A(){ 
    this.method = method; 
} 

Pero no creo que cualquier motor de JavaScript por ahí va a ir tan lejos, pero si usted es capaz de procesar previamente los archivos y dispuestos a invertir en una un poco más de esfuerzo, el google closure compiler podría ir bastante lejos si se usa en modo avanzado.

+0

Mi pregunta era si el primer ejemplo crea nuevas instancias de la función cada vez que ejecuto A(), no si se puede compilar en el otro ejemplo. – disc0dancer