object literal used in inherited property changes all instances of the class - javascript

I have the following code (ObjA) and it works as I would expect, instance 'a1' has the same properties as 'a2' but with different values.
function ObjA() {
this.objLiteral = {};
this.propA = 0;
}
var a1 = new ObjA();
a1.objLiteral['hello'] = 3;
a1.propA = 1.5;
var a2 = new ObjA();
a2.objLiteral['goodbye'] = 4;
a2.propA = 2;
debugger info for a1 and a2:
http://www.flickr.com/photos/76617756#N02/6879283032/
Next, I have the following ObjB that inherits from ObjA. Instance 'b1' and 'b2' have the same properties and different values for properties propA and propB but for some reason, objLiteral is the same in both as if it was referencing the same object.
ObjB.prototype = new ObjA();
ObjB.prototype.constructor=ObjB;
function ObjB() {
this.propB = 2;
}
var b1 = new ObjB();
b1.objLiteral['hello2'] = 6;
b1.propA = 4;
b1.propB = 5;
var b2 = new ObjB();
b2.objLiteral['goodbye2'] = 8;
b2.propA = 6;
b2.propB = 7;
debugger info for b1 and b2:
http://www.flickr.com/photos/76617756#N02/6879283088/
Can somebody help me understand what is happening? What do I have to do to ge what I am expecting?
Your help is much appreciated.

Well, both objects b1 and b2 have the same prototype, namely an instance of ObjA:
ObjB.prototype = new ObjA();
hence they inherit and have access its properties. b1.objLiteral and b2.objLiteral refer to the same object:
and
> b1.objLiteral === b2.objLiteral
true
To fix that, you have to create a new object of each instance. This is normally done by calling the "parent" constructor inside the "child" constructor:
function ObjB() {
ObjA.call(this);
this.propB = 2;
}
ObjA.call(this) will call ObjA and within that function, this will refer to the argument passed to call [MDN], in this case the new instance of ObjB:
As you can see, objLiteral is now property of each instance:
> b1.objLiteral === b2.objLiteral
false
To avoid this confusion in the first place, setting an instance of the parent constructor as the prototype of the child constructor should be avoided. What if the parent constructor expects instance specific arguments? What would you pass to ObjA in this case?
It's better to set the prototype of the child constructor to the prototype of the parent constructor (with one level of indirection) and call the parent constructor like above:
function inherit(Child, Parent) {
var Tmp = function() {};
Tmp.prototype = Parent.prototype;
Child.prototype = new Tmp();
Child.prototype.constructor = Child;
}
inherit(ObjB, ObjA);
Tmp to prevent extending Parent.prototype if you extend Child.prototype later on.
Regarding propA:
It "works", because you are assigning a new value to it.
Here are again the objects, after assigning properties to x.objLiteral and x.propA. You can see that the objects don't have an own objLiteral property, but an own propA:
If instead you assign a new value to objLiteral, e.g. through
b1.objLiteral = {hello: 42};
b1 will now have its own property objLiteral, which shadows the inherited one:

Related

Inheriting properties from prototype

function classA(){};
classA.prototype.age =25;
var obj = new classA();
console.log(obj.age) //returns 25
Now, when I do:
classA.prototype = {};
Why obj.age is still returning 25?
This is happening because you pointed the classA.prototype to a new object but you didn't change the object that obj.prototype points to.
What is happening is:
classA.prototype ------> A
// create object with new classA()
// now its prototype also points to A
obj.prototype ------> A // points to the same prototype
now you reasign the prototype of classA
classA.prototype ------> B
but the prototype of obj still points to A (it was never changed)
obj.prototype ------> A
So all you did was change the prototype reference of classA after the prototype reference for obj was already established.
If you want to have obj see the changes on the prototype, then change the object it references (A in the example above) and not the reference itself:
Sample code:
function classA(){};
classA.prototype.age =25;
var obj = new classA();
console.log(obj.age) // 25
// change the object itself, not the reference
delete classA.prototype.age;
console.log(obj.age); // undefined
If your code is in the following order:
function classA(){};
classA.prototype.age = 25;
classA.prototype = {};
var obj = new classA();
console.log(obj.age) // returns undefined
The result will be indeed your excepted behavior. It depends on when you set the prototype of the classA function. This will be demonstrated by the following code snippet:
function classA(){};
classA.prototype.age = 25;
var obj1 = new classA();
classA.prototype = {};
var obj2 = new classA();
console.log(obj1.age) // returns 25
console.log(obj2.age) // returns undefined
Hence, a object is only affected by the changes to a prototype of a function, when this happens before the object's creation.
reassigning your prototype after creating the object has no effect on the previously created object , it will still have the old prototype when it was created. New prototye will only effect newly created objects

