Weird issue on properties with John Resig simple inheritance

javascript, prototypal-inheritance, prototype

Solution

That's because `options` is a property of the class (on its prototype), not the object.

Verify it by adding this to the bottom of your second example:

console.log( p.options == p2.options ); // true

Explanation:

When using abstraction layers, it is very important to remember the underlying principles, so that you don't get bitten by the abstractions' leaks.

Your 1st example:

Let's re-write your class definition with regular Prototypal inheritance:

var Person = function (options) {
    if (options) {
        this.options = options;
    }
};

Person.prototype.options = {value: 2};

var john = new Person();

`john` will now be a new instance of `Person` and will not have an `options` property:

john.hasOwnProperty('options'); // false

When trying to access the `options` via `john.options`, you'll get the `{value: 2}` from the prototype chain.

If however you were to create this new `Person` object by supplying your `options` to the constructor:

var michael = new Person({value:5});

`michael` will have an `options` property:

michael.hasOwnProperty('options'); // true

Here's a simple test comparing the two:

john.options == Person.prototype.options // true
michael.options == Person.prototype.options // false

Your 2nd example:

Now consider your second example, written in native Prototypal inheritance:

var Person = function (options) {
    if (options) {
        for (var name in options) {
            this.options[name] = options[name];
        }
    }
};

Person.prototype.options = {value: 2};

Regardless of whether you pass `options` into the constructor, your new object will never have its own `options` property. All you're doing is augmenting the `options` on the prototype:

var john = new Person();
john.options // {value:2}
john.hasOwnProperty('options'); // false
john.options == Person.prototype.options // true

var michael = new Person({value:5});
michael.options // {value:5}
michael.hasOwnProperty('options'); // false
michael.options == Person.prototype.options // true

john.options // {value:5}
// Why? Because they share the same `options` property from the prototype
john.options == michael.options // true

If you then go and assign a new object to `john`, it won't affect the `options` on the prototype:

john.options = {value:10};
john.options // {value:10}
john.options == Person.prototype.options // false
michael.options // {value:5}
michael.options == john.options // false
michael.options == Person.prototype.options // true

Solution:

To get the functionality you want in your second example, you'll have to follow these steps:

- Create a new `options` property from within the constructor (the `init` method).

- Copy all the properties from `Person.prototype.options`.

- Finally, copy all the properties from the `options` argument.

Here's how you might do that:

var Person = Class.extend({
    options: {value: 2},
    init: function(options) {
        if (options) {
            var name, prototypeOptions = Person.prototype.options;
            this.options = {};

            for (name in prototypeOptions) {
                this.options[name] = prototypeOptions[name];
            }

            for (name in options) {
                this.options[name] = options[name];
            }
        }
    }
});

If you're using jQuery or underscore, you can use their `extend` method, which makes all this trivial:

var Person = Class.extend({
    options: {value: 2},
    init: function(options) {
        if (options) {
            this.options = $.extend({}, Person.prototype.options, options);
        }
    }
});

Simpler solution:

Depending on your needs, you might not even need the `options` to be on the prototype in the first place. Just add it to your object in the constructor (the `init` method), and be done with it:

var Person = Class.extend({
    init: function(options) {
        this.options = {value: 2};
        if (options) {
            for (var name in options) {
                this.options[name] = options[name];
            }
        }
    }
});

and once again, if you're using jQuery or underscore, this becomes even simpler:

var Person = Class.extend({
    init: function(options) {
        this.options = _.extend({value: 2}, options);
    }
});

Problem

I'm referring to this article. I'm basically creating a class with an `init` function which set an `options` attribute. see http://jsbin.com/usaboh/1/edit for the working example (enable the console to see the output). Below the first example works fine, however the second still get the `options` value of the first "instance". Works as expected : ``` var Person = Class.extend({ options: {value: 2}, init: function(options){ if(options) { this.options = options; } } }); var p = new Person({value: 5}); console.log(p.options); //{"value": 5} (ok) var p2 = new Person(); console.log(p2.options); //{"value": 2} (ok) ``` Doesn't work as expected : ``` var Person = Class.extend({ options: {value: 2}, init: function(options){ if(options) { for (var name in options) { this.options[name] = options[name]; } } } }); var p = new Person({value: 5}); console.log(p.options); //{"value": 5} (ok) var p2 = new Person(); console.log(p2.options); //{"value": 5} (why 5 ??? shouldn't be 2 ?) ``` Why the value of `options` in the second instance is not `{"value": 2}` like in the first example ?

Original source