JavaScript Objects, Functions aka Classes In ES5

Sep 20, 2016
>>

JavaScript has five primitive data types: string, number, boolean, null and undefined. And operators like, +, -, typeof, instanceof etc. Other than these, everything else are objects. Even functions are objects, can be created by invoking by Function constructor and typeof on a function returns function.

// new Function([arg1[, arg2[, ...argN]],] functionBody)
var animal = new Function('legs', 'this.legs = legs;');

// check the type
console.log(typeof animal); // function

// create an object
var a = new animal(4);

// and check if the value has been assigned or not
console.log(a.legs); // 4

JavaScript interpreter does not enforce it, but it’s a common convention, the name of the functions, which are not intended to be used as classes, starts with a small letter and follows the conventions of camelCase.

Functions in JavaScript either used as a block of codes, to be executed upon call or create objects with properties assigned to the function or to its prototype.

Though operators seems to work like function, but by nature they are special kind of entity, which can’t be assigned and always needs operand(s) to operate on. That means, we can’t write var o = +;.

Objects can be created by two ways.

// by object literal
var obj = {};

// by using constructor functions
var classObj = new Animal(4);

JavaScript objects are basically representations of data-structure of key-value pair. And they can be iterated. And in modern browsers we don’t need to check with .hasOwnProperty as with the old browsers. Now we can simply do by for...in loop.

// create the object
var obj = {
name: "Simon",
age: "20",
clothing: {
style: "simple",
hipster: false
}
}
// iterate over the properties of the objects
for(var propt in obj){
if (obj.hasOwnProperty(propt)) {
console.log(propt + ': ' + obj[propt]);
}
}

We can add properties to objects by two methods. Either declare properties at the time of creating the objects as the previous example or later assign by value-of notation.

// use the string literals directly
obj['literal'] = 'literal value';

// or dynamically set the property by variables
var property = 'dynamicProperty';
obj[property] = 'dynamic property value';

To delete a property from an object we can use delete keyword.

// directly delete the property if known
delete obj.name;

// dynamically assign property name to delete
var toDeleteKey = 'age';
delete obj[toDeleteKey];

console.log(JSON.stringify(obj)); // {"clothing":{"style":"simple","hipster":false}}

By default type of any object, created programmatically is object and parent is undefined.

var obj = {};
console.log(typeof obj); // object

Prior to ES-2015, there was no notion of classes in JavaScript, though class was a reserved keyword. So, until ES-2015, generally classes were reprsented as functions. And with functions, we can even derive one class into another, using the prototypal inheritance of JavaScript. There are several versions of deriving one class into another, and we can use one of these ways, as per the requirement and choice.

// declare class - 'Animal'
function Animal () {
this.legs = 4;
}
Animal.prototype.legs = 8;
Animal.prototype.move = function () {
console.log('moving...');
};

// declare class - 'Tiger'
function Tiger () {
this.class = 'mammal';
}

Unlike other programming languages, in JavaScript inheritance assigned after creation of class, through prototype. Here we will discuss various methods of deriving classes.

// assign a new object to the prototype of 'Tiger'
Tiger.prototype = new Animal();

// create a new object of type tiger
var t = new Tiger();

console.log(t.legs); // 4

console.log(t.class); // 'mammal'

console.log(t.move()); // moving...

All things are fine and all the properties present in the prototype along with the properties declared in direct closure of the super class has been derived to child class Tiger. Programmatically and in using this type of construct don’t cause any problems.

However, using new with a prototypal language like JavaScript, seems like going against the tide. And in this historic document, Crockford agrees to the fact, that JavaScript lacks the mechanism to inherit directly from another object. Instead it uses function along with new keyword.

This indirection was intended to make the language seem more familiar to classically trained programmers, but failed to do that, as we can see from the very low opinion Java programmers have of JavaScript. JavaScript’s constructor pattern did not appeal to the classical crowd. It also obscured JavaScript’s true prototypal nature. As a result, there are very few programmers who know how to use the language effectively.

Using new keyword, actually causes a little deviation from being a pure Prototypal Language. In modern browsers, there is a method called create in the Object prototype. This tries to fulfill the promise of a pure prototypal language.

So, our previous example can be formatted as,

// declare Animal class
function Animal () {
this.legs = 4;
}
Animal.prototype.legs = 8;

