Skip to content

inheritance

Actually this is about the whole core of javascript objects, functions and prototypes. I will attempt to visualize this in C like format.

hierarchy

This images provides a good overview of the objects in javascript:

image

By convention all (Constructor) function objects are written with Capital letter and 'plain' objects without capital letter. I will follow that in this document.

So these denote the factory functions, or constructors for making new Array, Function and Number objects.

create objects
1
2
3
4
5
var obj = new Object();

var arr = new Array();
var fun = new Function();
var num = new Number();

To make clear what is inside these objects, i created this function to dump all content of the functions and whether they are an 'own' property of the object or not :

dump an object verbose
function dump_var(v)
{
    console.log("type : " + typeof(v));
    for (var p in v) {
        if (v.hasOwnProperty(p)) {
            vv = v[p];
            console.log(" + " + p + " : " + typeof(vv) + "(" + vv + ")");
        } else { 
            vv = v[p];
            console.log(" - " + p + " : " + typeof(vv) + "(" + vv + ")");
        }
    }
}

First it prints the type of the object itself, then it's members name, type and values. But as you can see hasOwnProperty is in fact a property of object v but it does not get listed. So the members you will find are the ones you added yourself, and not the ones javascript defined.

To see those as well, you better use the chrome devtools debugger. Now to open up this code with dev-tools, type :

devtools
node --inspect-brk code.js

And open a chrome browser with the url it prints. You can now browse all variables, and see what changes when you step over an initialization.

Object

In the startup all variables used in the script are already created, but 'undefined'. If you step over the new Object() line obj gets filled with just a proto member that holds all Object.prototype specific methods .

  • defineGetter
  • defineSetter
  • lookupGetter
  • lookupSetter
  • constructor
  • hasOwnProperty
  • isPrototypeOf
  • propertyIsEnumerable
  • toLocaleString
  • toString
  • valueOf
  • get proto
  • set proto

You can find these in the official docs as well : for instance

https://developer.mozilla.org/nl/docs/Web/JavaScript/Reference/Global_Objects/Object/__lookupGetter__

Array

Array has it's own useful methods, but also a member named 'length' that is on both the object as the proto link.

Methods are things like : concat(), entries(), keys(), every(), forEach(), ...

Function

get_arguments(), call(), bind(), get_caller(), set_caller().

Also note that a number of members are part of the function itself, including:

name, arguments, called and length. Also note that the fun function as created with 'new' has name: "anonymous" while dump_var, created as a declared function has name: "dump_var".

Number

This has functions for conversion like :

toExponential(), toPrecision(), toFixed(), valueOf(), toString().

prototype chain

This is the main structure that is so hard to grasp. If i can reproduce the complete network by example it might get some clearer.

First note this image :

image

This is the structure after creating 3 Animal instances with this program:

create Animals
1
2
3
4
5
function Animal() {
}
var animal = new Animal();
var horse = new Animal();
var dog = new Animal();

You will only see animal, dog and horse in the local section of the debugger, but you can find the rest in the globals or through various proto links.

This all seems to be correct, you can follow the circular reference between Function.prototype -> Object.prototype -> Object -> ... forever.

But what i need to know is how values and functions are resolved. So here is my addition to the program :

resolve
function Animal(name) { 
    this.name = name;
}
Animal.prototype.age=4;
Animal.prototype.dump = function() { console.log(this.name + " is " + this.age); }

var animal = new Animal("beast");
var horse = new Animal("blacky");

horse.age = 10;
Animal.prototype.age = 8;
var dog = new Animal("doggy");

animal.dump();
horse.dump();
dog.dump();

So my first guess was the prototype was being used to create a default value for each animal instance on creation. That would result in :

expected output
1
2
3
beast = 4
blacky = 10
doggy = 8

At the time of beasts creation age = 4, and at the time of doggy's creation it is 8. But what it does print is :

output
1
2
3
beast = 8
blacky = 10
doggy = 8

So this is what happens :

The prototype value is not a default value, it is a fallback value. Objects just get a member at the time it is assigned.

So just before the dump functions the prototype chain looks like this, (ascii art because xs4all was down).

This would be the left side of the images shown before :

instances
+--------------------+     +--------------------+
| new Animal()       |     | Animal.prototype   |
+====================+     +====================+
| name: "beast"      |---->| age: 8             |
+--------------------+     +--------------------+
| __proto__:         |     | constructor        |---> Animal()
+--------------------+     +--------------------+
                           | __proto__          |---> Object.prototype
