Prototypes: Object-orientation and its traps

Care to share?

Object-oriented programming is a code-writing style that makes it possible to create applications in an efficient and quick manner, by helping the programmers to control, maintain and develop the code. Prototype-based programming is one of the styles of the object-oriented programming that differs from the typical object-orientation known from other programming languages such as java, php or c++. Prototype programming method is “classless” – classes, in their classic sense, are absent.

Prototypes are the function elements that enable easy definition of properties and functionalities that will be automatically applied to the object instances. All functions contain prototype properties referring to a null object. Instead of classes, JavaScript uses functions that may be defined in 2 ways:

function Bird() { }
// or
var Mammal = function() { }

// checking the type
console.log(typeof Mammal, typeof Bird); // (1)

<a class=”jsbin­embed” href=”http://jsbin.com/tupewo/1/embed?js,console”>JS
Bin</a><script src=”​http://static.jsbin.com/js/embed.js”></script>​

Analyzing the code above (1) we may notice that the functions are not objects until an instance is created with the use of the new​ keyword. ​The example below creates instances for Mammal​ and​ Bird​​.

var obj1 = new Mammal();
var obj2 = new Bird();

// checking the type
console.log(typeof obj1, typeof obj2); // (1)

<a class=”jsbin­embed” href=”http://jsbin.com/yinoze/1/embed?js,console”>JS
Bin</a><script src=”http://static.jsbin.com/js/embed.js”></script>

This way, we created the object instances. After checking the types (2), we may see that everything is correct and the code displays “object, object”. Omission of the expression ​new is a fatal mistake. It would contaminate the global namespace and cause a script error that would interrupt the stack of the processed commands. Methods defined by the prototypes would be simply unavailable. For instance:

function Bird() {}
Bird.prototype.fly = function() {
return true;
}

var obj1 = Bird();
console.log(obj1); // (1)

<a class=”jsbin­embed” href=”http://jsbin.com/xenigo/1/embed?js,console”>JS
Bin</a><script src=”http://static.jsbin.com/js/embed.js”></script>

obj1 variable is not an object, but it is undefined (1) – it does not possess the fly()​​ method. Situation is different in the case where an instance is created with the use of the new​​ expression:

function Bird() {}
Bird.prototype.fly = function() {

return true;
}

var obj1 = new Bird();
console.log(obj1 && obj1.fly()); // (1)

<a class=”jsbin­embed” href=”http://jsbin.com/seviso/1/embed?js,console”>JS
Bin</a><script src=”http://static.jsbin.com/js/embed.js”></script>

As you may see in the example above, the attempt to refer to the method returns true​​ (1). When creating an instance, we may use the constructor to send it some properties. The constructor is invoked when an object is initialized. The properties should be set up in the function prototype property to enable the proper inheritance at a later stage.

function Bird(color) {
this.color = color;

}

var obj = new Bird(“yellow”);
console.log(obj.color); // (1)

<a class=”jsbin­embed”href=”http://jsbin.com/mivelu/1/embed?js,console”>JS
Bin</a><script src=”http://static.jsbin.com/js/embed.js”></script>

access to the fields, methods within the object, is enabled by the keyword this​​, which refers to the current object with the use of the following syntax: instance_name.property (1). Methods may be defined as functions that may contain arguments. Just like it was in the case of properties, we attribute them to the prototype object.

function Bird(color) {
this.color = color;
}

Bird.prototype.info = function(weight, size) {
this.weight = weight;
this.size = size;

console.log(this.color + ” bird, weight ” + this.weight + ” and ” + this.size + ” size”);

};

var obj1 = new Bird(“yellow”);
var obj2 = new Bird(“green”);

obj1.info(1, 2); // 1 “yellow bird, weight 1 and 2 size”

var fn = obj2.info;

fn(1, 2); // 2 “undefined bird, weight 1 and 2 size”

fn.call(obj2, 1, 2); // 3 “green bird, weight 1 and 2 size”
fn.apply(obj2, [1, 2]); // 4 “green bird, weight 1 and 2 size”

<a class=”jsbin­embed” href=”http://jsbin.com/rusetes/1/embed?js,console”>JS
Bin</a><script src=”​ http://static.jsbin.com/js/embed.js”></script>​