// declare Tiger class
function Tiger () {
this.class = 'mammal';
}

// create a new instance of Animal.prototype and
// assign it to the Tiger.prototype
Tiger.prototype = Object.create(Animal.prototype);

// explicitly assign Tiger as the prototype's constructor
Tiger.prototype.constructor = Tiger;

// create instance of Tiger
var t = new Tiger();
console.log(t.legs); // 8
console.log(t.class); // mammal

So, it didn’t call the constructor of the Animal class. We need to do that explicitly inside the Tiger constructor.

function Tiger () {
Animal.call(this); // call Animal constructor
this.class = 'mammal';
}
var t = new Tiger();
console.log(t.legs); // 4

And if we are more into writing prototypically pure code, we should not even use functions as classes. Observe how the properties are created here. We can also control accesses of the properties. More details.

// create Animal class object creator
function createAnimal () {
return Object.create(Object.prototype, {
legs: {
value: 4
},
move: {
value: function () {
console.log('moving...');
}
}
});
}

// create Tiger class object creator
function createTiger () {
return Object.create(createAnimal(), {
class: {
value: 'mammal'
},
move: {
value: function () {
console.log('moving in stealth mode...');
}
}
});
}

// create instance of Tiger class
var t = createTiger();

console.log(t.legs); // 4
console.log(t.class); // mammal
t.move(); // moving in stealth mode...

One benefit of using prototypal language is, it’s possible to use delegation instead of inheriting another class in order to re-use code.

What is delegation? Let’s understand that by an example.

Suppose we have two classes Mammal and Reptile. By the genetic inheritance, we know that, there are almost no similarity between these two species. As designing classes means abstraction of real behavior, suppose for the sake of the program we have identified that, the movement of both of the classes are same. And some super enthusiastic developer has already implemented the move function for the mammals.

What can we do now. Derive the Mammal into Reptile, how odd it may look or sound. Or repeat the same method in Reptile. But that will introduce redundant code. Or move out the function out of Mammal and put in some Utility object and use from both the classes as method call.

As a prototypal language JavaScript gives another possibility. Delegation.

We can directly use the move function with an object of Reptile class by the use of call or apply available in Function prototype.

// create one object of reptile
var r = new Reptile();

// use call or apply to invoke the function 'move'
Mammal.move.apply(r, arguments);

Only constraint would be, Reptile class should contain all the properties, required by the move method in Mammal class.

But how this can be done?

Here comes the concept of closure. In JavaScript, there is no block-scope as in other programming languages like Java, C++, C.

{
var i = 0;
}

// i is available here
console.log(i); // 0

Instead it defines closure as the state of a function along with the scope of it. Let’s understand it by some examples.

var outside = 'Outside';
function a () {
console.log(outside); // Outside
}
a();
console.log(outside); // Outside

Let’s modify the code a little bit. We will declare the variable outside again inside the function a.

var outside = 'Outside';
function a () {
console.log(outside); // undefined

var outside;
}
a();
console.log(outside); // Outside

Why it printed undefined? Because inside the function the re-declaration of outside hoisted and it overrided the prior declaration of the variable outside. But, outside the function it remains same.

Now, let’s take an interesting example of closure.

for (var i = 1; i < 4; i++) {
setTimeout(function() {
console.log(i);
}, 2000);
}

It will print 4 three times. But we expected that, it should print 1, 2 then 3. Why it did that? Because, the closure, in which the expression console.log(i); was executed, had 4 as the value of i at the time of execution.

How can we correct this? simply by creating new closure for each of the functions passed to setTimeout.

for (var i = 1; i < 4; i++) {
setTimeout((function(i) { // new closure
console.log(i);
}(i)), 2000);
}

It’ll correctly print 1, 2 then 3.

Is this the only way-out? No. We can also use partial functions here. What are partial functions.

In Function prototype, there is one method, called bind, which returns a function binding with the passed context and arguments. This blog contains a previous post regarding usefulness of this function for a specific usecase. Here we can use bind for this purpose.

for (var i = 1; i < 4; i++) {
setTimeout(function(i) {
console.log(i);
}.bind(this, i), 2000);
}

It’ll print 1, 2 and 3 in correct manner.

Thank you for reading this. Be happy. :-)

Blog comments powered by Disqus