+--------------------+     +--------------------+
| new Animal()       |       ^      ^
+====================+       |      |
| name: "blacky"     |       |      |
+--------------------+       |      |
| age: 10            |       |      |
+--------------------+       |      |
| __proto__:         |-------+      |
+--------------------+              |
                                    |
+--------------------+              |
| new Animal()       |              |
+====================+              |
| name: "doggy"      |              |
+--------------------+              |
| __proto__:         |--------------+
+--------------------+

Now Just follow the links at the time of calling .dmp()

  • Beast has no member named age, so follow the chain. Animal.prototype has age: 8
  • blacky has an age of his own, no need to search further, age: 10
  • Doggy has no member named age, so follow the chain. Animal.prototype has age: 8

Doggy has the value at that point in time, and that's 8 and not 4 anymore.

Note that anything in javascript is an object and so anything can be at the end of the proto "pointer". Just always follow the chain.

constructor

Constructor functions are usually written with a capital first letter. Normal functions can be written lowercase.

You can determine the kind of object created with a constructor with the instanceof operator.

constructor
function Book() {
} 
var mybook = new Book();
var nothing = Book();

console.log(mybook instanceof Book);    // true
console.log(mybook instanceof String);  // false
console.log(nothing instanceof Book);   // false

something = {x:11;}
// <instance> instanceof <function>  so this will TypeError:
console.log(mybook instanceof something); // TypeError: not callable

As expected calling Book as a normal function has no effect on nothing and only new Book() makes a real instance of Book.

All object instances have a 'constructor' property that points to the constructor that created it.

constructor property
1
2
3
console.log(mybook.constructor == Book); // true
console.log(mybook.constructor);    // [Function: Book]
console.log(mobj.constructor);    // [Function: Object]

All objects inherit a constructor property from their prototype.

prototype.constructor
var s = new String("Text");
console.log(s.constructor);                // [function: String]
console.log(String.prototype.constructor); // [function: String]

console.log(s.constructor === String);  // true
console.log("any string".constructor === String);   // true

// or arrays : 
var a = new Array();
console.log(a.constructor);                // [function: Array]
console.log(Array.prototype.constructor); //  [function: Array]

console.log(a.constructor === Array);  // true
console.log([].constructor === Array);   // true

constructor property setup

You can setup Constructors with helper functions to do more :

helper functions
function Book(name) { 
Object.defineProperty(this, "name", { 
    get: function() { 
        return "Book: " + name;       
    },        
    set: function(newName) {            
        name = newName;        
    },               
    configurable: false     
}); 
}

var myBook = new Book("Single Page Web Applications");
console.log(myBook.name);    // Book: Single Page Web Applications

// we cannot delete the name property because "configurable" is set to false
delete myBook.name;    
console.log(myBook.name);    // Book: Single Page Web Applications

// but we can change the value of the name property
myBook.name = "Testable JavaScript";
console.log(myBook.name);    // Book: Testable JavaScript

So this creates a getter and setter for property 'name' and forbids deleting it.You don't have to use .get() and .set(), these are internal functions, but you can customize them like in the .get() function.

Object literals are preferred over constructor functions. They are more readable and faster to run.

As an example :

example
1
2
3
4
5
6
7
// strings have a slice() method 
var obj = new String("text");
obj.slice(0,2);     // "te"

// same as above
var string = "text";
string.slice(0,2);  // "te"

The second works just as well but is easier.

new

If you forget to use the new keyword, everything runs fine but not as you intended. So here is a way to enforce it if needed, it's called Scope-Safe constructors:

new operator
1
2
3
4
5
6
function Book(name) { 
    if (!(this instanceof Book)) { 
        // the constructor was called without "new".
        return new Book(name);
    } 
}

Or dump_object(), whatever, here is a function that prints an object in a readable way. :

print_r
function xinspect(o,i){
    if(typeof i=='undefined')i='';
    if(i.length>50)return '[MAX ITERATIONS]';
    var r=[];
    for(var p in o){
        var t=typeof o[p];
        r.push(i+'"'+p+'" ('+t+') => '+(t=='object' ? 'object:'+xinspect(o[p],i+'  ') : o[p]+''));
    }
    return r.join(i+'n');
}