1

What should I put into a constructor function: something related to an instance or something related to a class?

Consider this code:

var count = 0
TView = function (x, y) { this.x = x, this.y = y, this.id = count++ }
TGroup = function() {}
TGroup.prototype = new TView
var v1 = new TView(1, 1)
var g1 = new TGroup(2, 2)
console.log(count, v1.id, g1.id, g1.x, g1.y)

output:

2 1 0 undefined undefined

here new TView happens twice... Once when I really need to create a TView instance, other time when I assign a TGroup prototype. Hence my question.

exebook
  • 32,014
  • 33
  • 141
  • 226
  • Inside a constructor you should put instance specific code. It is use to *construct* the instance. To set up inheritance, you should use `Object.create`. Have a look at my answer here: http://stackoverflow.com/q/17392857/218196 – Felix Kling Feb 15 '14 at 03:55

2 Answers2

1

The problem here is that your constructor's arguments are not passed from TGroup's constructor to TView's constructor... And don't bother adding "super(x, y)" or whatever similar call, it won't work: constructors in JavaScripts "objects" are not so much "object-oriented".

Disclaimer: descriptions in the following paragraphs contains some statements that are not exactly true, in order to make the key concepts easier to understand. Please refer to comments bellow for clarifications and rectifications.

A better way to think about it is that an object in JavaScript is basically an hash map, containing both regular values (that are the object's fields) and functions (that are the methods). The class is also a map (well, it is a function, but JavaScript is somewhat lousy on these...), which usually contains a single method and maybe some values. Now one of these values stores on the "class" map is the special "prototype" map.

When a new "instance" map is created by using the new operator on the "class" object, three things happens: first, a new map is created; then, every key-values that existed in the class map prototype array is copied to the newly created map, along with the constructor method; finally the constructor method is invoked, with the arguments that were given.

So you should by know understand why, in your example, you need to create a new instance of TView to be passed to TGroup.prototype. It means that when creating new instances of TGroup, they will first copy everything that was in that first object you created there. Noticed that your TGroup has id 0? That's because the TGroup copied the id of the first TView that was ever created. No matter how many TGroup you create, they will all have id 0. The goes for your x and y arguments: its the one you gave when setting TGroup's prototype that will be kept, no matter what you give in input to TView.

Note that there are other issues related to this approach. Most importantly, you can't simply override a parent method, then call your super's original version.

Now, what are the solutions? Well there are a few, actually. One of them could be to use the most recent features for ECMA Script objects, but I won't go there. There are also some JavaScript libraries that offer a more "object-oriented" strategies.

The most simple strategy, though, might simply to follow a simple idiom, that is to add an init_class name here method inside each instance prototype, then have your constructor invoke that init_() method; then, each init_() method should explicitly invoke it's parent init_*() method, passing whatever arguments that need to be given.

James
  • 4,211
  • 1
  • 18
  • 34
  • *"every key-values that existed in the class map prototype array is copied to the newly created map"* That's incorrect. JavaScript uses prototype inheritance, so each instance gets an *internal reference* to the prototype object. Nothing gets copied. Also when you set up inheritance you don't *need* to create a new instance of the parent. In fact, doing so is the less optimal approach. – Felix Kling Feb 15 '14 at 04:33
  • Indeed and indeed :) I think you might have miss one two other incorrect claims... Well, thanks for mentioning; I'll try to improve these parts, but my main objective here was to explain why ECMA Script objects (well, pre-ECMA-5) would not behave similarly to objects in other languages. Still, in the mean time, I added a disclaimer about the fact that the explanation is not exact. – James Feb 15 '14 at 04:53
  • As for the use of a new instance of the parent class to be used as a prototype, I maintained that approach (and justified it as "simpler") only because it is the approach that was used in the OP code. But I agree that it would be pertinent to elaborate on alternative approaches... – James Feb 15 '14 at 05:02
  • Actually, I might refer to T.J. Crowder's answer on this http://stackoverflow.com/questions/20825060/how-to-implement-super-mechanism-in-javascript-through-prototype?rq=1 for a better strategy that works even in older browser. – James Feb 15 '14 at 05:04
  • And your answer (that is Felix King) on http://stackoverflow.com/questions/17392857/benefits-of-using-object-create-for-inheritance?lq=1 about the Object.create approach. – James Feb 15 '14 at 05:10
1

You should not create an instance of Parent to set the prototype part of inheritance because instance specific members of the Parent will be shared (instance of Parent is Child.prototype).

Your Child class does not re use Parent constructor code and initialises instance specific members (this.something, this.somethingElse).

When inheriting through prototype and constructor functions it may be better to pass one object instead of separate parameters.

A more detailed explanation about these three you can find here.

Community
  • 1
  • 1
HMR
  • 37,593
  • 24
  • 91
  • 160