Behavior of primitive types in prototypes

867 Views Asked by At

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?

4

There are 4 best solutions below

2
On BEST ANSWER

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.

0
On
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
2
On

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:

  1. Get me the array that is stored on obj1, and modify the array: obj1.arr.push("black");

  2. 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
0
On

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.