4

I'm trying my best to understand javascript. Here is a simple experiment in Chrome console which gets me very confused:

var foo=function(){this.p=1;}
foo.prototype.p=2;
var bar=new foo();
//foo{p:1,p:2} <- this is the output of Chrome console, from the last command above

The output of Chrome is what confuses me. It seems like bar is an object with 2 parameters, p:1 and p:2. Does this mean bar has 2 p??? What is the reasoning behind this?

Xun Yang
  • 4,209
  • 8
  • 39
  • 68

5 Answers5

2

The bar object has only one p with value 1

The earlier p with value 2 can be viewed in a readonly object which you can access with getPrototypeOf:

Object.getPrototypeOf(bar).p

You see both because the developer toolbar is designed to print an XML representation of the specified object which should intuitively show all the properties, whether directly accessible or not.

loxxy
  • 12,990
  • 2
  • 25
  • 56
  • sure, but why do both appear in the console log? – John Dvorak Sep 15 '13 at 10:36
  • a prototype is not read-only, even though you don't normally write to it – John Dvorak Sep 15 '13 at 10:38
  • @JanDvorak when you invoke `bar` chrome prints an XML representation of the specified object which should intuitively contain both p's. prototype is not readonly but in this case the reference to prototype exists as readonly or as the non recommended `__proto__` – loxxy Sep 15 '13 at 10:53
2

Yes. Sort of.

bar has both:

  • A p property of its own.

    bar.hasOwnProperty('p'); // true
    bar.p;                   // 1
    
  • A p property still remaining on the prototype that it has through inheritance.

    Object.getPrototypeOf(bar).p; // 2
    

Though, only 1 of them is accessible directly from bar at a time, with preference to the own property.

bar.p;        // 1
delete bar.p;
bar.p;        // 2

And, Chrome is showing both because it's traversing the prototype chain and looking for any enumerable properties.

Jonathan Lonowski
  • 121,453
  • 34
  • 200
  • 199
2

Chrome DevTools console's inline (non-extended) object representation currently does not display any difference between own properties and inherited prototype properties.

Now let's break what's going on into smaller steps.

new foo() creates a new object whose internal proto property points to foo.prototype. This means this object can access all properties defined in foo.prototype. It's called prototype chain.

Now when you set a property of the same name in the object, it "shadows" the prototype's property by the same name, turning the latter inaccessible through regular property access (see @loxxy's answer using Object.getPrototypeOf(obj) to access the shadowed prototype property).

Once you add a function to the object or its prototype, the console allows you to display the extended object representation, which does differ own properties from prototype properties. In the next example I've added a q method to the prototype to allow this behavior. The properties inherited from the prototype are shown inside the object's proto internal property:

enter image description here


If you just want to have the number of instanced objects in the constructor's prototype, you can use:

var foo = function() {
    Object.getPrototypeOf(this).p++;
}
foo.prototype.p = 0;

console.log(new foo()); //{p: 1}
console.log(new foo()); //{p: 2}

Or without the ES5 dependency:

var foo = function() {
    foo.prototype.p++;
}
foo.prototype.p = 0;

console.log(new foo()); //{p: 1}
console.log(new foo()); //{p: 2}
Fabrício Matté
  • 69,329
  • 26
  • 129
  • 166
  • Thanks! So is it possible to access the p in __proto__? In the console it seems impossible – Xun Yang Sep 15 '13 at 10:35
  • @XunYang Yes, @loxxy's answer does that. `=]` – Fabrício Matté Sep 15 '13 at 10:37
  • Alright, so if "p" is a method, I can "override" it in foo by calling `Object.getPrototypeOf(bar).p()`? Can it be done easier? – Xun Yang Sep 15 '13 at 10:39
  • @XunYang Sorry, not sure if I understood what you meant. You want to redefine a prototype property from inside an object instantiation? In case you just want to "override" (I've removed this word from the answer because it sounds too much Java'ish `:P`) the inherited `p` property in `bar`, what you've done is good enough. – Fabrício Matté Sep 15 '13 at 10:40
  • Like this: `foo=function(){this.p=function(){return parent.p()+1;}};`,`foo.prototype.p=function(){return 0;}` Not really for a counter, just an example of "classical" overriding :) – Xun Yang Sep 15 '13 at 10:42
  • @XunYang You're making an instantiation object count? Or making an function to return an "id" (number) of the instanced object? – Fabrício Matté Sep 15 '13 at 10:44
  • 1
    @XunYang Not sure what you're after but parent suggests you want to inherit override and call the super functions (as it's called in Java I think). Prototype is not really the parent of an object but when you inherit from an object called Parent you do add Parent.prototype to the Child.prototype (inheriting) and you take the instance variables of Parent by calling `Parent.apply(this,arguments);` in the Child function body. You can override functions of Parent and call it's prototype functions: http://stackoverflow.com/a/16063711/1641941 – HMR Sep 15 '13 at 16:27
  • @HMR Very interesting read, though I believe OP means Constructor.prototype -> instance instead of Parent Constructor -> Child Constructor. – Fabrício Matté Sep 15 '13 at 18:36
  • @FabrícioMatté Aha, maybe Xun wants to shadow the prototype function in the instance and then call it's prototype version from the shadowing function. I don't see a practical use for it as it forces you to create functions for each instance that do the same thing (use inheritance instead) but `__proto__` has been mentioned before if you just want to access it. (instance.`__proto__`.p()+1) In the Hamster example (post in my previous comment) it shows how to override and partly implement Parent methods with the getSpeed method. – HMR Sep 16 '13 at 01:19
  • @HMR yes, in that case it'd be easier to just copy the current value from the prototype to the instance. – Fabrício Matté Sep 16 '13 at 02:08
1

var foo=function(){this.p=1;} is constructor and executes after var bar=new foo();. So at the beginning p=2 and then p becomes 1. So:

var foo=function(){
   // here this.p is equal to 2
   this.p=1;
   // here this.p is equal to 1
}
foo.prototype.p=2;
var bar=new foo();

EDIT:

JSON.stringify(bar);
karaxuna
  • 26,752
  • 13
  • 82
  • 117
1

When you acces a property, the javascript engine will seek it on the object instance, then on all its prototype chain.
So the meaning of p as a prototype property is to have a default value for p, wether you define it on any instance of the class or not. One example might be the number of wheel for a vehicle, that could default to 4, for instance.
If later you write to this property :

 function Vehicle() {};
 Vehicle.protoype.wheelCount = 4;
 var myBike = new Vehicle();
 myBike.wheelCount = 2 ;           // this is a bike.

You won't change the value set on the prototype, but rather you'll create a new property on the instance, having the new value, so for instance :

 var myCar = new Vehicle();
 myCar.wheelCount // === 4

Now the very scenario you mention -setting a default value, and setting also an instance value in the constructor - doesn't make much sense, since you will have to use Object.getPrototypeOf to get to reach the default value. This is just a possibility that is of no use, just like there are many in all languages.

GameAlchemist
  • 18,995
  • 7
  • 36
  • 59