Object property in prototype

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'.

Setting prototype for Object Literal

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.

Behavior of primitive types in prototypes

Every instance have a link to prototype of the constructor using which it is created. So every instance shares the prototype members. If a change to the shared prototype member is made through one instance it is reflected to all other instances. Why this doesn't seem to work with primitive types as can be seen below:
//creating an empty object type
function OBJTYPE(){};
//adding primitive value and reference value as a memeber to
//the prototype of the object type
OBJTYPE.prototype.value = 0;
OBJTYPE.prototype.arr = ["red","green","blue"];
//creating instances of the object type
var obj1 = new OBJTYPE();
var obj2 = new OBJTYPE();
//outputting the prototype members through both the instances
document.write(obj1.value + "<br />"); //0
document.write(obj2.value + "<br />"); //0
document.write(obj1.arr + "<br />"); //red,green,blue
document.write(obj2.arr + "<br />"); //red,green,blue
//changing value of primitive member
obj1.value = 1; //creates a new instance property
//modifying the reference type member - pushing a value on the array
obj1.arr.push("black"); //modifies the prototype property
//outputting the prototype members through both the instances
document.write(obj1.value + "<br />"); //1 //.value from instance
document.write(obj1.__proto__.value + "<br />"); //0 //.value from prototype
//works in Firefox, Safari, and Chrome
document.write(obj2.value + "<br />"); //0 //.value from prototype
document.write(obj1.arr + "<br />"); //red,green,blue,black
document.write(obj2.arr + "<br />"); //red,green,blue,black
As you can see above changing value of primitive member creates a new instance property called value on obj1, instead of overwriting same named property in the prototype. Thus when accessing obj1.value property it returns instance property which masks the prototype property. That's why the two instances instances show different values of value.
However this doesn't reference type do not behave similarly as can be seen from above. Why?
You were pushing to the array, not assigning a new array like you did with the primitive value. This will work as expected:
obj1.value = 1;
obj1.arr = [];
Note that setting a value never sets on the prototype, but on the object itself.
When you write to a property in an object, the new value is stored on the object, not the prototype.
The array issue you have works like this:
Get me the array that is stored on obj1, and modify the array:
obj1.arr.push("black");
Write a new value on the instance of obj1:
obj1.arr = [1,2,3];
I will expand if you need me to.
Yes, there are lots of ways to do this, depending on what you need.
1 What seems most obvious to me is that you want to identical objects, which in this case I would just make one object.
var obj1 = {
"arr": [],
"value": 0
};
// I can garuntee any editing you do on obj1 will be reflected on obj2
var obj2 = obj1;
2 Make a prototype accessor method:
function OBJTYPE(){};
//adding primitive value and reference value as a memeber to
//the prototype of the object type
OBJTYPE.prototype.value = 0;
OBJTYPE.prototype.arr = ["red","green","blue"];
OBJTYPE.prototype.proto = function (name, optValue) {
if (arguments.length === 2) {
return OBJTYPE.prototype[name] = optValue;
}
return OBJTYPE.prototype.proto[name];
};
var obj1 = new OBJTYPE();
var obj2 = new OBJTYPE();
obj1.proto('value', 1); //on the prototype
// The rest will behave like desired
3 Store direct link to proto:
function OBJTYPE(){};
//adding primitive value and reference value as a memeber to
//the prototype of the object type
OBJTYPE.prototype.value = 0;
OBJTYPE.prototype.arr = ["red","green","blue"];
OBJTYPE.prototype.proto = OBJTYPE.prototype;
//creating instances of the object type
var obj1 = new OBJTYPE();
var obj2 = new OBJTYPE();
//changing value of primitive member
obj1.proto.value = 1; //on the prototype
// The rest will behave like desired
4 And last but not least, use ES5's method getPrototypeOf
function OBJTYPE(){};
OBJTYPE.prototype.value = 0;
OBJTYPE.prototype.arr = ["red","green","blue"];
//creating instances of the object type
var obj1 = new OBJTYPE();
var obj2 = new OBJTYPE();
//changing value of primitive member
Object.getPrototypeOf(obj1).value = 1; //on the prototype
// The rest will behave like desired
obj1.value = 1;
This created a new property "value" of obj1 and doesn't mutate the prototype. That's why obj2.value returns the same old value which was set initially in prototype.
If you want to mutate the prototype via obj1 so that it affects all other instances of the function as well, change the property by going down the prototype chain:
obj1.__proto__ === OBJTYPE.prototype; //true
obj1.__proto__.value = 1;
obj1.value; //1
obj2.value; //1
I came to this question from similar example:
function Person(first, last, age) {
this.name = {
first,
last
};
this.age = age;
};
let person1 = new Person('Tammi', 'Smith', 17);
let person2 = Object.create(person1);
person2.name.first = "Bob" // modifies prototype value on person1 instance
person2.age = 18 // creates new Property on person2
I believe the reason is that object that holds "name" Property on Prototype person1 object is accessed by runtime as associative array so person2.name.first = "Bob" is actually person2.name['first'] = "Bob".
When I try to set "name" Property like this though:
person2.name = "Bob";
then new Property is created on the person2 instance effectively shadowing person1 "name" object that holds first and last Properties. This explains behaviour why changing content of reference instance is different then setting new reference instance all together.

