JavaScript Tutorial for Programmers - Prototype(1)

I still remember the days of debugging CORS problem when I put together some projects using JavaScript (& ajax), “a very particular programming language” in my first impression. Recently I got a great opportunity. The new role uses JS, the browser-side script that is now winning in all sides, as the major language. So I tooke it as a good chance to learn JS more systematically, and this series will be part of the outcome of my study. As the name implies, I will not cover primary level such as “if, else” (condition), “for” (or any kinds of loops), or basic OOP concepts. Instead, I will focus only on differences so you can learn this versatile language like reviewing a pull request, and use it the next day in your next awesome project.

prototype, a semi-OOP semantic

A prototype is a singleton that is created for each class (by JS runtime), so that instances of the class can be constructed based on the in memory template. I call prototype semi-OOP because it is not normal. For instance, this is how a prototype based inheritance is programmed:

1
2
3
4
5
6
7
8
function inherits(ChildClass, ParentClass) {
function IntermediateClass() {}

IntermediateClass.prototype = ParentClass.prototype;
ChildClass.prototype = new IntermediateClass;
ChildClass.prototype.constructor = ChildClass;
return ChildClass;
};

I will cover prototype based inheritance in the next post. For now, we just grasp the idea of how things are different here.

Practical implication

As per discussed in the last post, ES6 has already defined a more standard OOP paradigm in JavaScript. And ES6 code can be “transpiled” (compile into another language or syntax) into ES5 so the two standards are equally compatible with various browsers in practice. Does that mean prototype is not useful anymore? My answer is No, because it still largely exists in a large amount of existing code. For concrete numbers, if grep -R prototype *|wc -l, at the point of time when I am writing, in Angular, the output shows there are 286 lines; in React, there are 405; and Vue, 756). As for me, I need to know prototype to understand these 1447 lines of backbones. Last but not least, the frameworks are ordered alphabetically 😅.

Playaround with prototype

In the following example, we first define a constructor (If you do not know what is a constructor in JS context, please ⬅ to my last post). Then we use it to create an object and examine the prototype(s).

Example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var MyClass = function (){};

var obj = new MyClass;
alert(obj.__proto__ == MyClass.prototype); //=true
alert(obj.prototype == MyClass.prototype); //=>false

// if the object's prototype does not equal to the class' prototype, what is it then
alert(obj.prototype); //=>undefined

// you can change the definition of a class on the run
MyClass.prototype.a_member = "abc";

// and the object's structure is changed as well
alert(obj.a_member); //=>abc
alert(obj.__proto__.a_member); //=>abc

Result:

1
2
3
4
5
true
false
undefined
abc
abc

An initial observation:

  1. prototype only exists in a class (a.k.a., a constructor). If you need to access prototype from a class instance (a.k.a., an object), __proto__ is the way to go.
  2. prototype and __proto__ are just two sides of one coin. In technical words, they refer to the same instance, i.e., the singleton mentioned in the beginning of this text.
  3. the prototype of a class can be changed at runtime, and all the instances of this class are affected accordingly.

Moreover, a modification of __proto__ of an instance can change the prototype (definition) of a class, and all the sibling instances are affected in a cascade manner.

Example:

1
2
3
4
5
6
7
8
9
10
11
var MyClass = function (){};

var obj1 = new MyClass;
var obj2 = new MyClass;

obj1.__proto__.a_member = 'abc';

// an instance can change the definition of the class
alert(MyClass.prototype.a_member); //Ooooops...
// so all other instances are affected
alert(obj2.a_member);

Result:

1
2
abc
abc

This operation is dangerous. It will cause side-effect and confusion in a project with reasonable complexity as the definition of a class can not be easily traceable. Hence I highly recommend considering __proto__ final (in Java) or constant (in C++) and should be never touched.

If you really want to modify the behavior of an object, say, add a property, at least modify the object itself rather than __proto__.

1
2
3
4
5
6
7
8
9
10
11
var MyClass = function (){};
var obj1 = new MyClass;
var obj2 = new MyClass;

obj1.a_member = 'abc'; // change the object behavior at run time

