Object prototype does not "live update" - javascript

I have the following piece of code:
var Test = function () {
};
Test.prototype.doSomething = function() {
return "done";
};
Now, I create an object of Test
var t = new Test();
alert(t.doSomething()); // Correct alerts "done"
Now I add another method to the prototype:
Test.prototype.fly = function() { return "fly"; };
alert(t.fly()); // Correctly alerts "fly" (existing objects get "live udpated")
Now, I make the prototype point to a blank object:
Test.prototype = {};
alert(t.doSomething()); // Continues to alert "done", but why?
alert(t.fly()); // Continues to alert "fly", but why?
var t2 = new Test();
alert(t.doSomething()); // As expected, this does not work
When I add a method to prototype, it reflects correctly on all new and existing objects
When I "blank" out the prototype by doing <name>.prototype = {};, it only "blanks" out new instances, but not existing ones. Why?

An analogy is this:
var a = {foo : 'bar'};
var b = a; //the same object as a
var c = a;
var d = a;
a.apple = 'orange';
a = 1; //a === 1. b, c and d stay the same, pointing to the object with apple
What I did here is replace what a was pointing, but not the object itself.
When you added fly, you are modifying that single prototype object which all instances share, the object that Test.prototype is currently pointing to.
But when you assigned a blank object to Test.prototype, you modified what Test.prototype was pointing to. It does not modify what the existing instances are pointing to. But from this point on, any new instances will now use the new object on Test.prototype as their prototype object.
If you are familiar with C, I'd rather think of JS variables as pointers rather than references.

I'm completing the previous answer, if you want reflect your changes on all instances you must update the prototype correctly.
Prototype property is an object and you can delete a propery with the reserved keyword 'delete'.
If you want delete 'doSomething' property :
delete Test.prototype.doSomething;