static and non static data in same class (object) in javascript

I understand that there are two ways of creating classes in JavaScript
var MyObj = function(val)
{
this.vari = val;
}
Where val will be a non static member.
Whereas,
var MyObj = new Object();
MyObj.vari = "";
This is a single object so members of it here will act like static members. My question is that how do we have both static as well as non static content in same class?
There are no classes in JavaScript, only objects.
When you create a new object you are "extending" from the function's prototype, not the function itself. In order to make a variable or function appear to be static you would have to attach it to the function used to create the object, and not its prototype.
js> var Foo = function(){ this.member = "member"; };
js> Foo.staticMember = "staticMember";
js> var f = new Foo();
js> Foo.member;
undefined
js> Foo.staticMember;
"staticMember"
js> f.member;
"member"
js> f.staticMember;
undefined
Using your first example:
var MyObj = function(val) {
this.vari = val;
};
MyObj.var2=value;
Will create a new "static" property callable as:
var o = MyObj.var2;
The first one initializes the MyObj variable with the anonymous function - it does not create the class. The MyObj may be later used as the object constructor that initializes one object field - vari. There is nothing "statis" in OOP meaning.
The second one creates the object of the Object type and initializes its property. Again, there is nothing static.
In OOP "statis" means the member of the class, e.g. variables and objects that belong to class definition. For example, the following schema demonstrates this concept:
function MyClass() {
this.objectProperty = "I'm object property";
// the following demonstrates initialization of
// the object property from class (static) property:
this.varFromStatic = MyClass.classProperty;
}
MyClass.classProperty = "I'm class (static) property";
var obj = new MyClass();
// now obj.varFromStatic contains "I'm class (static) property"

Categories