views:

291

answers:

5

Is there a way I can run piece of javascript code ONCE without the use of boolean flag variables to know that it already ran?

Specifically not something like:

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

  /* do stuff here */

}

I'm going to have a lot of these types of functions and keeping all the booleans around would be messy...

+37  A: 

Just an alternative.

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();

'Useful' example:

var preferences = {};
function read_preferences(){
   // we do not want to think, just destroy itself
   read_preferences = Function("");
   // do some nasty things with cookies
}
function readPreference(pref_name){
    read_prefences();
    return pref_name in preferences ? preferences[pref_name] : '';
}
if(readPreference('like_javascript') != 'yes'){
   alert("What's wrong wth you?!");
}
alert(readPreference('is_stupid') ? "Stupid!" : ":)");

Edit: as CMS pointed out, just overwriting the old function with function(){} will create a closure in which old variables still exist. To work around that problem, I replaced it with Function(""). This will create a empty function in the global scope, leaving the old function scope.

Lekensteyn
+1 For a clever solution!
Shakedown
+1 indeed! Putting a new meaning to "this function will self-destruct after first use".
EboMike
very neat though I fail to see the value of this. though that's not your fault +1
mcgrailm
+1 for understanding the question
gawi
+1, very clever solution.
Josh K
I've added an example for mcgrailm :)
Lekensteyn
Come on... really?
Dan
I've given you an answer on your question "Run a code once without using booleans". If this does not answer your question, please provide a code example with your current situation, the problem and the desired situation.
Lekensteyn
Well... that looks really complicated.
Dan
yeah +1, thats pretty funny.
Joshua Evensen
you can use static variable and assign it a value and then test againts it, but if you want to have no booleans... i would rather say that you are complicating you way ;]
Tomasz Kowalczyk
I was thinking while loops
Dan
Really...? I'm not an expert like you...
Dan
Dan, what kind of loops? You can use `break;` or `return;` (in functions) to get out of a loop.
Lekensteyn
+1 - For a solution that is far better than something I would have come up with.
James Black
Answering on 'that looks complicated': the idea behind this is overwriting the function with an empty function. Thus, calling the function `useThisFunctionOnce` will execute the original code the first time. The next time `useThisFunctionOnce` is called, it executes an empty function; the original code does not exist anymore. Defining a function with `function funcName(){}` is the same as `var funcName=function(){};`.
Lekensteyn
@Lekensteyn - My understanding was that the variable gets assigned `undefined` first, then to the named function, as in - `var funcName = undefined; funcName = function funcName(){};`
Russ Cam
God I *love* this language.
Alexandre C.
This is how GWT handles one-time inits. It works for functions with a global name which don't have other references to them.
levik
@Russ Cam, If I run `var a=function(){};alert(a)`, I get `function () {}`. `var a=function b(){};alert(a+';'+typeof b);` yields `function b() {};undefined` (`b` is undefined). (tested in Firefox and Chrome)
Lekensteyn
b would be undefined as it shouldn't leak out of the assignment to a (unlike in IE where it does leak out)
Russ Cam
I would *strongly discourage* the usage of this pattern, because *all* variables, arguments, and inner FunctionDeclarations of the first function, will be **never** be garbage collected, this might not be obvious, but it's true, the empty function is created within the first one, this creates a closure, and all identifiers will remain *live*, creating a memory leak. I would recommend either, declare the empty function outside, or use the Function constructor, e.g. `useThisFunctionOnce = Function("");`. Try [this test](http://j.mp/aVlAiX) to see how effectively all variables remain in-scope.
CMS
Good point CMS, I updated my answer.
Lekensteyn
A: 

If not bound to an event, code is usually ran once

Zlatev
And what about loops?
Daff
simple enough, considering the question.
Zlatev
+5  A: 

I like Lekensteyn's implementation, but you could also just have one variable to store what functions have run. The code below should run "runOnce", and "runAgain" both one time. It's still booleans, but it sounds like you just don't want lots of 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();
Mike Robinson
(Note: I still like Lekensteyn's implementation better)
Mike Robinson
Three lines for parsing the function name is kinda heavy. I recommend a simple regexp solution: `functionName = func.toString().match(/\w+(?=\()/)[0]`
bennedich
@Mike Robinson - Why is there two function definitions for `runAgain()`?
irishbuzz
@irish - Because ... umm.... extra reliability? :) (Removed one of them)
Mike Robinson
@bennedich - I like the idea of a regex, but the one you supplied doesn't work, and returns "function" when I fix it
Mike Robinson
@Mike That's fair enough. I was actually unsure whether it was somehow part of the trickery - wasn't trying to be a smart ass ;-)
irishbuzz
+1  A: 

What about a self-invoking anonymous function?

(function () {

    // code in here to run once

})();

the code will execute immediately and leave no trace in the global namespace.

If this code is going to need to be called from elsewhere, then a closure can be used to ensure that the contents of a function are run only once. Personally, I prefer this to a function that rewrites itself as I feel doing so can cause confusion, but to each their own :) This particular implementation takes advantage of the fact that 0 is a falsy value.

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();
Russ Cam
A: 

In addition, the nature of what happens in the "/* do stuff here */" may leave something around that, when present, must mean that the function has run e.g.

var counter = null;

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