consider
function Foo(){}
Foo.prototype = {a:{"VMAddress":"#1234"}}
consider that Foo.prototype.a object has VMAddress "#1234"
if you create object now then,
var f1 = new Foo();
now f1.a will point to the same object ie with Virtual Machine address "#1234"
if you look
f1.a === Foo.prototype.a ;//prints tue
If you change prototype to some other value now,
Foo.prototype = {a:{"VMAddress":"#5678"}}
and if you create object now then,
var f2 = new Foo();
although
f2.a === Foo.prototype.a; //prints true because both point to same VM address #5678
but
f1.a === f2.a; //prints false
why??
because their VM address are different(one is #1234 and other is #5678) and infact they are different object
final verdict
the prototype chain at the time of object creation decides what an object's prototype will be.

Related

Trying to refresh my knowledge of the Prototype Tree. What is wrong here?

According to http://js4py.readthedocs.org/en/latest/object-tree.html,
All JavaScript objects are part of an inheritance tree. Each object in
the tree has a parent object, which is also called the prototype
object (of the child).
I was playing around to make sure I understand it correctly. Shouldn't the following print "lakdas" since myOtherObj inherits the field x from its parent myObj? Why does it instead log undefined?
var myObj = { x:"lakdas" };
var myOtherObj = { y:"lkjaas" };
myOtherObj.prototype = myObj;
console.log(myOtherObj.x); /* Should print "lakdas", right? */
You can't change an object's prototype by assigning to a prototype property. In many engines you can't change the prototype at all after the object is created. You can set the prototype at object creation time:
var myObj = { x:"lakdas" };
var myOtherObj = Object.create(myObj); // sets prototype
myOtherObj.y = "lkjaas";
console.log(myOtherObj.x); // prints "lakdas"
Functions have a prototype property - when you use a function as a constructor, the object stored in the function's prototype property becomes the prototype of the constructed object:
var myObj = { x:"lakdas" };
function foo() {
this.y = "lkjaas";
}
foo.prototype = myObj;
var myOtherObj = new foo();
console.log(myOtherObj.x); // prints "lakdas"

object in prototype is inherited as reference

I want to inherit new object instance using prototype.
Test case:
var MyObj = function() {}
MyObj.prototype.objName = {}
// I want this to be a different object for each instance of MyObj
var o1 = new MyObj (),
o2 = new MyObj ();
o1.objName['a'] = 1;
o2.objName['a'] = 2;
alert(o1.objName['a']) // 2
alert(o1.objName === o2.objName) // true
This means that objects in prototype are not inherited as its copies but instead as its reference.
I know that normally you can do it like this.
var MyObj = function() {
this.objName = {}
}
var o1 = new MyObj(),
o2 = new MyObj();
alert(o1.objName === o2.objName) // false
This works fine, but in my case this is not an option. I really need to define objName outside the MyObj function.
I managed to "solve" the problem with this
MyObj.prototype.objName = function() {
if ( this._objName === undefined ) {
this._objName = {};
}
return this._objName;
}
var o1 = new MyObj(),
o2 = new MyObj();
o1.objName()['a'] = 1;
o2.objName()['a'] = 2;
alert(o1.objName()['a']) // 1
But this is not very pretty and the performance of this code is much worse.
Is there any way to solve this more elegantly ?
This means that objects in prototype are not inherited as its copies but instead as its reference.
Nothing on the prototype is copied - the whole concept of prototypical inheritance is that properties reference the shared properties of the prototype object. So if you want a property to be individual for each instance, you have to explicitly assign it to the object and shadow the prototype property; just as you're doing it with the _objName property in your code.
But this is not very pretty and the performance of this code is much worse.
If you want it pretty, move it to the constructor (or make the constructor look for something like an init method to call if exists, then you can create that init method on the prototype.
To make performance a little better, you can change the getter function to
MyObj.prototype.getObj = function() {
var obj = {};
this.getObj = function(){ return obj; }; // overwrite itself
return obj;
};
though it still has the function call overhead. For even more elegance, you can use a getter property (not supported in old browsers) that removes itself on the first access:
Object.defineProperty(MyObj.prototype, "objName", {
get: function() {
var obj = {};
Object.defineProperty(this, "objName", {
value: obj,
writable: true //?
});
return obj;
},
enumerable: true,
configurable: true
});
Now you can omit the function call parenthesis.
This means that objects in prototype are not inherited as its copies but instead as its reference.
Just to be clear. First of all in JavaScript all objects are passed by reference, not by value. Only primitives are passed by value.
Second, you're not actually "copying" or "passing" anything. When you set a prototype, you're creating a prototype's chain. It means that in your case:
var MyObj = function() {};
MyObj.prototype.objName = {} ;
var o1 = new MyObj ();
var o2 = new MyObj ();
Both o1 and o2 doesn't have any property called objName, and you can simply test it with:
console.log(Object.keys(o1)); // []
When JS see a code like o1.objName, as first thing checks if the object has this property, and if it has, use it. If not, start to looking in the prototype's chain, starting by the prototype of o1, that is MyObj.prototype: it found the properties objName, and returns it. If it didn't find it, then JS will continue to check the prototype of MyObj.prototype, and so on. So, here the point: MyObj.prototype it's an object: and you shared that object between o1 and o2. That's why the instance of objName is the same. It's exactly the same logic of having:
function objName(obj) {
return "objName" in obj ? obj.objName : O.objName;
}
var O = { objName: [] };
var foo = {};
var bar = {};
objName(foo).push(0);
objName(bar).push(1);
So, you can't put in prototype any object that is not meant to be shared across the objects creates using that prototype. I would say that shared states like that is also a bad practice that should be avoided, that's why in general prototype shouldn't have such property.
It's still not clear to me why you can't modify the constructor, but the point is: when you create the instance of your object, you have to "setup" it. Usually, calling the constructor, but any function is fine. This is made also when you want to support inheritance, and calling the "super" constructor to initialize your object.

How does javascript look up a member of an object through its prototype chain?

When evaluating the value of
myObject.myMember
My guess is that javascript will try to look up the "myMember" entry in:
myObject
myObject.constructor.prototype
myObject.constructor.prototype.constructor.prototype
myObject.constructor.prototype.constructor.prototype.constructor.prototype
.......
until succeed.
The following code seems to validate my guess
var MyClass1=function(){
this.key1="value1";
};
var myObject=new MyClass1();
MyClass1.prototype.key2="value2";
console.log(myObject.constructor.prototype.key2=="value2"); //true
console.log(myObject.key2=="value2"); //true :)
But the following code proves my guess is wrong
var MyClass1=function(){
this.key1="value1";
};
var MyClass2=function(){
this.key2="value2";
};
var myObject=new MyClass1();
MyClass1.prototype=new MyClass2();
console.log(myObject.constructor.prototype.key2=="value2");//true
console.log(myObject.key2=="value2");// false! why?
What is the actual algorithm by which javascript looks up a member of an object through its prototype chain?
It's because you called MyClass1.prototype=new MyClass2(); after creating an instance of MyClass1.
var MyClass1=function(){
this.key1="value1";
};
var MyClass2=function(){
this.key2="value2";
};
MyClass1.prototype=new MyClass2();
var myObject=new MyClass1();
myObject.key1 === "value1";
myObject.key2 === "value2";
Let's inspect the operation var myObject=new MyClass1()
The new operator ( http://es5.github.com/#x11.2.2 ) calls the [[Construct]] internal method of myClass1 (pseudo-code, of course.)
This is the skimmed version of what happens inside [[Construct]] ( http://es5.github.com/#x13.2.2 ) calls (obviously pseudo-code):
//fun in our operation is MyClass1
function Construct ( fun ) {
//we create a fresh new object
var obi = makeNewObject();
//grab the prototype of MyClass1
var proto = fun.prototype;
//and set the actual prototype of obi to that prototype
setPrototype( obi, proto );
//and then returning the created and altered object, that will be myObject
return obi;
}
By "actual prototype", I mean not the fun.prototype you're used of seeing - that prototype looks forward, to instances we will create. The [[Prototype]] of an object is the properties inherited to it, it's its past.
That can be retrieved by using Object.getPrototypeOf, and in some implementations can be set using the non-standard obj.__proto__.
Okay, now that we've established how objects are created using new, why doesn't your example work?
Replacing MyClass1.prototype after you create myObject will break the link that was between the two. MyClass1 has moved to the dark side, it has completely changed, a new object now replaces it, and myObject wants nothing to do with that.
Before the change, myObject.__proto__ === MyClass1.prototype, it holds a reference to the object MyClass1.prototype. Once you've changed the object MyClass1.prototype, myObject.__proto__ will continue referencing that old object instead of the new one.
The answer is pretty much done. To rehash - The [[Construct]] function establishes a link between MyClass1.prototype and myObject. That link doesn't change when you replace MyClass1.prototype, so myObject will happily live with the object it was originally linked to.
In case you're still curious about how property fetching is done: http://es5.github.com/#x8.12.2 I warn you though, spec-language is tedious to read.
This is because after creating myObject instance you "reprototype" MyClass1.
So this is how it works:
function BaseClass() {
this.key1 = "value";
};
BaseClass.prototype.method1 = function() { };
function SubClass() {
this.key2 = "value";
}
SubClass.prototype = new BaseClass();
SubClass.prototype.constructor = SubClass; //just to have classnames rendered correctly in webkit
SubClass.prototype.method2 = function() { };
var instance = new SubClass();
console.log(instance instanceof BaseClass); //true
console.log(instance instanceof SubClass); //true
console.log("key1" in instance); //true
console.log(instance.hasOwnProperty(key1));// false
console.log(instance.hasOwnProperty(key2));// true
console.log("key2" in instance); //true;
console.log("method1" in instance); //true;
console.log("method2" in instance); //true;
Also following the prototypechain is best done with Object.getPrototypeOf(Object.getPrototypeOf(...))

Why is it impossible to change constructor function from prototype?

I have such example.
function Rabbit() {
var jumps = "yes";
};
var rabbit = new Rabbit();
alert(rabbit.jumps); // undefined
alert(Rabbit.prototype.constructor); // outputs exactly the code of the function Rabbit();
I want to change the code in Rabbit() so that the var jumps becomes public. I do it this way:
Rabbit.prototype.constructor = function Rabbit() {
this.jumps = "no";
};
alert(Rabbit.prototype.constructor); // again outputs the code of function Rabbit() and with new this.jumps = "no";
var rabbit2 = new Rabbit(); // create new object with new constructor
alert(rabbit2.jumps); // but still outputs undefined
Why is it not possible to change the code in constructor function this way?
You cannot change a constructor by reassigning to prototype.constructor
What is happening is that Rabbit.prototype.constructor is a pointer to the original constructor (function Rabbit(){...}), so that users of the 'class' can detect the constructor from an instance. Therefore, when you try to do:
Rabbit.prototype.constructor = function Rabbit() {
this.jumps = "no";
};
You're only going to affect code that relies on prototype.constructor to dynamically instantiate objects from instances.
When you call new X, the JS engine doesn't reference X.prototype.constructor, it uses the X as the constructor function and X.prototype as the newly created object's prototype., ignoring X.prototype.constructor.
A good way to explain this is to implement the new operator ourselves. ( Crockford will be happy, no more new ;)
// `new` emulator
//
// Doesn't reference `.constructor` to show that prototype.constructor is not used
// when istantiating objects a la `new`
function make(ctorFun, argsArray) {
// New instance attached to the prototype but the constructor
// hasn't been called on it.
const newInstance = Object.create(ctorFun.prototype);
ctorFun.apply(newInstance, argsArray);
return newInstance;
}
// If you create a utility function to create from instance, then it uses the
// inherited `constructor` property and your change would affect that.
function makeFromInstance(instance, argsArray) {
return make(instance.constructor, argsArray);
}
function X(jumps) {
this.jumps = jumps;
}
// Flip the constructor, see what it affects
X.prototype.constructor = function(jumps) {
this.jumps = !jumps;
}
const xFromConstructorIsGood = make(X, [true]);
const xFromInstanceIsBad = makeFromInstance(xFromConstructorIsGood, [true]);
console.log({
xFromConstructorIsGood,
xFromInstanceIsBad
});
Inheritance in JS
Libraries that help with JS inheritance implement inheritance and do rely on prototype.constructor with something in the spirit the following:
function extend(base, sub) {
function surrogateCtor() {}
// Copy the prototype from the base to setup inheritance
surrogateCtor.prototype = base.prototype;
sub.prototype = new surrogateCtor();
// The constructor property is set to the base constructor
// with the above trick, let's fix it
sub.prototype.constructor = sub;
}
You can see that in the above code, we have to fix the constructor property because it's sometimes used to create instantiate an object when you only have an instance. but it doesn't affect the actual constructor. See my post about JS inheritance http://js-bits.blogspot.com/2010/08/javascript-inheritance-done-right.html
How to redefine a constructor
If you really want to redefine a constructor, just do
// If Rabbit had any custom properties on it
// (or static properties as some call it), they would not be copied, you'd have to do that manually using getOwnPropertyNames
// See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/getOwnPropertyNames
var oldProto = Rabbit.prototype;
Rabbit = function() {...};
Rabbit.prototype = oldProto;
Note that this would not affect code that had already copied that reference, for example:
const myRefRabbit = Rabbit
This is wonderful workaround creating an object from a literal, not from constructor function.
Firstly, if you want jumps member to be contained in the object, rather than being just a local variable in the constructor then you need this keyword.
function Rabbit() {
this.jumps = "yes";
};
var rabbit = new Rabbit();
alert(rabbit.jumps); // not undefined anymore
And now you can easily now access jumps publicly the way you wanted:
rabbit.jumps = 'no';
alert(rabbit.jumps); // outputs 'no'
But still if you create another Rabbit object it will initially have 'yes' as defined in the constructor, right?
var rabbit2 = new Rabbit();
alert(rabbit.jumps); // outputs 'no' from before
alert(rabbit2.jumps); // outputs 'yes'
What you could do is creating a Rabbit from some default Rabbit Object. The concrete rabbits will always have the default value from default Rabbit object even when you change it on the fly unless you have changed the value in concrete rabbit object (implementation). This is a different than #Juan Mendes's solution which is probably the best but it can open another point of view.
Rabbit = {jumps : 'yes'}; // default object
rabbit = Object.create(Rabbit);
Rabbit.jumps = 'no';
rabbit2 = Object.create(Rabbit);
console.log(rabbit.jumps); // outputs "no" - from default object
console.log(rabbit2.jumps); // outputs "no" - from default object
// but...
rabbit.jumps = 'yes';
Rabbit.jumps = 'unknown';
console.log(rabbit.jumps); // outputs "yes" - from concrete object
console.log(rabbit2.jumps); // outputs "unknown" - from default object
Try the following
function Rabbit() {
this.jumps = "no";
};
var rabbit = new Rabbit();
alert(rabbit.jumps); // Prints "no"

Invoking a function Object in JavaScript

I have a small question in JavaScript.
Here is a declaration:
function answerToLifeUniverseAndEverything() {
return 42;
}
var myLife = answerToLifeUniverseAndEverything();
If I do console.log(myLife), it will print 42, as I am just invoking the same instance of function resulting in 42 as the answer. (Basic rule on JavaScript that only references of objects are passed and not the object.)
Now, on the other, hand if I do:
var myLife = new answerToLifeUniverseAndEverything();
then I can't invoke the function; instead, myLife becomes just an object? I understand that this is a new copy of the same function object and not a reference, but why can't I invoke the method?
Can you please clarify the basic fundamental I am missing here?
By prefixing the call to answerToLifeUniverseAndEverything() with new you are telling JavaScript to invoke the function as a constructor function, similar (internally) to this:
var newInstance = {};
var obj = answerToLifeUniverseAndEverything.call(newInstance); // returs 42
if (typeof obj === 'object') {
return obj
} else {
return newInstance;
}
JavaScript proceeds to initialize the this variable inside the constructor function to point to a new instance of answerToLifeUniverseAndEverything. Unless you return a different Object yourself, this new instance will get returned, whether you like it or not.
When you do var myLife = answerToLifeUniverseAndEverything();, myLife is simply holding the return value from the function call - in this case, 42. myLife knows nothing about your function in that case, because the function was already called, returned, and then it assigned the resulting value (42) to the new variable myLife.
A completely different thing happens when you do var myLife = new answerToLifeUniverseAndEverything(); - instead, a new object is created, passed to the function as this, and then (assuming the function doesn't return an object itself), stored in the newly created variable. Since your function returns a number, not an object, the newly generated object is stored.
Try:
function answerToLifeUniverseAndEverything() {
return 42;
}
var myLife = answerToLifeUniverseAndEverything;
alert(myLife());
When you do:
var myLife = answerToLifeUniverseAndEverything();
you're assigning the function result to myLife ie 42.
I think i've described the behaviour of new elsewhere. Basically when you do new f() the JS engine creates an object and passes that as this, then uses that object if the return value of f() is not an object.
eg.
o = new f();
is equivalent (approximately) to
temp = {};
temp2 = f.call(temp);
o = typeof temp2 === "object" ? temp2 : temp;
If I do console.log(myLife) It'll print 42, as I am just invoking the same instance of function resulting in 42 as the answer. (Basic rule on javascripts that only references of objects are passed and not the object)
Not quite. This is because you're assigning the return value of answerToLifeUniverseAndEverything() to myLife. If you wanted to make a copy of the function, drop the brackets:
var myLife = answerToLifeUniverseAndEverything;
console.log(myLife());

Categories

Resources