1

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?

ovnia
  • 2,412
  • 4
  • 33
  • 54
  • Don't like private properties. –  Feb 11 '14 at 22:49
  • @wared yeeh awesome solution) – ovnia Feb 11 '14 at 22:50
  • 1
    You should add this line `Element.call(this, n)` into `Select`. –  Feb 11 '14 at 22:50
  • I know. I would say that there is no access modifier in javascript and I personally don't see any good reason to mimic such a feature. –  Feb 11 '14 at 22:58
  • a big thank to you, @wared. Please, make a full answer with `Element.call(this, n)` and i'll make it accepted. – ovnia Feb 11 '14 at 23:08
  • I'm playing with your code and I realize that there is a mistake : `this.setName() = function () {}` throws `ReferenceError: Invalid left-hand side in assignment`. You have to remove the first pair of parenthesis. –  Feb 11 '14 at 23:16
  • 1
    More on how to inherit and use constructor functions can be found here http://stackoverflow.com/a/16063711/1641941 like how to re use parent constructor and why you should not create an instance of Parent to set up the prototype part of Child. – HMR Feb 12 '14 at 00:15
  • possible duplicate of [Define Private field Members and Inheritance in JAVASCRIPT module pattern](http://stackoverflow.com/questions/12463040/define-private-field-members-and-inheritance-in-javascript-module-pattern) – Bergi Feb 12 '14 at 00:50
  • 1
    Btw, you might consider not overwriting the global [`Element` interface](https://developer.mozilla.org/en-US/docs/Web/API/Element)… – Bergi Feb 12 '14 at 00:54
  • @wingsofovnia Updated : http://stackoverflow.com/a/21715156/1636522. Hope this is clear enough - I took the opportunity to practice my English... –  Feb 12 '14 at 10:17

2 Answers2

3

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 :

Community
  • 1
  • 1
  • 1
    Also, `Element` should actually be [never called instead of once with `new`](http://stackoverflow.com/questions/12592913/what-is-the-reason-to-use-the-new-keyword-here) – Bergi Feb 12 '14 at 00:52
2

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.

dan-lee
  • 14,365
  • 5
  • 52
  • 77
  • With this approach the name variable would be exposed. – Péter Feb 12 '14 at 11:42
  • @Péter And it does not change anything since, even private, the variable is still mutable through `setName`. This is why I feel grumpy :D +1 Dan Lee. –  Feb 12 '14 at 11:59
  • Yes, but we don't saw the the source of the setName method and I thought @wingsofovnia choose this structre on purpose. – Péter Feb 12 '14 at 12:08
  • @Péter OP did not mention that the variable has to be private (we're just assuming here). It's just another viable solution. – dan-lee Feb 12 '14 at 12:12
  • @Péter I wasn't blaming you, I just wanted to take the opportunity to detail my point of view. That would be great to have some clarifications from the OP. –  Feb 12 '14 at 12:23
  • That's right. We can only make assumptions. I just assumed based on the structure of his code, the comment (2nd comment to reply the 1st) and the answer he accepted. – Péter Feb 12 '14 at 14:06