In the example above (1) we use the obj1 instance to refer to the info method, which makes the console display the following message: “yellow bird, weight 1 and 2 size”. In point (2), there should be “green bird, weight 1 and 2 size”, but it is not. We attributed the functions to a variable that belongs to the global object window, which does not possess the color​​ property in its namespace. To solve this problem, we may use the fact that the methods are functions attributed to an object as properties. Functions call​ and​ apply ​ make it possible to set a method of one object in the context of another. The call method (3) takes the following arguments: context, arg1, arg2… (argument, one after another), while the apply (4) method takes 2 arguments: context and argument table. This way, we made the code display the proper message.

Let us now take a closer look at the inheritance. There is only one relation in the object-based programming, child – parent. The child is a specialized class that inherits the methods and properties of its parent. In the example below, we define a parent object: Animal and 2 child objects: Bird and Mammal.

function Animal(name) {
this.name = name;
}

Animal.prototype.eat = function() {
console.log(this.name + ” eats.”);
};

Animal.prototype.sleep = function() {
console.log(this.name + ” sleeps.”);
};

function Bird(name, color) {
Animal.call(this, name); // 1
}

Bird.prototype = Object.create(Animal.prototype); // 2
Bird.prototype.constructor = Bird; // 3

Bird.prototype.fly = function() {
console.log(this.name + ” flies.”);
};

function Mammal(name) {
Animal.call(this, name); // 1
}

Mammal.prototype = Object.create(Animal.prototype); // 2
Mammal.prototype.constructor = Mammal; // 3

Mammal.prototype.run = function() {
console.log(this.name + ” runs.”);
};

var mammal = new Mammal(“dog”);
var bird = new Bird(“parrot”);

mammal.eat(); // “dog eats.” (4)
mammal.run(); // “dog runs.” (4)

bird.eat(); // “parrot eats.” (4)
bird.fly(); // “parrot flies.” (4)

console.log(bird instanceof Animal); // true (5)
console.log(mammal instanceof Animal); // true (5)

<a href=”http://jsbin.com/kefiwa/1/embed?js,console”>JS
Bin</a><script src=”http://static.jsbin.com/js/embed.js”></script>

Analyzing the code above, we may notice that the parent constructor (1) is invoked in the child constructor in the child’s context. It makes it possible to access all methods of the object Animal. Object.create (2) enables the creation of an object of specific properties. We will use it to create a child object Animal. Set the constructor (3). Refer to the specialized methods of the child and parent (4). Check whether the child object is an instance of the parent object (5).

We need to mention the fact that JavaScript possesses the “built-in” objects, i.e. Math, Object, Array and String. When trying to specialize these objects, we need to take special precautions, because they will be added to all objects of the given type. For example, when expanding the Object.prototype, we may cause unexpected errors:

Object.prototype.items = function() {
var items = [];
for (var p in this)
items.push(p); return items;
};

var obj = { a: 1, b: 2, c: 3 };
console.log(obj.items()); // [“a”, “b”, “c”, “items”]

<a class=”jsbin­embed” href=”http://jsbin.com/newopa/1/embed?js,console”>JS
Bin</a><script src=”http://static.jsbin.com/js/embed.js”></script>

The code above should return 3 elements, but it returns 4. In order to avoid such a situation during the iteration, we need to check whether the object possesses the defined properties. We may do this with the use of the hasOwnProperty() method.

The inheritance makes it possible to discover the abstraction mechanism that enables the creation of specialized parent objects or the inclusion of compositions or instances of an object in the properties of another class. Inherited classes are polymorphic – the child class is of the same type as the parent class. Polymorphism works one way only, along the child – parent line. Example:

   function Animal(name) {
this.name = name;
}

function Bird(name, color) {
Animal.call(this, name);
}

Bird.prototype = Object.create(Animal.prototype);

Bird.prototype.constructor = Bird;

var bird = new Bird(“parrot”);

console.log(bird instanceof Object); // (1)
console.log(bird instanceof Animal); // (2)
console.log(bird instanceof Bird); // (3)

<a class=”jsbin­embed” href=”http://jsbin.com/cujawa/1/embed?js,console”>JS
Bin</a><script src=”​ http://static.jsbin.com/js/embed.js”></script>​

As you may see, bird is an instance of the Object (1), Animal(2) and Bird(3) classes – that is where the polymorphism takes place.

Do you want to control, maintain and develop the code with Divante? Contact us.

Published May 27, 2015