Can someone please educate me on why the result is what it is, instead of what I expected it to be. This is driving me nuts!
var f = function(b){
console.log(this.config);
this.config.b = b;
}
f.prototype.config = {
a: 'a',
b: 'b'
};
var f1 = new f('bb');
var f2 = new f('bbb');
// logs
// { a: 'a', b: 'b' }
// { a: 'a', b: 'bb' }
// expected
// { a: 'a', b: 'b' }
// { a: 'a', b: 'b' }
It’s not the prototype that’s being modified, but rather the
configobject you’ve put on the prototype. This is correct behavior, objects referenced by the prototype are not copied when you create a new instance.f1.config === f2.config, they point to the same object.The way the prototype chain works for
getoperations is this:this.config.config, so we continue with the next step.undefined.(
setoperations work differently; asetoperation always updates or creates a property on the object it’s being set on, never further down [up?] the prototype chain.)So in your case, since your instances don’t have a
configproperty we go to the prototype. Since the prototype does have aconfigproperty, it’s used. The value of the property is an object reference, and so if you change the object (by assigning to one of its properties), it’s changed and anything else that also uses that object will see the change.Another way to look at it is to do a graph:
+------+ +------+ | f1 | | f2 | +------+ +------+ | | +------+-------+ | v +--------------------+ +--------+ | [[Proto]] assigned | | config | | via `new f` |------>| object | +--------------------+ +--------+ | +-------+-------+ | | V v +------------+ +------------+ | a property | | b property | +------------+ +------------+Another way to think of it is to get the function and prototype out of the way entirely:
In a very real way, that’s what’s happening under the covers via
new f(). You can’t directly access the property of thef1andf2instances that points to the prototype (the spec calls it the[[Proto]]property), but it’s a real thing, and it’s really there. [FYI: The latest version of the ECMAScript spec lets us do a few things directly with the[[Proto]]property, like create raw objects with a specific[[Proto]](without going via a function), but still doesn’t give us direct access to the property itself.]There are plenty of times you want all instances to share the same object (function objects, for instance!), and so the prototype is the right place for those object references to be; but if you want each instance to have its own copy of the object, you want to create that object in the constructor function. In your case:
// Logs // { a: 'a', b: 'bb' } // { a: 'a', b: 'bbb' }(Note I moved the
console.logstatements, so we see the result at the end rather than an intermediate state.)