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 ?