x = 5;
var x;
console.log(x) //5
plus5(5);
function plus5(a) {
console.log(a+" plus 5 is "+(a+5)); //5 plus 5 is 10
}
Hmm, looks okay doesn’t it? Hang on, the variable x is being initialized before it is declared. Yes, that is because the interpreter has placed the declaration to the top of the file for you. You don’t see this happening it is just magic. The same goes for functions as well. Here the function plus5 is called before it is defined. Spooky.
var a = 5; //Initialize a
var result;
c=2; // Initialization causes declaration, no hoisting
//Display a and b and c, b at this point is undefined because it
//is initialized on the next line
result = a+" "+b+" "+c;
var b = 7; // Initialize b
console.log(result); // 5 undefined 2
In the example above b is undefined when result is initialized. This is because only the declaration is hoisted and not the assignment. This is the same as writing
var a,b; //Declare a and b
a=5; //Initialize a
var result; //Declare result
result=a+" "+b; //Display a and b,b is undefined at this point
b = 7; //Initialize b
console.log(result); //5 undefined
This behaviour can lead to unexpected behaviour in your code.
If you use these ES6 keywords then you do not need to be concerned about hoisting. If it doubt then ditch var and use const or let instead.
Creating classes using Javascript is a strange affair with multiple exotic constructs, which adds up to a lot of confusion especially for novice coders. Now with the advent of ES6 finally we have a way to define and create class hierachies in an understandable way. It still falls a little short though. More of that later.
If you want to experiment with any of these patterns then you can use the repl.it Javascript sandbox straight from your browser(clicking the link opens a new tab). So without further ado let’s dig in.
The old way(s)
I will go through these patterns in turn, feel free to skip but I think it’s useful to become familiar as you will most likely come across some if not all of these at some point in your coding career.
This example uses object notation using properties. Note that a property can define a variable or a function. Remember Javascript objects are nothing more than bags of properties at the end of the day!
This is possibly the most commonly used way of creating Javascript objects. It uses a special function prototype object to define the class. Over to Mr Crockford for an explanation…
JavaScript is a prototypal inheritance language. That means that objects can inherit properties directly from other objects. The language is class-free.
This is a radical departure from the current fashion. Most languages today are classical. Prototypal inheritance is powerfully expressive, but is not widely understood. JavaScript itself is not confident in its prototypal nature, so it offers an object-making syntax that is reminiscent of the classical languages. Few classical programmers found prototypal inheritance to be acceptable, and classically inspired syntax obscures the language’s true prototypal nature. It is the worst of both worlds.
If a function is invoked with the new prefix, then a new object will be created with a hidden link to the value of the function’s prototype member, and this will be bound to that new object.
Douglas Crockford – Javascript: The Good Parts
Here is our Car class created using this pattern. Firstly the Car function is declared. Then each class function is attached to the prototype. Calling new will attach the prototype to the Car function, allowing our class members be be accessed within the show and _showDetails functions.
var car = new Car('Escort RS2000', 'Ford');
console.log(car.name); //Escort RS2000
console.log(car.make); //Ford
console.log(car.show()); //Ford-Escort RS2000 goes Beep!
console.log(car instanceof Car); //true
This way has the advantage of being able to do proper type checking which is always useful. This actually how ES6 classes work under the hood using a sprinkle of magic syntactic sugar!
So why would you use this construct? Firstly, you can assign extra options called property descriptors when defining the property. This is shown in the name property above. The bigger draw is that it allows you to build class hierarchies using prototype linking.
Consider the following example.
// Declare a `Car` object
function Car(name, make) {
this.name = name;
this.make = make;
this.sound = 'Beep!';
}
Car.prototype.show = function() {
return this._showDetails();
};
Car.prototype._showDetails = function() {
return `${this.make}-${this.name} goes ${this.sound}`;
};
// Use the `Car` object to create an `Electric` car. First we define the prototype
// function, remembering to call the `Car` constructor
function ElectricCar(name,make) {
Car.call(this,name,make); // call super constructor.
this.sound = 'Laser blast!'; // override the sound prop
}
// Now we use `Object.create` to extend the `Car` object by binding to its prototype
ElectricCar.prototype = Object.create(Car.prototype);
var elec = new ElectricCar('X', 'Tesla');
console.log(elec.name); //X
console.log(elec.make); //Tesla
console.log(elec.show()); //Tesla-X goes Laser blast!
Way 4. ES6 Classes
class Car {
constructor(_name,_make) {
this.name = _name;
this.make = _make;
}
// Getter
// get keyword allows you to define custom properties that can
// be accessed using dot notation
get show() {
return this._showDetails();
}
_showDetails = function() {
return `${this.make}-${this.name} goes ${this.sound}`;
}
get sound() {
return 'Beep!';
}
}
We have created a class using the class keyword. Each class has a constructor function.
Now we can create a new Car class using the new keyword. Note the special name property which returns the name of the class.
let car = new Car('Escort RS2000', 'Ford');
console.log(car.name); //Escort RS2000
console.log(car.show); //Ford-Escort RS2000 goes Beep!
console.log(Car.name); //Car
console.log(car instanceof Car); //true
Having created the class we can easily implement inheritance using the extends keyword.
class Truck extends Car {
constructor(_name, _make) {
super(_name, _make);
}
get sound() {
return 'HONK!';
}
}
Remember to call the base class constructor using the super keyword. In this example the sound property has been overridden to return ‘HONK!’
let truck = new Truck('SCANIA', 'R480');
console.log(truck.show); //SCANIA-R480 goes HONK!
console.log(truck instanceof Car); true
console.log(truck instanceof Truck); true
It’s interesting to mention again that under the hood, constructor invocation is used using prototype linking. This new way of defining a class is syntactic sugar used to simplify the creation of classes. One thing is missing however, This class has a private function denoted by the underscore. But there is nothing to stop you doing this:
truck._showDetails();//SCANIA-R480 goes HONK!
There are plans afoot to add the ability to define private functions using the # prefix as mentioned here but at present it has limited browser support.