Tales from the Evil Empire

Bertrand Le Roy's blog

News


Bertrand Le Roy

BoudinFatal's Gamercard

Tales from the Evil Empire - Blogged

Blogs I read

My other stuff

Archives

Careful with that prototype, Eugene

Here's one that could cost you some long and puzzling debugging sessions.

What's the difference between

Foo.Bar = function() {
  this.number = 0;
  this.stuff = {};
}
Foo.Bar.prototype.toString = function() {
  return "Bar #" + this.number + (this.stuff.id || "");
}
and
Foo.Bar = function() {}
Foo.Bar.prototype.number = 0;
Foo.Bar.prototype.stuff = {};
Foo.Bar.prototype.toString = function() {
  return "Bar #" + this.number + (this.stuff.id || "");
}
Well, it becomes obvious when you try the following:
var a = new Foo.Bar();
var b = new Foo.Bar();
a.number = 1;
a.stuff.id = "a";
b.number = 2;
b.stuff.id = "b";
alert(a.toString());
alert(b.toString());

In the first case, you'll get "Bar #1 a" and "Bar #2 b". In the second "Bar #1 b" and "Bar #2 b". The reason is that both instances of Foo.Bar have the same prototype, so they both share the same object as their "stuff" field. If you set it on any instance, it affects all of them. In the first case, a new object is created from each instance's constructor.

Using the prototype to keep the cost of constructing instances as low as possible is a good idea in general but as most powerful concepts it must be handled with care. In the next article, I'll detail the difference between the closure style of JavaScript type definition and the prototype style. This is one of the caveats to keep in mind when going the prototype way.

Now, this could very well be taken advantage of by defining a "static" blob on the prototype to simulate the concept of fields shared between instances. That would kinda work but it would be a little weird to have statics hanging off a field on instances and it would be easy to overwrite this static blob off any instance. But the best argument against that is that defining statics as members of types (i.e. constructor functions) works much better and is more natural. The only problem with the latter method is statics are hard to differentiate from instance members of the type Function.

Comments

Rick Strahl said:

I've run into this on a few occasions, and it's one of those things that are totally non-obvious about JavaScript - until you hit it... I've come to think of prototype like a sort of static interface with closures being more like a typical instance interface. It depends on the situation, but it seems to me one should be really careful with assigning protypes because you may never know how a class is used.
# October 8, 2006 5:23 AM

Bertrand Le Roy said:

Manuel, you'll see in the next CTP that all Atlas classes are now prototype-based.

I'll fix the post.

# October 9, 2006 12:02 AM

Manuel Abadia said:

Bertrand, that's good news! Is the next CTP comming out soon? :)
# October 9, 2006 3:08 AM

Garbin said:

Hi, I still remember that headache... I've read about this tip only in a book entirely dedicated to JavaScript. Thanks for sharing it.
# October 9, 2006 1:46 PM

Bertrand Le Roy said:

Manuel: we're working really hard on the new CTP currently. I can't tell you when it will be ready exactly yet, but we're doing everthing we can to get that into your hands as soon as possible.

I'll make another post soon about the transition from closure to prototype, its motivations and caveats.

# October 9, 2006 2:49 PM

Manuel Abadia said:

Bertrand: Thanks for the information. As the last release was a few months ago I'm a bit impatient to get the latests bits in my hands. The post you talk about sounds really interesting...
# October 9, 2006 6:24 PM

Bertrand Le Roy said:

# October 11, 2006 12:18 PM

Patrick J. Fox said:

Bertand - Thanks for your informative posts. I tired out he example and came up with a combination of the two patterns shownm above:
Foo.Bar = function() {
		this.stuff = {}
	}
Foo.Bar.prototype = {
	number: 0,
	toString: function() {
  	return "Bar #" + this.number + (this.stuff.id || "");
	}
}
Any reason why someone shouldn't code it this way?
# November 15, 2006 2:17 PM

Bertrand Le Roy said:

Patrick: sure, that's what we're doing in ASP.NET AJAX, see my more recent posts on prototype. In this post, I tried to keep it simple and without dependancy on AJAX.

The problem with your code if you add nothing else is that you broke the prototype's constructor by overwriting the whole prototype with a JSON object. We solve this in the AJAX Library by resetting prototype.constructor to Foo.Bar from the class registration method.

# November 15, 2006 5:20 PM