2010-09-09 8 views
30

¿Hay alguna forma de ejecutar un código JavaScript solo UNA VEZ, sin usar variables de indicador booleanas para recordar si ya se ejecutó o no?JavaScript - ejecutar una vez sin booleanos

Específicamente no algo como:

var alreadyRan = false; 
function runOnce() { 
    if (alreadyRan) { 
    return; 
    } 
    alreadyRan = true; 

    /* do stuff here */ 

} 

Voy a tener una gran cantidad de este tipo de funciones y mantener todos los valores booleanos gustaría ser un poco incómodo ...

+2

Necesita aclarar la pregunta un poco más. –

+1

Simplemente no use bucles? – irishbuzz

+0

¿No lo llamas dos veces? – EboMike

Respuesta

75

Una forma alternativa de que sobrescribe una función cuando se ejecuta, por lo que se ejecutará solo una vez.

function useThisFunctionOnce(){ 
    // overwrite this function, so it will be executed only once 
    useThisFunctionOnce = Function(""); 
    // real code below 
    alert("Hi!"); 
} 

// displays "Hi!" 
useThisFunctionOnce(); 
// does nothing 
useThisFunctionOnce(); 

'útil' ejemplo:

var preferences = {}; 
function read_preferences(){ 
    // read preferences once 
    read_preferences = Function(""); 
    // load preferences from storage and save it in 'preferences' 
} 
function readPreference(pref_name){ 
    read_prefences(); 
    return preferences.hasOwnProperty(pref_name) ? preferences[pref_name] : ''; 
} 
if(readPreference('like_javascript') != 'yes'){ 
    alert("What's wrong wth you?!"); 
} 
alert(readPreference('is_stupid') ? "Stupid!" : ":)"); 

Editar: como CMS señaló, simplemente sobrescribir la función de edad con function(){} creará un cierre en el que todavía existen las variables de edad. Para evitar este problema, function(){} se reemplaza por Function(""). Esto creará una función vacía en el alcance global, evitando un cierre.

+2

+1 ¡Para una solución inteligente! –

+6

+1 de hecho! Dando un nuevo significado a "esta función se autodestruirá después del primer uso". – EboMike

+0

muy limpio aunque no veo el valor de esto. aunque no es tu culpa +1 – mcgrailm

-2

Si no está vinculada a un evento, el código es por lo general una vez que corrió

+1

¿Y qué hay de los bucles? – Daff

+0

lo suficientemente simple, teniendo en cuenta la pregunta. – Zlatev

9

me gusta la aplicación de Lekensteyn, pero también se podría sólo hay una variable para almacenar qué funciones han corrido. El código siguiente debe ejecutar "runOnce" y "runAgain" ambas una vez. Siguen siendo booleanos, pero parece que no quieres muchas variables.

var runFunctions = {}; 

function runOnce() { 
    if(!hasRun(arguments.callee)) { 
    /* do stuff here */ 
    console.log("once"); 
    } 
} 

function runAgain() { 
    if(!hasRun(arguments.callee)) { 
    /* do stuff here */ 
    console.log("again"); 
    } 
} 


function hasRun(functionName) { 
functionName = functionName.toString(); 
functionName = functionName.substr('function '.length); 
functionName = functionName.substr(0, functionName.indexOf('(')); 

if(runFunctions[functionName]) { 
    return true; 
} else { 
    runFunctions[functionName] = true; 
    return false; 
} 
} 

runOnce(); 
runAgain(); 
runAgain(); 
+1

(Nota: todavía me gusta más la implementación de Lekensteyn) –

+0

Tres líneas para analizar el nombre de la función son algo pesadas. Recomiendo una solución simple de expresiones regulares: 'functionName = func.toString(). Match (/ \ w + (? = \() /) [0]' – bennedich

+0

@Mike Robinson - ¿Por qué hay dos definiciones de funciones para 'runAgain()' ? – irishbuzz

6

¿Qué ocurre con una función anónima invocada de forma inmediata?

(function() { 

    // code in here to run once 

})(); 

el código se ejecutará inmediatamente y no dejará rastros en el espacio de nombres global.

Si se va a necesitar llamar a este código desde otro lugar, se puede usar un cierre para garantizar que el contenido de una función se ejecute solo una vez. Personalmente, prefiero esto a una función que se reescribe a sí misma porque siento que puedo causar confusión, pero a cada uno de ellos :) Esta implementación en particular aprovecha el hecho de que 0 es un valor falso.

