質問

Here is my superclass:

function Element() {
   var name;
   this.setName(n) = func()...{};
   this.getName() = func()..{return name};
}

My another child class:

Select = null;
...
Select =
   function (n) {
       if (typeof n !== "undefined")
          this.setName(n);
   ...
}
Select.prototype = new Element();
Select.prototype.constructor = Select;

So, what kind of "weird moment" am I talking about? Here it is:

var e1 = new Select("element1");
e1.getName();  // return "element1"
var e2 = new Select();  // WITHOUT NAME
e2.getName();  // return "element1"!!! should be ""!

This is a fairly predictable behavior, but how to get around this? Of course, i can make something like a this.clear() in Element, that will clear properties and put this method in Select function, but maybe there is a proper solution?

役に立ちましたか?

解決

You should add this line Element.call(this, n) into Select. It's quite hard to explain and I feel grumpy because I don't like fake private properties in javascript, but well, I must provide some details in order for you to understand what you are currently doing, otherwise I will not be able to get to sleep.

So, doing new Element() creates a new context where name can live without disturbing anyone. Additionally, two new functions called setName and getName are created, and bound to the prototype object of Select.

From now on, if you create a new instance of Select, you can call these functions since they are available in the prototype of the instance. This actually happens doing new Select("element1"). Indeed, n is defined, so, setName is called from this, which refers to the instance.

But most importantly, calling setName with n set to "element1" will also set name to "element1". Then, if you create a second instance of Select, without defining n, setName is not called, so, name remains set to "element1", for both instances.

Why for both instances? Because both instances share the same setName method, the one bound to the prototype, and this method refers to a unique name variable - remember that Element was called only once.

Finally, why this new line Element.call(this, n) prevents name from being shared? Because each call to Element creates a new context, that is to say, a new name, a new setName and a new getName, and binds these two methods to the newly created instance.

Well, hope this is the morning for you, I would be sorry if you get into sleeping disorders by my fault...


As mentionned by Bergi and HMR, this way of creating the prototype - Child.prototype = new Parent - is out of fashion and will lead you to a dead end. Keep in mind that the prototype is kind of a template for creating instances. Knowing this and considering your code, we can make at least two observations :

  1. As you know, a constructor is intended to initialize instances. In your case, the initialization process - what's inside Element - is unnecessarily executed since the goal is currently to set the template upon which instances will be created.
  2. Let's say that name is required in new Element(name), otherwise your program crashes. What kind of name would you give to the prototype? This question is obviously useless, since you would not want to give a name to a template.

To bypass these problems you need a middleman as shown in the below code (copied from the link shared by Bergi). As you can see, the creation of the prototype is delegated to a "dummy" constructor which is empty. Doing so allows to resolve the two problems raised above.

function Dummy () {}
Dummy.prototype = Element.prototype;
Select.prototype = new Dummy();
Select.prototype.constructor = Select;

Alternatively, you could use the built-in Object.create() :

Select.prototype = Object.create(Element.prototype);
Select.prototype.constructor = Select;

Further reading :

他のヒント

You should use the prototype of Element so the variables are not shared between the instances:

function Element() {}    
Element.prototype.setName = function(n) { this.name = n; };
Element.prototype.getName = function() { return this.name; };

In this case e2.getName() will return undefined.

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top