3

Recognizing that JavaScript doesn't have the concept of class per se, and that the "type" of all objects is "object", I'm trying to get my head around just what a "prototype" consists of and, in particular, how its "name" is associated with it. For example, in the following:

function Foo(){};
console.log(Foo.prototype);                // => "Foo {}"

How does console.log know to output Foo before the braces and what is that name referring to?

(Note: I'm aware that in the above, I'm referring to the prototype property of functions and not the prototype per se (i.e. not the thing accessible by __proto__), but the same question applies to the actual prototype objects. I just used the prototype property to simplify my example.)

Update: Based on the comment thread, this question is really focused on what Chrome is doing and, in particular, rationalizing its behavior in the following:

function Foo(){};
Foo.prototype.constructor = function Bar(){};
f = new Foo();
console.log(f);              // => Foo{} (remembering that f created by Foo, ignoring constructor)
console.log(Foo.prototype)   // => Bar{} (reporting constructor value)

See https://gist.github.com/getify/5793213 for more discussion.

Peter Alfvin
  • 28,599
  • 8
  • 68
  • 106
  • I'm guessing the `prototype` property of functions and an object's `__proto__` are the same thing. Which is why you see the output you see. – millimoose Jun 15 '13 at 00:11
  • Basically, whenever you do `new Foo()`, the resulting object's `__proto__` is set to the `prototype` of the constructor function. I doubt there's a deeper explanation beyond "that's how Javascript OO works". (Notwithstanding links to the ECMAScript spec.) Alternately: whenever you define a function, Javascript also internally defines a prototype object for objects created using that function as a constructor. – millimoose Jun 15 '13 at 00:12
  • 1
    Did you read the gist at the end of the question? The question about an official/unifying explanation of Chrome's behavior remains unresolved. The primary response below is "don't worry about it", "don't panic", etc. I'm doing neither, but that's besides the point. I've upvoted your answer on the grounds that it provides a nice overview of prototypes, but it doesn't really address the Chrome behavior question. – Peter Alfvin Apr 05 '14 at 16:13

4 Answers4

17

JavaScript has a very twisted form of prototypal inheritance. I like to call it the constructor pattern of prototypal inheritance. There is another pattern of prototypal inheritance as well - the prototypal pattern of prototypal inheritance. I'll explain the latter first.

In JavaScript objects inherit from objects. There's no need for classes. This is a good thing. It makes life easier. For example say we have a class for lines:

class Line {
    int x1, y1, x2, y2;

    public:

    Line(int x1, int y1, int x2, int y2) {
        this.x1 = x1;
        this.y1 = y1;
        this.x2 = x2;
        this.y2 = y2;
    }

    int length() {
        int dx = x2 - x1;
        int dy = y2 - y1;
        return sqrt(dx * dx + dy * dy);
    }
}

Yes, this is C++. Now that we created a class we may now create objects:

Line line1(0, 0, 0, 100);
Line line2(0, 100, 100, 100);
Line line3(100, 100, 100, 0);
Line line4(100, 0, 0, 0);

These four lines form a square.

JavaScript doesn't have any classes. It has prototypal inheritance. If you wanted to do the same thing using the prototypal pattern you would do this:

var line = {
    create: function (x1, y1, x2, y2) {
        var line = Object.create(this);
        line.x1 = x1;
        line.y1 = y1;
        line.x2 = x2;
        line.y2 = y2;
        return line;
    },
    length: function () {
        var dx = this.x2 - this.x1;
        var dy = this.y2 - this.y1;
        return Math.sqrt(dx * dx + dy * dy);
    }
};

Then you create instances of the object line as follows:

var line1 = line.create(0, 0, 0, 100);
var line2 = line.create(0, 100, 100, 100);
var line3 = line.create(100, 100, 100, 0);
var line4 = line.create(100, 0, 0, 0);

That's all there is to it. No confusing constructor functions with prototype properties. The only function needed for inheritance is Object.create. This function takes an object (the prototype) and returns another object which inherits from the prototype.

Unfortunately, unlike Lua, JavaScript endorses the constructor pattern of prototypal inheritance which makes it more difficult to understand prototypal inheritance. The constructor pattern is the inverse of the prototypal pattern.

  1. In the prototypal pattern objects are given the most importance. Hence it's easy to see that objects inherit from other objects.
  2. In the constructor pattern functions are given the most importance. Hence people tend to think that constructors inherit from other constructors. This is wrong.

The above program would look like this when written using the constructor pattern:

function Line(x1, y1, x2, y2) {
    this.x1 = x1;
    this.y1 = y1;
    this.x2 = x2;
    this.y2 = y2;
}

Line.prototype.length = function () {
    var dx = this.x2 - this.x1;
    var dy = this.y2 - this.y1;
    return Math.sqrt(dx * dx + dy * dy);
};

You may now create instances of Line.prototype as follows:

var line1 = new Line(0, 0, 0, 100);
var line2 = new Line(0, 100, 100, 100);
var line3 = new Line(100, 100, 100, 0);
var line4 = new Line(100, 0, 0, 0);

