Developers who come from famous OO languages such as Java/C++, like myself, may often find defining class in JavaScript confusing. This article aims to provide a very basic "class definition" in JavaScript.
Few notes on JavaScript nature
- there is no class actually, usual class concept in Java/C++ can hardly apply
- everything are basically object literal, simply key-value collection
- value of an object can be accessed by notation ".key" or "[key]"
Let's begin with a simple example below.
var eddie = { name: "Eddie", age: 25 };
var steven = { name: "Steven", age: 72 };
var benny = { name: "Benny", age: 3 };
1. Object Literal
Above objects eddie, steven, benny are all defined by object literal notation. It doesn't define any class, everything is raw but simple.
- you can access the fields (properties) in this object like this:
- eddie.name prints Eddie
- steven["age"] prints 72
- you can add new property dynamically
eddie.gender = 'M';
benny.gender = 'M';
- pros: simple, intuitive
- cons: not reusable. Imagine when you want to define function (e.g. getName()) among all persons. >
You need a way to define a class.
2. Constructor Function
2.1 class definition
var Person = function(name, age) {
this.name = name;
this.age = age;
this.getName = function() {
return this.age;
}
}
- Person is called constructor function
- constructor function will implicitly return this to caller
- use keyword new to invoke constructor function, as below
2.2 object creation
var eddie = new Person("Eddie", 25);
var steven = new Person("Steven", 72);
var benny = new Person("Benny", 3);
- new will allocate new memory, and this in constructor function will reference to the new memory
- "eddie.name" will print Eddie
- "benny.getName()" will print Benny
2.3 syntax
- var Person = function(name, age) { ... } is the equivalent to
- function Person(name, age) { ... }
2.4 memory concern
- note that memory of function getName() is allocated in every instance. To solve this, move the function from constructor function to prototype.
Person.prototype.getName = function() {
return this.name;
}
This way, only a single copy of getName would exist in memory.
- "benny.getName()" will still print Benny
2.5 incorrect object creation
var john = Person("John", 11);
- if "new" is missing (likely by mistake), you are NOT calling constructor function.
- "this" will reference to global context, window. Effectively, you are doing the following:
window.name = "Kalok";
window.age = 11;
- "typeof john" will return undefined, because "implicit return this" would not apply without "new"
3. That vs This
To address the issue "this" may refer to global context, here's a possible solution.
var Person = function(name, age) {
// define an empty object literal
var that = {};
// assign to that instead of this
that.name = name;
that.age = age;
// return that explicit
return that;
}
- "that" is just a dummy word, use "self", "_this" or whatever meaningful to you
- the disadvantage of this solution is it loses the link to prototype, not good enough in most cases.
4. Self-invoking Constructor
Now we go back to the class definition at the beginning, and check whether
this is an instance of the constructor itself.
var Person = function(name, age) {
if (!this instanceof Person)) {
return new Person(name, age);
}
this.name = name;
this.age = age;
this.getName = function() {
return this.age;
}
}