[javascript] Static variables in JavaScript

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:

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:

_x000D_
_x000D_
let console = { log: function(msg) {  _x000D_
  let canvas = document.getElementById("log"), br = canvas.innerHTML==="" ? "" : "<br/>";_x000D_
  canvas.innerHTML += (br + (msg || "").toString());_x000D_
}};_x000D_
_x000D_
console.log('For details, see the explaining text');_x000D_
_x000D_
function Podcast() {_x000D_
_x000D_
  // with this, you can instantiate without new (see description in text)_x000D_
  if (false === (this instanceof Podcast)) {_x000D_
    return new Podcast();_x000D_
  }_x000D_
_x000D_
  // private variables_x000D_
  var _somePrivateVariable = 123;_x000D_
_x000D_
  // object properties_x000D_
  this.title = 'Astronomy Cast';_x000D_
  this.description = 'A fact-based journey through the galaxy.';_x000D_
  this.link = 'http://www.astronomycast.com';_x000D_
_x000D_
  this.immutableProp = function() {_x000D_
    return _somePrivateVariable;_x000D_
  }_x000D_
_x000D_
  // object function_x000D_
  this.toString = function() {_x000D_
    return 'Title: ' + this.title;_x000D_
  }_x000D_
};_x000D_
_x000D_
// static property_x000D_
Podcast.FILE_EXTENSION = 'mp3';_x000D_
// static function_x000D_
Podcast.download = function(podcast) {_x000D_
  console.log('Downloading ' + podcast + ' ...');_x000D_
};_x000D_
_x000D_
_x000D_
// access static properties/functions_x000D_
Podcast.FILE_EXTENSION; // 'mp3'_x000D_
Podcast.download('Astronomy cast'); // 'Downloading Astronomy cast ...'_x000D_
_x000D_
// access object properties/functions_x000D_
var podcast = new Podcast();_x000D_
podcast.title = 'The Simpsons';_x000D_
console.log(podcast.toString()); // Title: The Simpsons_x000D_
console.log(podcast.immutableProp()); // 123_x000D_
_x000D_
// getters and setters_x000D_
var d = Date.prototype;_x000D_
Object.defineProperty(d, "year", {_x000D_
  get: function() {_x000D_
    return this.getFullYear()_x000D_
  },_x000D_
  set: function(y) {_x000D_
    this.setFullYear(y)_x000D_
  }_x000D_
});_x000D_
_x000D_
// getters and setters - alternative syntax_x000D_
var obj = {_x000D_
  a: 7,_x000D_
  get b() {_x000D_
    return this.a + 1;_x000D_
  },_x000D_
  set c(x) {_x000D_
    this.a = x / 2_x000D_
  }_x000D_
};_x000D_
_x000D_
// usage:_x000D_
console.log(obj.a); console.log(obj.b); // output: 7, 8_x000D_
obj.c=40;_x000D_
console.log(obj.a); console.log(obj.b); // output: 20, 21_x000D_
_x000D_
var a=new Podcast();_x000D_
var b=new Podcast();_x000D_
a.title="a"; b.title="An "+b.title;_x000D_
console.log(a.title); // "a"_x000D_
console.log(b.title); // "An Astronomy Cast"_x000D_
_x000D_
Podcast.prototype.titleAndLink = function() {_x000D_
    return this.title + " [" + this.link + "]";_x000D_
};_x000D_
    _x000D_
console.log(a.titleAndLink()); // "a [http://www.astronomycast.com]"_x000D_
console.log(b.titleAndLink()); // "An Astronomy Cast [http://www.astronomycast.com]"
_x000D_
<div id="log"></div>
_x000D_
_x000D_
_x000D_


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):

_x000D_
_x000D_
<html>_x000D_
<head></head>_x000D_
<body><script>_x000D_
    'use strict';_x000D_
    // module pattern (self invoked function)_x000D_
    const myModule = (function(context) { _x000D_
    // to allow replacement of the function, use 'var' otherwise keep 'const'_x000D_
_x000D_
      // put variables and function with local module scope here:_x000D_
      var print = function(str) {_x000D_
        if (str !== undefined) context.document.write(str);_x000D_
        context.document.write("<br/><br/>");_x000D_
        return;_x000D_
      }_x000D_
      // ... more variables ..._x000D_
_x000D_
      // main method_x000D_
      var _main = function(title) {_x000D_
_x000D_
        if (title !== undefined) print(title);_x000D_
        print("<b>last modified:&nbsp;</b>" + context.document.lastModified + "<br/>");        _x000D_
        // ... more code ..._x000D_
      }_x000D_
_x000D_
      // public methods_x000D_
      return {_x000D_
        Main: _main_x000D_
        // ... more public methods, properties ..._x000D_
      };_x000D_
_x000D_
    })(this);_x000D_
_x000D_
    // use module_x000D_
    myModule.Main("<b>Module demo</b>");_x000D_
</script></body>_x000D_
</html>
_x000D_
_x000D_
_x000D_

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.

Examples related to javascript

need to add a class to an element How to make a variable accessible outside a function? Hide Signs that Meteor.js was Used How to create a showdown.js markdown extension Please help me convert this script to a simple image slider Highlight Anchor Links when user manually scrolls? Summing radio input values How to execute an action before close metro app WinJS javascript, for loop defines a dynamic variable name Getting all files in directory with ajax

Examples related to variables

When to create variables (memory management) How to print a Groovy variable in Jenkins? What does ${} (dollar sign and curly braces) mean in a string in Javascript? How to access global variables How to initialize a variable of date type in java? How to define a variable in a Dockerfile? Why does foo = filter(...) return a <filter object>, not a list? How can I pass variable to ansible playbook in the command line? How do I use this JavaScript variable in HTML? Static vs class functions/variables in Swift classes?

Examples related to static

What is the equivalent of Java static methods in Kotlin? Creating a static class with no instances Static vs class functions/variables in Swift classes? Call static methods from regular ES6 class methods What is the difference between static func and class func in Swift? An object reference is required to access a non-static member Mocking static methods with Mockito @Autowired and static method The static keyword and its various uses in C++ Non-Static method cannot be referenced from a static context with methods and variables

Examples related to closures

Store a closure as a variable in Swift How to map to multiple elements with Java 8 streams? groovy: safely find a key in a map and return its value Exception: Serialization of 'Closure' is not allowed JavaScript closures vs. anonymous functions Don't understand why UnboundLocalError occurs (closure) Closure in Java 7 How should I call 3 functions in order to execute them one after the other? Why aren't python nested functions called closures? Passing parameters in Javascript onClick event