How can I create static variables in Javascript?
In addition to the rest, there's currently a draft (stage-2 proposal) on ECMA Proposals that introduces static
public fields in classes. (private fields were considered)
Using the example from the proposal, the proposed static
syntax will look like this:
class CustomDate {
// ...
static epoch = new CustomDate(0);
}
and be equivalent to the following which others have highlighted:
class CustomDate {
// ...
}
CustomDate.epoch = new CustomDate(0);
You can then access it via CustomDate.epoch
.
You can keep track of the new proposal in proposal-static-class-features.
Currently, babel supports this feature with the transform class properties plugin which you can use. Additionally, though still in progress, V8 is implementing it.
I used the prototype and that way it worked:
class Cat extends Anima {
constructor() {
super(Cat.COLLECTION_NAME);
}
}
Cat.COLLECTION_NAME = "cats";
or using an static getter:
class Cat extends Anima {
constructor() {
super(Cat.COLLECTION_NAME);
}
static get COLLECTION_NAME() {
return "cats"
}
}
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);
}
Window level vars are sorta like statics in the sense that you can use direct reference and these are available to all parts of your app
If you are using the new class syntax then you can now do the following:
class MyClass {
static get myStaticVariable() {
return "some static variable";
}
}
console.log(MyClass.myStaticVariable);
aMyClass = new MyClass();
console.log(aMyClass.myStaticVariable, "is undefined");
This effectively creates a static variable 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:
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:
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: </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.