What I currently have is an object, say b, that inherits an other object, say a. In object a I have an object as property(value). To clear things up:
var a = (function(){});
a.prototype.value = {
val : "value a"
};
var b = (function(){
this.value.val = "b";
alert("object b: " + this.value.val);
});
b.prototype = Object.create(a.prototype);
b.prototype.constructor = a;
new b(); // alerts "object b: b"
http://jsfiddle.net/qqmtb846/
So far, so good. In object b I set the 'val' property of the 'value' object to another value. As expected the value is set(value is now 'b').
When I create another object, lets say c, I would expect that I get the original value('value a').
This is not the case. I'm sure this is due object reference.
var a = (function(){});
a.prototype.value = {
val : "value a"
};
var b = (function(){
this.value.val = "b";
alert("object b: " + this.value.val);
});
var c = (function(){
alert("object c: " + this.value.val);
});
b.prototype = Object.create(a.prototype);
b.prototype.constructor = a;
c.prototype = Object.create(a.prototype);
c.prototype.constructor = a;
var ObjectB = new b(); // alerts "object b: b"
var ObjectC = new c(); // alerts "object c: b", I want that "object b: value a" is displayed
http://jsfiddle.net/eb6dsgv9/1/
I want to use an object as property in the super class because most of the values will be the same, but sometimes they have to change. There is still a referance despite it's a new instance.
1) Is this some kind of design weakness of JavaScript?
2) How can I resolve this?
This is expected behavior. In your constructor, this.value refers to the same object as a.prototype.value. You can overwrite this.value with a new value (which will leave a.prototype and its properties unchanged), but if you modify this.value.val you are modifying the val property of a.prototype.value.
I don't know what you mean by "design weakness" so I can't answer (1). This is simply the way JavaScript's prototypal inheritance works.
As for how to resolve it, I think you need to show us a more concrete example, as I don't think there's a simple one-size-fits-all solution to this. It could be resolved by using a deep copy instead of Object.create (though depending on how you do the deep copy you could lose any methods on the prototype, if it has any). To put it simply, if you need to modify the properties of the prototype's properties, you are going to run into a situation like this.
Probably a better solution is to have a separate set of data values for each instance. Having all your instances share the same set of data values is creating a mess in your situation.
Initialize the properties in a's constructor:
var a = function () {
this.value = {
val: "value a"
};
};
And then you can call that constructor from b and c's constructor:
var b = function () {
b.prototype.constructor.call(this);
this.value.val = "b";
console.log("object b: " + this.value.val);
};
var c = function () {
c.prototype.constructor.call(this);
console.log("object c: " + this.value.val);
};
http://jsfiddle.net/eb6dsgv9/6/
To add to JLRishe answer, you can make it work this way:
http://jsfiddle.net/eb6dsgv9/3/
var a = (function(){
this.value = {
val : "value a"
}
});
var b = (function(){
a.call(this);
this.value.val = "b";
alert("object b: " + this.value.val);
});
var c = (function(){
a.call(this);
alert("object c: " + this.value.val);
});
var ObjectB = new b();
var ObjectC = new c();
Here, 'value' is property of 'a', not a.prototype. In 'a' and 'c' constructors you create new, separate 'a', so you get a fresh 'value a' each time. The downside of it is you spend more memory for all 'a' instances.
I based my answer on this: Javascript property inheritance
Object.create(a.prototype) creates a brand new object with it's [prototype] pointing to a.prototype.
Your new ObjectB and ObjectC are brand new objects with their [prototype] pointing to c.prototype resp. b.prototype which is
your above new empty object with [prototype] pointing to a.prototype.
In your b constructor you try set the property value.val to "b" but ObjectB does not have such a property value. javascript than looks into b prototype which is again
empty object but has the link in it's [prototype] to another object which is a.prototype. that object has property value so it is set value.val = "b" .
In case of ObjectC the linking is the same, the only difference is that you are looking for property value. Javascript looks in every objects in the [prototype] chain until it finds the value property which is in your a.prototype object.
It is not design weakness of javascript it is it's strength actually. javascript is not OOP language.
Related
In this code, the prototype can still change.
How I can prevent changes to the prototype?
var a = {a:1}
var b={b:1}
var c = Object.create(a)
Object.getPrototypeOf(c) //a
c.__proto__ = b;
Object.getPrototypeOf(c) //b
var d = Object.create(null)
Object.getPrototypeOf(d) //null
d.__proto__ = b;
Object.getPrototypeOf(d) //null
How I can prevent changes to the prototype?
I assume you are not talking about mutating the prototype object itself, but overwriting the prototype of an existing object.
You can use Object.preventExtensions() to prevent that:
var a = {a:1}
var b = {b:1}
var c = Object.create(a)
Object.preventExtensions(c)
console.log(Object.getPrototypeOf(c)) //a
c.__proto__ = b; // Error
But that also means you cannot add any new properties to it. You could also use Object.freeze() or Object.seal() depending on your needs, which restrict modifications to the object even more.
There are no other ways though.
Yes we can ,use Object.freeze.
Object.freeze() method freezes an object: that is, prevents new properties from being added to it; prevents existing properties from being removed; and prevents existing properties, or their enumerability, configurability, or writability, from being changed. In essence the object is made effectively immutable. The method returns the object being frozen.
See this freeze reference
check this snippet
var a = {a:1}
var b={b:1}
var c = Object.create(a)
Object.getPrototypeOf(c) //a
Object.freeze(c);
c.__proto__ = b;//throws error now
console.log(Object.getPrototypeOf(c)) //a
var d = Object.create(null)
Object.getPrototypeOf(d) //null
d.__proto__ = b;
Object.getPrototypeOf(d) //null
Hope this helps
This piece of code:
function A(){}
A.prototype = {
a : 5
}
var b = new A();
console.log('b.a : ' + b.a);
A.prototype = {
a : 10
}
console.log('b.a : ' + b.a);
produces:
b.a : 5
b.a : 5
While the piece of code below modifies the prototype for the existing instances of A:
function A(){}
A.prototype = {
a : 5
}
var b = new A();
console.log('b.a : ' + b.a);
A.prototype.a = 10;
console.log('b.a : ' + b.a);
and produces:
b.a : 5
b.a : 10
How is that so?
Confusion between prototype and [[prototype]] (or __proto__).
The former is a (normal, non-magical) property that will get set as the latter (which is internal, and generally not to be messed with) in any object that is created from a constructor. Thus, when you do b = new A(), A's prototype becomes b's [[prototype]]. When we say that in JavaScript attributes are looked up first on the object and then up the prototype chain, it is this [[prototype]] that is meant.
When you change A.prototype.a in your second case, you changed b's [[prototype]] at the same time (as it's the same object).
When you assign a new object to A.prototype, it is no longer the same object as b's [[prototype]]. Thus, b.a is divorced from A.prototype.a.
You need to read this to understand JavaScript Object Model better.
MDN JavaScript - Details_of_the_Object_Model
There are a lot of questions talking about cloning a JS object, but it seems this one does not exist.
I have a function A extending a function B as follows:
function A () {}
function B () {}
B.prototype = new A()
I then have an object b of type B. I'd like to clone it, and preserve its type, so that the clone of b would be of type B.
Here is how I'm cloning it: (jsfiddle)
function A() {}
function B() {}
B.prototype = new A()
var b = new B()
var bPrime = new b.constructor()
$("#a").text(b instanceof A)
$("#b").text(b instanceof B)
$("#aPrime").text(bPrime instanceof A)
$("#bPrime").text(bPrime instanceof B)
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
b is of type A: <span id="a"></span>
<br>b is of type B: <span id="b"></span>
<br>bPrime is of type A: <span id="aPrime"></span>
<br>bPrime is of type B: <span id="bPrime"></span>
In my example, the clone is of type A. How could I have a clone of b that is typed B?
You need to set B.prototype.constructor back to B. As it is, it inherits from A, and the constructor is A. So when you do new b.constructor(), you're actually getting a new A().
function A() {}
function B() {}
B.prototype = new A()
B.prototype.constructor = B
var b = new B()
var bPrime = new b.constructor()
$("#a").text(b instanceof A)
$("#b").text(b instanceof B)
$("#aPrime").text(bPrime instanceof A)
$("#bPrime").text(bPrime instanceof B)
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
b is of type A: <span id="a"></span>
<br>b is of type B: <span id="b"></span>
<br>bPrime is of type A: <span id="aPrime"></span>
<br>bPrime is of type B: <span id="bPrime"></span>
Object.create()
The Object.create() method creates a new object with the specified prototype object and properties.
function A() {}
function B() {}
B.prototype = new A()
var b = new B();
var c = Object.create(b);
console.log(c instanceof B); // -> true
fiddle
Clone is a pretty ambiguous word. If you want to create a totally new object with no references to anything related to the source object, that's difficult to do without knowing something about the object in question in advance, though you can do wonky prototype-based stuff if you want (not particular advisable). In ES6, it becomes literally impossible to reliably deep clone things in this "property-by-property" sense because of the possibility of an object / constructor / prototype possessing private symbols, WeakMaps, etc.
However you can use Object.create(b) to create a new object that takes b as its own prototype. If you replace
var bPrime = new b.constructor()
with
var bPrime = Object.create(b)
Then you get four trues. However, since b acts as the prototype of bPrime here, changes to b will be reflected in bPrime unless bPrime provides its own definitions that override those things. This is probably not desired for cloning purposes because:
b.prop; // undefined
bPrime.prop; // undefined
b.prop = 7;
bPrime.prop; // 7!
But there’s some refinement we can do:
var bPrime = Object.create(Object.getPrototypeOf(b));
Now you get four trues, but further modifications to b will not be reflected on bPrime.
I have a problem I can't solved because I can't explain this behaviour :
var A = function(value) {
this.prop = value;
};
A.prototype = {
prop: 0
};
var a = new A(1);
var b = new A(2);
console.log(a.prop);
console.log(b.prop);
output :
1
2
But, with this code (almost the same) :
var A = function(value) {
this.prop.value = value;
};
A.prototype = {
prop: {
value: 0
}
};
var a = new A(1);
var b = new A(2);
console.log(a.prop.value);
console.log(b.prop.value);
I have this output :
2
2
Can anybody explain me this ?
Thanks...
EDIT :
Here's a solution :
var A = function(value) {
this.prop = {};
this.prop.value = value;
};
A.prototype = {
};
var a = new A(1);
var b = new A(2);
console.log(a.prop.value);
console.log(b.prop.value);
In example 1, this.prop is a primitive type, which is referenced by value and thus not shared between instances.
In example 2, this.prop is an Object whose reference is initialized from the prototype from a single object, so this single object is shared by all instances.
In the last "example" solution, you create a new object with = {}, so all instances now have their own object.
More details about primitive types : Primitive value vs Reference value
prototype is created only once and it's only a simple object whose childs are attached for all instances of a function it belongs to.
So it's basically the same as if you write:
var obj = { value: 0 };
var a = {}, b = {};
a.obj = obj;
b.obj = obj;
obj.value = 2;
as you can see both a.obj and b.obj references to the same obj and both a.obj.value and b.obj.value will be 2 in this example
This is happening because in JS, objects are passed by reference, while primitives are not.
Since the prototype is shared between the 2 instances, modifying a object on it will update all instances.
Think of a prototype property as a property shared amongst all instances.
Yet you can override it on each instance, that 's what you do in the first example.
Once overriden in each instance, you do not access any more to the prototype property with obj.prop, which now refers to the instance property. You would need to use obj.prototype.prop to read it again, but this syntax is illegal : you can use obj.__proto__.prop (non standard) or Object.getPrototypeOf(obj).prop (EcmaScript 5) to do so.
In the second instance, you do not change the property in the constructor : rather you change a property of this property. So both constructor access to the very same object, then change one of its property value, so the last to set it will 'win'. Here the prototype property is not overriden ('hidden') by the instance property and accessing obj.prop in fact access 'obj.prototype.prop'.
Let's say I have the following code;
var A = {a:10};
var B = {b:20};
B.prototype = A;
alert(B.a);
I am getting undefined for B.a .
Am I doing something wrong? How do I set the prototype for object literal ?
I know how to do for Constructor object. So the following code works perfect
function A(){this.a=10}
function B(){this.b=20}
B.prototype = new A();
b = new B;
alert(b.a);
How do I do it for object literal ?
Objects inherit from their constructor's prototype property, not their own. The constructor's prototype is assigned to the internal [[Prototype]] property that is available in some browsers as the __proto__ property.
So for b to inherit from a, you need to put a on b's inheritance chain, e.g.
Classic prototype inheritance:
var a = {a: 'a'};
function B(){}
B.prototype = a;
var b = new B();
alert(b.a); // a
Using ES5 Object.create:
var a = {a: 'a'};
var b = Object.create(a);
alert(b.a); // a
Using Mozilla __proto__:
var a = {a: 'a'};
var b = {};
b.__proto__ = a;
alert(b.a); // a
The prototype property is usually present in a Function object. This prototype should be an object, and this object is used to define the properties of an object created with a constructor.
// Plain object, no prototype property here.
var plainObject = {one: 1, two: 2};
// Constructor, a prototype property will be created by default
var someConstruct = function() {
// Constructor property
someConstruct.constructProp = "Some value";
// Constructor's prototype method
someConstruct.prototype.hello = function() {
return "Hello world!";
}
};
// Another constructor's prototype method
someConstruct.prototype.usefulMethod = function() {
return "Useful string";
}
var someInstance = new someConstruct();
console.log(someInstance.hello()); // => Hello world!
console.log(someInstance.usefulMethod()); // => Useful string
console.log(someConstruct.constructProp); // => Some value
console.log(someConstruct.prototype); // => {usefulMethod: function, hello: function}
console.log(plainObject.prototype); // => undefined
So, plain objects have no prototypes.
Functions which work as constructors do have prototypes. These prototypes are used to fill an instance created with each construct.
Hope that helps :)
Only when using Function object that prototype is used, e.g. when you use a constructor. But no need of that for object literals.
Both of them are very good techniques, so it depends on what you want to do in a project and the JavaScript pattern you are using or like.