var once = (function() { 
    var hasRun = 0; 
    return function() { 
    if (!hasRun) { 
     hasRun++; 

     // body to run only once 

     // log to the console for a test  
     console.log("only ran once"); 
    }    
    } 
})(); 

// test that the body of the function executes only once 
for (var i = 0; i < 5; i++) 
    once(); 
0

Además, la naturaleza de lo que sucede en el "hacer cosas */aquí * /" puede dejar algo en torno a que, cuando está presente, debe significar que la función se ejecute, por ejemplo,

var counter = null; 

function initCounter() { 
    if (counter === null) { 
    counter = 0; 
    } 
} 
8

Un problema con un buen número de estos enfoques es que dependen de nombres de funciones para trabajar: el enfoque de Mike fallará si se crea una función con "x = function() ..." y el enfoque de Lekensteyn lo harán fallar si establece x = useThisFunctionOnce antes de usar thisThisFunctionOnce.

yo recomendaría usar el enfoque de cierre de Russ si desea que se ejecute de inmediato o el enfoque adoptado por Underscore.js si desea retrasar la ejecución:

function once(func) { 
    var ran = false, memo; 
    return function() { 
     if (ran) return memo; 
     ran = true; 
     return memo = func.apply(this, arguments); 
    }; 
} 

var myFunction = once(function() { 
    return new Date().toString(); 
}); 

setInterval(function() {console.log(myFunction());}, 1000); 

En la primera ejecución, se ejecuta la función interior y la los resultados son devueltosEn ejecuciones posteriores, se devuelve el objeto de resultado original.

+0

He estado tratando de entender 'Underscore'' una vez' y estoy confundido. En la segunda llamada, ¿no se vuelve a 'ejecutar' y se inicializa a 'falso', por lo que se invoca' func.apply() 'por segunda vez? – seebiscuit

+0

@Seabiscuit una vez devuelve la función interna y 'run' se definió en la función externa. El resultado es un cierre donde todas las llamadas a la función ejecutan run y memo. –

+0

Imaginé que así fue como funcionó. Lo que todavía no entiendo es cómo (y aquí estoy admitiendo mi ignorancia de cómo funcionan los cierres en un nivel profundo) cuando se hace la llamada a 'una vez (func)' la asignación de variables en la función externa, es decir, 'ejecutó = false' no afecta el valor de 'ran' en la función' return'. Como cada llamada a 'once (func)' invoca las funciones externa e interna, entonces 'ran = false' se asigna cada vez que se llama a' once (func) '. – seebiscuit

1
(function(){ 

    var run = (function(){ 

    var func, blank = function() {}; 

    func = function() { 
     func = blank; 

     // following code executes only once 
     console.log('run once !'); 
    }; 

    return function(){ 
     func.call(); 
    }; 
    })(); 

    run(); 
    run(); 
    run(); 
    run(); 

})(); 
3

solución elegante de Douglas Crockford, pasó algún tiempo para entender cómo funciona y se topó con este hilo.

De modo que el wrapper devuelve una función que solo invoca la función del parámetro que aprobó. Y aprovechando los cierres, esta construcción reemplazó la función pasada a función vacía, o nula en la fuente original, después de la primera llamada, por lo que todas las próximas llamadas serán inútiles.

Esto es algo muy parecido a todas las otras respuestas, pero es un código que contiene un código que se puede usar de manera independiente, lo cual es bueno. Todavía estoy tratando de comprender todo el mecanismo de reemplazo, pero prácticamente funciona perfectamente.

function once (func) { 

return function() { 
    var f = func; 
    func = null; 
    return f.apply(this, arguments); 
}; 

} 

function hi(name) { 
    console.log("Hi %s", name); 
} 

sayonce = once(hi); 
sayonce("Vasya"); 
sayonce("Petya"); 

para aquellos que son curiosos aquí es jsbin transformaciones

1

Me acabo de encontrar con este problema, y ​​terminé haciendo algo como lo siguiente:

function runOnce() { 
    if (!this.alreadyRan) { 
     // put all your functionality here 
     console.log('running my function!'); 

     // set a property on the function itself to prevent it being run again 
     this.alreadyRan = true; 
    } 
} 

Esto se aprovecha del hecho de que Las propiedades de Javascript no están definidas de manera predeterminada.

Cuestiones relacionadas