2012-03-27 15 views
7

en JavaScript, puede volver a escribir una función, así:Consecuencias de reescribir dinámicamente una función en javascript?

function foo() { 
    setTimeout(function() { 
     alert('sup stallion'); 
     foo = function() { //rewrite foo to nolonger wait 4 seconds to alert. 
      alert('sup stallion'); 
     } 
    }, 4000); 
} 

Obviamente, esto es un ejemplo artificial, pero ¿hay algo conceptualmente mal con este enfoque ( que no sea una condición de carrera ).

+2

¿Por qué habría una condición de carrera? No hay múltiples subprocesos simultáneos ejecutando el JavaScript. – mellamokb

+0

Oh duh. Gracias :) – Alan

Respuesta

7

El código de auto modificación puede ser confuso y difícil de depurar, por lo que generalmente se evita.

Aparte de eso, no hay ningún problema, y ​​no hay ninguna condición de carrera tampoco.

+2

Este no es un código de auto modificación. Está haciendo una cosa bien definida: reemplazar el enlace 'foo' que contiene una función, con una nueva función. – Kaz

+0

es decir para ser más claro: la función original no se está cambiando destructivamente a otra función; solo se está perdiendo una referencia a esa función, que es muy diferente. Modificar el entorno de enlace variable no es lo mismo que modificar el código en su lugar. – Kaz

+0

@Kaz: Sí, no es estrictamente auto modificador, pero el efecto es el mismo, por lo que puede ser tan difícil de seguir. Si desea cambiar la función, debe declararla como una variable desde el principio, si se declara utilizando la sintaxis de la función normal, generalmente no se espera que cambie. – Guffa

4

Una cosa que noté al probar su código. Considere esto:

setInterval(foo, 6000); 

Función foo se está pasando a setInterval antes de que se modificó, y el original de foo pasarán cada 6 segundos, incluso después de la unión se ha actualizado.

Por otro lado, el siguiente código ejecutará la función original solo en la primera llamada (que actualiza el enlace). Las llamadas posteriores invocará la versión actualizada:

setInterval(function(){foo();}, 6000); 

Parece obvio, pero podrían ser difíciles de depurar ...

3

función dinámica reescritura puede ser utilizado como una forma de lazy initialization, sin embargo, hay un inconveniente:

function Foo() {...} 
Foo.prototype = { 
    bar: function() { 
     //some initialized variables to close over 
     var a, b, c, ...; 
     Foo.prototype.bar = function() { 
      //do stuff with variables 
     }; 
     Foo.prototype.bar.call(this); 
    } 
}; 

Aunque este código es relativamente sencillo de entender, y se utilizaría como:

var f = new Foo(); 
f.bar(); //initializes `bar` function 
f.bar(); //uses initialized `bar` function 

tiene un problema oculto:

var f = new Foo(), 
    g = {}; 
//passing by reference before the function was initialized will behave poorly 
g.bar = f.bar; 
f.bar(); //initializes `bar` function 
g.bar(); //re-initializes `bar` function 
f.bar(); //uses initialized `bar` function 
g.bar(); //re-initializes `bar` function 

Es por esta razón que cualquier inicialización necesaria para una función se realiza normalmente usando un patrón módulo:

function Foo() {...} 
Foo.prototype = { 
    bar: (function() { 
     var a, b, c, ..., fn; 
     //some initialized variables to close over 
     fn = function() { 
      //do stuff with variables 
     }; 
     return fn; 
    }()) 
}; 

El patrón módulo tiene la desventaja de llamar a la código de inicialización de inmediato, pero no tendrá los problemas asociados con la referencia de función.

Cuestiones relacionadas