// no effect
alert(MyClass.prototype.a_member);

// no effect
alert(obj2.a_member);

Result:

1
2
abc
abc

On the other hand, adding a property to a class effectively creates a static member (in other language term).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var MyClass = function (){};
var obj1 = new MyClass;
var obj2 = new MyClass;

MyClass.a_member = 'abc'; // change the class behavior

// no effect
alert(MyClass.prototype.a_member);

// no effect
alert(obj1.a_member);
alert(obj2.a_member);

// the member has been effectively added
alert(MyClass.a_member);

Result:

1
2
3
4
undefined
undefined
undefined
abc

I would recommend this usage.

So the bright side of this feature is flexibility, empowered by that

Everything is object

Everything in JavaScript is object. That includes, functions, constructors (class)es, instances, prototypes and __proto__s, etc. Let’s prove it in code.

Example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
var MyClass = function (){
this.a = "a";
this.b = "b";
};

var obj = new MyClass;

var arry = [];

function f1() {
alert("something");
};

alert(MyClass instanceof Object);
alert(MyClass.prototype instanceof Object);
alert(MyClass.__proto__ instanceof Object);
alert(obj instanceof Object);
alert(obj.__proto__ instanceof Object);
alert(obj instanceof Object);
alert(arry instanceof Object);
alert(f1 instanceof Object);
alert(f1.prototype instanceof Object);
alert(f1.__proto__ instanceof Object);

Result:

1
true(chorus)

Furthermore, Object is subdivided into two categories, first class objects and all other normal objects. Normal objects are just variables, which can be created, modified, assigned to other variables, and passed as arguments (or return value), all in runtime. Whilst first class object is the “platinum version” of object that unlocks extra privileges. Besides the operations listed above, first class object can be invoked (as a normal function); and it can be invoked to create normal objects (as a constructor). I think those extra privileges can explain from another perspective why first class object is attached one extra member property prototype alongside __proto__.

More examples:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
var MyClass = function (){
this.a = "a";
this.b = "b";
};

var obj = new MyClass;

function f1() {
alert("something");
};

// MyClass is a first class object so...
alert(MyClass.prototype); // it has prototype and
alert(MyClass.__proto__); // __proto__

// obj is an object so...
alert(obj.prototype); // it does not have prototype but
alert(obj.__proto__); // it has __proto__

// f1 is a first class object so...
alert(f1.prototype); // it has prototype and
alert(f1.__proto__); // __proto__

Result:

1
2
3
4
5
6
[object Object]
function () {}
undefined
[object Object]
[object Object]
function () {}

Except for…

Exception 1, primitive types

Did I just said everything is object?

1
2
3
var str = "whatever";
alert(str instanceof Object);
alert(str.__proto__ instanceof Object);

Result:

1
2
false
true

Even though a string literal is not an object, it contains __proto__ that is an object?! So either string literal is an object, which is obviously not, or what I said in the last section is just bullshit.

It is not. What happens here is that a mechanism called automatic wrapping kicks in, which wraps the no-object (primitive) variables to its object counterpart , in this case, a String(), as required. Automatic wrapping can enable calling methods directly from primitives as well:

1
2
var str = "whatever";
alert(str.indexOf("tever"));

Result:

1
3

Same phenomenon happens: the runtime wrap the literal to String() automatically and calls the indexOf() method. And the rule applies to other primitive types (int, float) as well.

Exception 2, ???

All sounds perfect until I tried this

Example:

1
2
3
4
5
6
7
8
var obj = {
a:"abc",
b:"bcd"
};

alert(obj instanceof Object);
alert(obj.__proto__);
alert(obj.__proto__ instanceof Object);

Result:

1
2
3
true
[object Object]
false

Apparently var obj and obj.__proto__ are objects according to the first two outputs. However, when I want to confirm if obj.__proto__ is really an object, runtime gives me a false. I hope I am not the first bloger who writes about things he is not sure himself, but if you have an answer, please let me know in the comments. It does not matter if you don’t, as __proto__ is no-standard usage anyway.

In the next post, I will discuss one of the hardest parts of JavaScript, inheritance based on prototype chain.