0

How does the prototype method work?

var object, u1, u2;
object = function(o) {
    var F = function() {};
    F.prototype = o;
    return new F();
};
u1 = {'name': 'adam'};
u2 = object(u1);
u2.name = 'roman';
delete u1.name;
console.log(u2.name);
delete u2.name;
console.log(u2.name);
u1.name = 'tytus';
console.log(u2.name);

roman
undefined
titus

Why is the third console.log being output? Could you explain this behaviour?

Tomasz Szymanek
  • 253
  • 2
  • 5
  • 15

3 Answers3

4

It's because of the way JS resolves references, or evaluates expressions. What is happening is similar to this:

u2[name] ===> JS checks instance for property name<------------||
    || --> property not found @instance, check prototype (u1)  ||
    ===========> u1.name was found, return its value ----------||
         ||
         || if it weren\'t found here:
         ==========> Object.prototype.name: not found check prototype?
             ||
             =======>prototype of object.prototype is null, return undefined

When you log u2.name the first time round, name was an instance property, so JS resolved the expression to that string constant (roman). Then you deleted it, so upon trying to access it again, JS resolved to u1.name (u2's prototype), which also didn't have the name property, nor did it find Object.prototype.name, hence the expression was resolved to undefined.

In the third case, the same steps were repeated, only this time the prototype of u2 did have a name property, so that's what the expression was resolved to. It's as simple as that, really.
Ok, to be fair, it took me some time to get used to this, too, but once you get it, it's pure logic.

BTW, the diagram I made here is a visual representation of what is referred to as "the prototype-chain". From the viewpoint of u2, the chain is u1 -> Object, which isn't a very large chain at all (in case you were worried): it's very similar to the chain of an Array instance:

console.log(Object.getPrototypeOf(Array.prototype));//Logs Object

Hence, the prototype chain of an array instance, say var foo = [];) looks like this:

Array.prototype => Object.prototype

Which means that:

foo = []
foo[123];//does not exists

Triggers a similar lookup through the prototype chain in order for the expression to resolve to undefined:

foo[123] ===> 123 is coerced to string first BTW  <------------||
    || --> property not found @instance, check Array.prototype ||
    ===========> Array.prototype[123] not found                ||
         ||                                                    ||
         ==========> no Object.prototype[123]: check prototype ||
             ||                                                ||
             =======>prototype of object.prototype is null, return undefined
Elias Van Ootegem
  • 74,482
  • 9
  • 111
  • 149
2

JavaScript is an object oriented programming language. However unlike other object oriented languages like C++ and Python it doesn't have classes. Instead JavaScript has prototypal inheritance.

In prototypal object oriented programming languages you only have objects. Inheritance is achieved via one of two ways:

  1. Delegation
  2. Concatenation

Suppose I have an object called rectangle as follows:

var rectangle = {
    height: 5,
    width: 10,
    area: function () {
        return this.width * this.height;
    }
};

Now I can calculate the area of this rectangle by calling rectangle.area. However what if I wanted to create another rectangle with a different width and height? This is where your object function is used (this function is available natively in most JavaScript interpreters as Object.create):

var rectangle2 = Object.create(rectangle);

rectangle2.height = 10;
rectangle2.width = 20;

alert(rectangle2.area()); // 200

What's happening here is that the object rectangle2 is inheriting from the object rectangle via delegation. To help you visualize what's happening look at the following diagram:

                null
                 ^
                 | [[Proto]]
                 |
 +-------------------------------+
 |        Object.prototype       |
 +-------------------------------+
 |              ...              |
 +-------------------------------+
                 ^
                 | [[Proto]]
                 |
 +-------------------------------+
 |           rectangle           |
 +-------------------------------+
 |           height: 5           |
 +-------------------------------+
 |           width: 10           |
 +-------------------------------+
 |           area: ...           |
 +-------------------------------+
                 ^
                 | [[Proto]]
                 |
 +------------------------------+
 |          rectangle2          |
 +------------------------------+
 |          height: 10          |
 +------------------------------+
 |          width:  20          |
 +------------------------------+

The Object.create function creates an object (rectangle2) whose internal [[Proto]] property points to the object rectangle (the prototype of rectangle2).

When you try to access a property on rectangle2 JavaScript first tries to find the property on rectangle2 itself. If it can't find the property on rectangle2 then it tries to find it on the prototype of rectangle2 (i.e. rectangle) and so on until:

  1. It find the property, in which case it returns the value associated with that property.
  2. It exhausts the prototype chain (i.e. it reaches null), in which case it returns undefined.

Hence if I try to access rectangle2.width it will return 20. However if I try to access rectangle2.area it will return the rectangle.area function instead of undefined. That's prototypal inheritance for you in a nutshell.


Now in you code you first create an object u1:

var u1 = {
    name: "adam"
};

Then you create an object u2 which inherits from u1:

var u2 = Object.create(u1);

After that you set a new name property on u2 (this doesn't overwrite the name property of u1, it simply shadows it):

u2.name = "roman";

Then you delete the name property of u1 (this doesn't affect the name property of u2):

delete u1.name;

So when you log u2.name it displays roman:

console.log(u2.name); // roman

Then you delete the name property of u2 as well:

delete u2.name;

Hence when you log u2.name again it displays undefined:

console.log(u2.name); // undefined

Finally you set u1.name to tytus:

u1.name = "tytus";

Hence when you log u2.name again it displays tytus because u2 delegates to u1:

console.log(u2.name); // tytus

I hope this helped you understand prototypal inheritance. If you want to learn more then read my blog post on Why Prototypal Inheritance Matters.

Aadit M Shah
  • 72,912
  • 30
  • 168
  • 299
1

If you try to access a property of an object, JavaScript first checks if the actual object has that property and if not, it checks if the prototype of its constructor function has that property:

var object, u1, u2;
object = function(o) {
    var F = function() {};
    F.prototype = o;
    return new F();
};

//u1 is just a "normal" JavaScript Object, created by the Object constructor function
u1 = {'name': 'adam'};
//u2 is an object, that was created by a custom constructor function (F) with the prototype u1
u2 = object(u1);

//add a local property "name" to u2
u2.name = 'roman';

//delete the property "name" from u1 (and thereby from the prototype of u2)
delete u1.name;

//prints "roman" because u2 has a local property "name"
console.log(u2.name);

//delete the local property "name" from u2
delete u2.name;

//neither u2 nor the prototype of u2 have a property name -> undefined
console.log(u2.name);

//add a property "name" to u1 (and thereby to the prototype of u2)
u1.name = 'tytus';

//prints "tytus" because u2 doesn't have a local property and therefor the property "name" of its prototype is used.
console.log(u2.name);
basilikum
  • 10,378
  • 5
  • 45
  • 58