Static variables in JavaScript

If you come from a class-based, statically typed object-oriented language (like Java, C++ or C#) I assume that you are trying to create a variable or method associated to a "type" but not to an instance.

An example using a "classical" approach, with constructor functions maybe could help you to catch the concepts of basic OO JavaScript:

function MyClass () { // constructor function
  var privateVariable = "foo";  // Private variable 

  this.publicVariable = "bar";  // Public variable 

  this.privilegedMethod = function () {  // Public Method
    alert(privateVariable);
  };
}

// Instance method will be available to all instances but only load once in memory 
MyClass.prototype.publicMethod = function () {    
  alert(this.publicVariable);
};

// Static variable shared by all instances
MyClass.staticProperty = "baz";

var myInstance = new MyClass();

staticProperty is defined in the MyClass object (which is a function) and has nothing to do with its created instances, JavaScript treats functions as first-class objects, so being an object, you can assign properties to a function.

UPDATE: ES6 introduced the ability to declare classes through the class keyword. It is syntax sugar over the existing prototype-based inheritance.

The static keyword allows you to easily define static properties or methods in a class.

Let's see the above example implemented with ES6 classes:

class MyClass {
  // class constructor, equivalent to
  // the function body of a constructor
  constructor() {
    const privateVariable = 'private value'; // Private variable at the constructor scope
    this.publicVariable = 'public value'; // Public property

    this.privilegedMethod = function() {
      // Public Method with access to the constructor scope variables
      console.log(privateVariable);
    };
  }

  // Prototype methods:
  publicMethod() {
    console.log(this.publicVariable);
  }

  // Static properties shared by all instances
  static staticProperty = 'static value';

  static staticMethod() {
    console.log(this.staticProperty);
  }
}

// We can add properties to the class prototype
MyClass.prototype.additionalMethod = function() {
  console.log(this.publicVariable);
};

var myInstance = new MyClass();
myInstance.publicMethod();       // "public value"
myInstance.additionalMethod(); // "public value"
myInstance.privilegedMethod(); // "private value"
MyClass.staticMethod();             // "static value"

You might take advantage of the fact that JS functions are also objects -- which means they can have properties.

For instance, quoting the example given on the (now vanished) article Static variables in Javascript:

function countMyself() {
    // Check to see if the counter has been initialized
    if ( typeof countMyself.counter == 'undefined' ) {
        // It has not... perform the initialization
        countMyself.counter = 0;
    }

    // Do something stupid to indicate the value
    alert(++countMyself.counter);
}

If you call that function several time, you'll see the counter is being incremented.

And this is probably a much better solution than poluting the global namespace with a global variable.


And here is another possible solution, based on a closure : Trick to use static variables in javascript :

var uniqueID = (function() {
   var id = 0; // This is the private persistent value
   // The outer function returns a nested function that has access
   // to the persistent value.  It is this nested function we're storing
   // in the variable uniqueID above.
   return function() { return id++; };  // Return and increment
})(); // Invoke the outer function after defining it.

Which gets you the same kind of result -- except, this time, the incremented value is returned, instead of displayed.


You do it through an IIFE (immediately invoked function expression):

var incr = (function () {
    var i = 1;

    return function () {
        return i++;
    }
})();

incr(); // returns 1
incr(); // returns 2

I've seen a couple of similar answers, but I'd like to mention that this post describes it best, so I'd like to share it with you.

Here's some code taken from it, which I have modified to get a complete example which hopefully gives benefit to the community because it can be used as a design template for classes.

It also answers your question:

function Podcast() {

    // private variables
    var _somePrivateVariable = 123;

    // object properties (read/write)
    this.title = 'Astronomy Cast';
    this.description = 'A fact-based journey through the galaxy.';
    this.link = 'http://www.astronomycast.com';

    // for read access to _somePrivateVariable via immutableProp 
    this.immutableProp = function() {
        return _somePrivateVariable;
    }

    // object function
    this.toString = function() {
       return 'Title: ' + this.title;
    }
};

// static property
Podcast.FILE_EXTENSION = 'mp3';
// static function
Podcast.download = function(podcast) {
    console.log('Downloading ' + podcast + ' ...');
};

Given that example, you can access the static properties/function as follows:

// access static properties/functions
console.log(Podcast.FILE_EXTENSION);   // 'mp3'
Podcast.download('Astronomy cast');    // 'Downloading Astronomy cast ...'

And the object properties/functions simply as:

// access object properties/functions
var podcast = new Podcast();
podcast.title = 'The Simpsons';
console.log(podcast.toString());       // Title: The Simpsons
console.log(podcast.immutableProp());  // 123

Note that in podcast.immutableProp(), we have a closure: The reference to _somePrivateVariable is kept inside the function.

You can even define getters and setters. Take a look at this code snippet (where d is the object's prototype for which you want to declare a property, y is a private variable not visible outside of the constructor):

// getters and setters
var d = Date.prototype;
Object.defineProperty(d, "year", {
    get: function() {return this.getFullYear() },
    set: function(y) { this.setFullYear(y) }
});

It defines the property d.year via get and set functions - if you don't specify set, then the property is read-only and cannot be modified (be aware you will not get an error if you try to set it, but it has no effect). Each property has the attributes writable, configurable (allow to change after declaration) and enumerable (allow to use it as enumerator), which are per default false. You can set them via defineProperty in the 3rd parameter, e.g. enumerable: true.

What is also valid is this syntax:

// getters and setters - alternative syntax
var obj = { a: 7, 
            get b() {return this.a + 1;}, 
            set c(x) {this.a = x / 2}
        };

which defines a readable/writable property a, a readonly property b and a write-only property c, through which property a can be accessed.

Usage:

console.log(obj.a); console.log(obj.b); // output: 7, 8
obj.c=40;
console.log(obj.a); console.log(obj.b); // output: 20, 21

Notes:

To avoid unexpected behaviour in case you've forgotten the new keyword, I suggest that you add the following to the function Podcast:

// instantiation helper
function Podcast() {
    if(false === (this instanceof Podcast)) {
        return new Podcast();
    }
// [... same as above ...]
};

Now both of the following instantiations will work as expected:

var podcast = new Podcast(); // normal usage, still allowed
var podcast = Podcast();     // you can omit the new keyword because of the helper

The 'new' statement creates a new object and copies all properties and methods, i.e.

var a=new Podcast();
var b=new Podcast();
a.title="a"; b.title="An "+b.title;
console.log(a.title); // "a"
console.log(b.title); // "An Astronomy Cast"

Note also, that in some situations it can be useful to use the return statement in the constructor function Podcast to return a custom object protecting functions the class internally relies on but which need to be exposed. This is explained further in chapter 2 (Objects) of the article series.

You can say that a and b inherit from Podcast. Now, what if you want to add a method to Podcast that applies to all of them after a and b have been instanciated? In this case, use the .prototype as follows:

Podcast.prototype.titleAndLink = function() {
    return this.title + " [" + this.link + "]";
};

Now call a and b again:

console.log(a.titleAndLink()); // "a [http://www.astronomycast.com]"
console.log(b.titleAndLink()); // "An Astronomy Cast [http://www.astronomycast.com]"

You can find more details about prototypes here. If you want to do more inheritance, I suggest looking into this.


The article series I've mentioned above are highly recommended to read, they include also the following topics:

  1. Functions
  2. Objects
  3. Prototypes
  4. Enforcing New on Constructor Functions
  5. Hoisting
  6. Automatic Semicolon Insertion
  7. Static Properties and Methods

Note that the automatic semicolon insertion "feature" of JavaScript (as mentioned in 6.) is very often responsible for causing strange issues in your code. Hence, I would rather regard it as a bug than as a feature.

If you want to read more, here is a quite interesting MSDN article about these topics, some of them described there provide even more details.

What is interesting to read as well (also covering the topics mentioned above) are those articles from the MDN JavaScript Guide:

  • A re-introduction to JavaScript
  • Working with Objects

If you want to know how to emulate c# out parameters (like in DateTime.TryParse(str, out result)) in JavaScript, you can find sample code here.


Those of you who are working with IE (which has no console for JavaScript unless you open the developer tools using F12 and open the console tab) might find the following snippet useful. It allows you to use console.log(msg); as used in the examples above. Just insert it before the Podcast function.

For your convenience, here's the code above in one complete single code snippet:

let console = { log: function(msg) {  
  let canvas = document.getElementById("log"), br = canvas.innerHTML==="" ? "" : "<br/>";
  canvas.innerHTML += (br + (msg || "").toString());
}};

console.log('For details, see the explaining text');

function Podcast() {

  // with this, you can instantiate without new (see description in text)
  if (false === (this instanceof Podcast)) {
    return new Podcast();
  }

  // private variables
  var _somePrivateVariable = 123;

  // object properties
  this.title = 'Astronomy Cast';
  this.description = 'A fact-based journey through the galaxy.';
  this.link = 'http://www.astronomycast.com';

  this.immutableProp = function() {
    return _somePrivateVariable;
  }

  // object function
  this.toString = function() {
    return 'Title: ' + this.title;
  }
};

// static property
Podcast.FILE_EXTENSION = 'mp3';
// static function
Podcast.download = function(podcast) {
  console.log('Downloading ' + podcast + ' ...');
};


// access static properties/functions
Podcast.FILE_EXTENSION; // 'mp3'
Podcast.download('Astronomy cast'); // 'Downloading Astronomy cast ...'

// access object properties/functions
var podcast = new Podcast();
podcast.title = 'The Simpsons';
console.log(podcast.toString()); // Title: The Simpsons
console.log(podcast.immutableProp()); // 123

// getters and setters
var d = Date.prototype;
Object.defineProperty(d, "year", {
  get: function() {
    return this.getFullYear()
  },
  set: function(y) {
    this.setFullYear(y)
  }
});

// getters and setters - alternative syntax
var obj = {
  a: 7,
  get b() {
    return this.a + 1;
  },
  set c(x) {
    this.a = x / 2
  }
};

// usage:
console.log(obj.a); console.log(obj.b); // output: 7, 8
obj.c=40;
console.log(obj.a); console.log(obj.b); // output: 20, 21

var a=new Podcast();
var b=new Podcast();
a.title="a"; b.title="An "+b.title;
console.log(a.title); // "a"
console.log(b.title); // "An Astronomy Cast"

Podcast.prototype.titleAndLink = function() {
    return this.title + " [" + this.link + "]";
};
    
console.log(a.titleAndLink()); // "a [http://www.astronomycast.com]"
console.log(b.titleAndLink()); // "An Astronomy Cast [http://www.astronomycast.com]"
<div id="log"></div>

Notes:

  • Some good tips, hints and recommendations about JavaScript programming in general you can find here (JavaScript best practices) and there ('var' versus 'let'). Also recommended is this article about implicit typecasts (coercion).

  • A convenient way to use classes and compile them into JavaScript is TypeScript. Here is a playground where you can find some examples showing you how it works. Even if you're not using TypeScript at the moment, you can have a look because you can compare TypeScript with the JavaScript result on a side-by-side view. Most examples are simple, but there is also a Raytracer example which you can try out instantly. I recommend especially looking into the "Using Classes", "Using Inheritance" and "Using Generics" examples by selecting them in the combobox - these are nice templates you can instantly use in JavaScript. Typescript is used with Angular.

  • To achieve encapsulation of local variables, functions etc in JavaScript, I suggest to use a pattern like the following (JQuery uses the same technique):

<html>
<head></head>
<body><script>
    'use strict';
    // module pattern (self invoked function)
    const myModule = (function(context) { 
    // to allow replacement of the function, use 'var' otherwise keep 'const'

      // put variables and function with local module scope here:
      var print = function(str) {
        if (str !== undefined) context.document.write(str);
        context.document.write("<br/><br/>");
        return;
      }
      // ... more variables ...

      // main method
      var _main = function(title) {

        if (title !== undefined) print(title);
        print("<b>last modified:&nbsp;</b>" + context.document.lastModified + "<br/>");        
        // ... more code ...
      }

      // public methods
      return {
        Main: _main
        // ... more public methods, properties ...
      };

    })(this);

    // use module
    myModule.Main("<b>Module demo</b>");
</script></body>
</html>

Of course, you can - and should - put the script code in a separate *.js file; this is just written inline to keep the example short.

Self-invocing functions (also known as IIFE = Immediately Invoked Function Expression) are described in more detail here.


you can use arguments.callee to store "static" variables (this is useful in anonymous function too):

function () {
  arguments.callee.myStaticVar = arguments.callee.myStaticVar || 1;
  arguments.callee.myStaticVar++;
  alert(arguments.callee.myStaticVar);
}