Notice the similarity between the constructor pattern and the prototypal pattern?

  1. In the prototypal pattern we simply create an object which has a create method. In the constructor pattern we create a function and JavaScript automatically creates a prototype object for us.
  2. In the prototypal pattern we have two methods - create and length. In the constructor pattern too we have two methods - constructor and length.

The constructor pattern is the inverse of the prototypal pattern because when you create a function JavaScript automatically creates a prototype object for the function. The prototype object has a property called constructor which points back to the function itself:

As Eric said, the reason console.log knows to output Foo is because when you pass Foo.prototype to console.log:

  1. It finds Foo.prototype.constructor which is Foo itself.
  2. Every named function in JavaScript has a property called name.
  3. Hence Foo.name is "Foo". So it finds the string "Foo" on Foo.prototype.constructor.name.

Edit: Alright, I understand that you have a problem with the redefining the prototype.constructor property in JavaScript. To understand the problem let's first understand how the new operator works.

  1. First, I want you to take a good look at the diagram I showed you above.
  2. In the above diagram we have a constructor function, a prototype object and an instance.
  3. When we create an instance using the new keyword before a constructor JS creates a new object.
  4. The internal [[proto]] property of this new object is set to point to whatever constructor.prototype points to at the time of object creation.

What does this imply? Consider the following program:

function Foo() {}
function Bar() {}

var foo = new Foo;

Foo.prototype = Bar.prototype;

var bar = new Foo;

alert(foo.constructor.name); // Foo
alert(bar.constructor.name); // Bar

See the output here: http://jsfiddle.net/z6b8w/

  1. The instance foo inherits from Foo.prototype.
  2. Hence foo.constructor.name displays "Foo".
  3. Then we set Foo.prototype to Bar.prototype.
  4. Hence bar inherits from Bar.prototype although it was created by new Foo.
  5. Thus bar.constructor.name is "Bar".

In the JS fiddle you provided you created a function Foo and then set Foo.prototype.constructor to function Bar() {}:

function Foo() {}
Foo.prototype.constructor = function Bar() {};
var f = new Foo;
console.log(f.hasOwnProperty("constructor"));
console.log(f.constructor);
console.log(f);

Because you modified a property of Foo.prototype every instance of Foo.prototype will reflect this change. Hence f.constructor is function Bar() {}. Thus f.constructor.name is "Bar", not "Foo".

See it for yourself - f.constructor.name is "Bar".


Chrome is known to do weird things like that. What's important to understand is that Chrome is a debugging utility and console.log is primarily used for debugging purposes.

Hence when you create a new instance Chrome probably records the original constructor in an internal property which is accessed by console.log. Thus it displays Foo, not Bar.

This is not actual JavaScript behavior. According to the specification when you overwrite the prototype.constructor property there's no link between the instance and the original constructor.

Other JavaScript implementations (like the Opera console, node.js and RingoJS) do the right thing and display Bar. Hence Chrome's behavior is non-standard and browser-specific, so don't panic.

What's important to understand is that even though Chrome displays Foo instead of Bar the constructor property of the object is still function Bar() {} as with other implementations:

Community
  • 1
  • 1
