5

I am having some Problems with JavaScript. I have got the following code:

<html>
<head>
<title>Test</title>
<script  type="text/javascript">
function Control(){
    var name;

    this.setName = function(newName){
        name = newName;
    };

    this.getName = function(){
        return name;
    };
}

function SpecializedControl(){
}
SpecializedControl.prototype = new Control();

function Form(){
    var formControls = [];

    this.addControl = function(control){
         formControls.push(control);

         alert(formControls[0].getName());
    };
}

var form = new Form();

var control1 = new SpecializedControl();
control1.setName("Control1");
form.addControl(control1);

var control2 = new SpecializedControl();
control2.setName("Control2");
form.addControl(control2);

</script>
</head>
<body>
</body>
</html>

The SpecializedControl inherits from the Control class.

The addControl function in the Form class just adds the control to an array.

The problem is that when I add more than one SpecializedControl, the values in the array are kind of overriden, that means that when I access the first item in the array, which should be "Control1" I get "Control2". Control1 is not in the array any longer.

When I use the same function with Control objects as parameters everything works as expected.

Does someone know why this happens and what can be done to correct this?

0

2 Answers 2

5

The values in the array aren't being overridden; the problem is both controls share the same name variable. Because the Control function only executes once, there is only one name variable ever declared.

You have two main options to fix this. (1) Make name an instance variable which is specific to each individual control (e.g. this._name). (2) Execute the Control function from inside the SpecializedControl constructor. (Actually, IMO, for a well-balanced and thorough inheritence model you should be doing a bit of both of these methods).

Here are three working solutions. The first two use options (1) and (2) respectively. The third combines both methods and is the way I would do it (but requires joi).

Option 1:

function Control(){

    this.setName = function(newName){
        this._name = newName;
    };

    this.getName = function(){
        return this._name;
    };
}

Option 2:

function SpecializedControl(){
    Control.apply(this, arguments);
}

Option 3:

var Control = joi.Unit.sub(function() {

    function constructor() {
        this.base();
    }

    constructor.prototype = {
        '#name': null,
        setName: function(name) {
            this['#name'] = name;
        },
        getName: function() {
            return this['#name'];
        }
    };

    return constructor;

}());

var SpecializedControl = Control.sub(function() {

    function constructor() {
        this.base();
    }

    return constructor;

}());

var Form = joi.Unit.sub(function() {

    function constructor() {
        this.base();
        this['#formControls'] = [];
    }

    constructor.prototype = {
        '#formControls': null,
        addControl: function(control) {
            this['#formControls'].push(control);
            alert(this['#formControls'][0].getName());
        }
    };

    return constructor;

}());

var form = new Form();

var control1 = new SpecializedControl();
control1.setName("Control1");
form.addControl(control1);

var control2 = new SpecializedControl();
control2.setName("Control2");
form.addControl(control2);​
Sign up to request clarification or add additional context in comments.

5 Comments

I recommend the second option, calling the constructor of the superclass in the subclass constructor. It seems like the right thing to do. The first option requires exposing the name variable and the third option uses an extra library which tries to implement regular class-based inheritance as a replacement of JavaScript's prototypical inheritance.
joi uses prototypical inheritance, not classical inheritance
Oh, my bad. Still, the #name is only internal by convention and doesn't actually prevent you from accessing it from outside. With closures, you can have truly private variables.
Option 2 looks good to me and solved the problem. Thank you :-)
Yeah, Option 2 is a good choice. They all have their benefits and some cons. Just to shoot the breeze with Mattias, one con of Option 2 is that it doesn't allow the setName and getName methods to be overridden on the constructor's prototype. It also uses a little more memory than putting the functions on the prototype due to the fact that new closures are generated each time the constructor is called. Still, nothing wrong with Option 2; just worth pointing out there are reasons you might choose another option.
5

Your get/setName functions are getting/setting the value of the name variable that is local to the Control constructor function.

The only time you invoked that function, was to create an instance as the prototype object of SpecializedControl. So every time you invoked setName from a SpecializedControl instance, that single variable is being updated.

So since the get/setName methods that reference that variable are in the prototype chain of all SpecializedControl instances, they'll all be observing the same name.


In setName, you should do...

this.name = newName;

And in getName, you should do...

return this.name;

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.