Aadit M Shah
  • 72,912
  • 30
  • 168
  • 299
  • +1. Too many words, but a good overview. I get frustrated when people don't understand either prototypal OOP or its variations, but I used Self, so I'm biased. – Dave Newton Jun 15 '13 at 01:37
  • 1
    @DaveNewton: I agree. Prototypal OOP is dead simple. However most people get confused because of the way prototypal OOP is implemented in JavaScript (i.e. the constructor pattern). Since JS is the most well known language that supports prototypal OOP this makes people think that prototypal OOP is difficult or substandard. JavaScript was my language of choice for nearly 8 years, but now I've taken a shine to Lua. I love the colon operator and the concept of metatables in Lua, and Lua does prototypal OOP right. You can implement prototypal inheritance any way you like using the `__index` method. – Aadit M Shah Jun 15 '13 at 04:52
  • @AaditMShah, thanks for responding. Per my response to @Eric below, can you comment on how the name of an "instance object" is known in the case that the `prototype.constructor` property of the constructor has been redefined, per http://jsfiddle.net/at4zQ/2/ – Peter Alfvin Jun 15 '13 at 14:51
  • 1
    @PeterAlfvin - When you redefine `prototype.constructor` then all the objects that inherit from that prototype reflect the change (remember objects inherit from other objects in JavaScript, not from constructors). Hence `instance.constructor` will point to the new constructor and `instance.constructor.name` will be the name of the new constructor, not the old one. I've updated my answer. Read it. – Aadit M Shah Jun 15 '13 at 17:29
  • 1
    @AaditMShah, I really have read your posts carefully and believe I understand them. However, my question is not about the value of the constructor property of the created objects. My question is about the output of console.log(f), at least on the Chrome browser. (I get different results when I do the test under node or produce alerts in JS Fiddle). In the case of the constructor having been redefined to be Bar, "f" is still output as "Foo {}", not "Bar {}". Where is the string "Foo" coming from at that point? See http://jsfiddle.net/at4zQ/4/ for a simplified case of this. – Peter Alfvin Jun 15 '13 at 20:30
  • 1
    @PeterAlfvin - Don't worry about that too much. It's browser specific behavior. It has nothing to do with the JavaScript language itself. Chrome probably attaches an internal property pointing to the original constructor of every object for debugging purposes. However no other JavaScript implementation that I know of does this, so don't panic. I've edited my answer. Read it. – Aadit M Shah Jun 16 '13 at 03:08
  • I'll upvote this answer given the effort expended and the tutorial about prototypal inheritance, but as far as I'm concerned, the specific questions remain unanswered in any definitive sense, to wit: 1) Where is Chrome getting the name it's displaying? 2) What is the name of this name if not "type" or "class"? See http://pivotallabs.com/javascript-constructors-prototypes-and-the-new-keyword/ for an example of related bad info on this subject and see https://gist.github.com/getify/5793213 for an attempt to clear up what Chrome is doing. Thanks again for your time. – Peter Alfvin Jun 17 '13 at 15:42
  • Nice read. I really like the Object.create way of doing things. One question comes to mind though. How would you access `this` inside a callback? Using a constructor function, you could set `var self = this` and in the callback use `self` to access the original instance. Can that be done when using a `create` function with a `Object.create` as in the example? Setting self inside the `create` function will be scoped only to that function and I cant see where else to do it. – datacarl Sep 24 '13 at 14:43
  • 1
    @datacarl Inside a constructor function `this` refers to the instance which is automatically created by `new`. Inside the `create` function we manually create an instance using `Object.create(this)` (here `this` refers to the prototype object, not the instance). You can save the instance created by `Object.create(this)` in any variable you want allowing you to access it inside nested functions. For example: `var self = Object.create(this);`. Now `self` can be accessed by any nested function. You may even set the `this` pointer of a function to the `self` object using `apply`, `bind` or `call`. – Aadit M Shah Sep 24 '13 at 16:39
  • @AaditMShah The problem is that the functions that I'm trying to use are not nested inside the create function (where I assign `var self = this`) but rather directly on the instance. I created a gist to show you what I mean. https://gist.github.com/datacarl/6688180 – datacarl Sep 24 '13 at 17:27
  • @datacarl I posted a comment on your gist. Let me know if that helps. – Aadit M Shah Sep 25 '13 at 02:16
1

The constructor property (which refers to a function originally used as a generator of the corresponding objects) is used to give a name to a prototype object in the console log. Consider the following:

function Foo() { 
  this.x = 1; 
}
console.log(Foo.prototype);  // Foo {}
Foo.prototype.constructor = function Bar() {  
  this.y = 2 
}
console.log(Foo.prototype);  // Bar {}        
var f = new Foo();
console.log(f.constructor);  // function Bar() { this.y = 2}

console.log(f.x);            // 1
console.log(f.y);            // undefined
console.log(f);              // Foo {x:1}

Here we've switched constructor to another function, giving a new name to prototype object. Note that the same function is returned when constructor property is queried directly from an object, created with Foo() function (as we go up the inheritance chain).

Still, it doesn't mean that another function (Bar()) was actually used to create the corresponding objects; it's still Foo(), and you can see it both by querying properties -- and f directly. Basically, objects remember the function that was used to create them, even if constructor property of prototype was "redirected".

raina77ow
  • 103,633
  • 15
  • 192
  • 229
  • Thanks, @raina77ow. Regarding your last sentence: "Basically, objects remember the function that was used to create them, even if `constructor` property of `prototype` was "redirected", my follow up questions are: 1) Is there an accepted name for the concept of "the function that was used to create them", and 2) Where is this information stored for purposes of "remembering"? – Peter Alfvin Jun 15 '13 at 14:29
0
function Foo(){};

Working down the chain:

console.log(Foo.prototype);
console.log(Foo.prototype.constructor);
console.log(Foo.prototype.constructor.name);
Eric
  • 95,302
  • 53
  • 242
  • 374
  • raina77ow's answer would suggest that this isn't correct. If a constructor's `prototype.constructor` property is redefined, objects created by that constructor are still displayed with the name of the constructor. Do you see it differently? – Peter Alfvin Jun 15 '13 at 14:33
  • Sorry, I realize that my question focused on what name was displayed for a function's prototype property and that does seem to track `prototype.constructor`. However, objects created by the constructor retain the name of the constructor even if the constructor's prototype.constructor property is redefined, so I'm still wondering about that. Is there some internal, non-accessible `constructor` property that retains the constructor used to create the object? – Peter Alfvin Jun 15 '13 at 14:42
0

Took a bit of digging online but I found this article that really illustrates how prototypes and other key core javascript functionality works:

http://dmitrysoshnikov.com/ecmascript/javascript-the-core/

I particularly like the diagram on how the prototype chain looks like.

TOBlender
  • 1,053
  • 11
  • 17