Can someone please explain this behaviour to me.
First let's create a constructor and a new object using the constructor:
var MyConstructor = function() {};
var obj = new MyConstructor();
Everything is as expected:
console.log(obj.constructor === MyConstructor); //true
console.log(obj.__proto__ === MyConstructor.prototype); //true
Let's try again, but this time let's add a custom prototype to the constructor:
var MyConstructor2 = function() {};
var myProto = { fld: 'val' };
MyConstructor2.prototype = myProto;
var obj2 = new MyConstructor2();
Now things are not as I expect them to be:
console.log(obj2.constructor === MyConstructor2); //false?!?!
console.log(obj2.constructor === Object); //true, b-but i didnt use Object..
console.log(obj2.__proto__ === MyConstructor2.prototype); //true
Why is obj2.constructor referring to Object and not MyConstructor2?
--- edit1 ---
Just to clarify. If you create a new function:
var MyConstructor = function() {};
then Javascript implementation will also create a new object:
var temp = { constructor: MyConstructor };
and set it to:
MyConstructor.prototype = temp;
The thing to note here is that the temp object overwrites the constructor field from Object.prototype (and by default Object.prototype.constructor === Object).
So when I create a new object using the constructor:
var obj = new MyConstructor();
then the object inherits the constructor field which points to MyConstructor. In the second case there was no overwriting, so the second object inherited the constructor field directly from Object.prototype.
Each Function object has a prototype property whose "constructor" property is referencing the function. When you create a new prototype using the Object literal syntax, you are created a brand new object whose constructor is literally the Object function. You need to explicitly set the constructor property:
function MyConstructor2() {
}
MyConstructor2.prototype = {
constructor: MyConstructor2
};
Related
Situation:
I have multiple JS-Objects with the same structure.
In all of those Objects there's a property called console.
I'd like to assign a value (the same value) to each of these properties.
Please find below the structure of my code:
NameOfMyObject = function() {
return {
actions: null,
console: null,
init: function() {},
//object specific functions
}
};
My Problem:
Currently I'm assigning the value manual, but I want do do it generic.
Code snippet
this.console = console;
this.actions.console = console;
this.fixtures.console = console;
How can I access the properties of my objects?
I hope I asked my question clear enough.
Here is how you would share a property across objects:
function MyClass() {};
MyClass.prototype.console = {}; // Define a "common" property on the prototype
var obj1 = new MyClass(); // Create two instances
var obj2 = new MyClass();
Object.getPrototypeOf(obj1).console.id = 13; // Assign a value once...
console.log(obj1.console.id); // ... and it exists on both instances
console.log(obj2.console.id);
The shared property is on the prototype object.
Instead of Object.getPrototypeOf(obj1) you can of course use MyClass.prototype, since you know that obj1 was created by MyClass. They give the same prototype object.
If your property always has an object as value and you don't need to replace that value, only mutate it by setting properties on that object, then you don't need to explicitly reference the prototype to set a new value.
So this works:
function MyClass() {};
MyClass.prototype.console = {}; // Define a "common" property on the prototype
var obj1 = new MyClass(); // Create two instances
var obj2 = new MyClass();
obj1.console.id = 13; // Assign a value once... (getting console from the prototype)
console.log(obj1.console.id); // ... and it exists on both instances
console.log(obj2.console.id);
But if you change console itself, you'll be setting it on the instance, not on the prototype:
function MyClass() {};
MyClass.prototype.console = {}; // Define a "common" property on the prototype
var obj1 = new MyClass(); // Create two instances
var obj2 = new MyClass();
obj1.console = { id: 13}; // Setting an instance property now
console.log(obj1.console.id); // ... and it does not exist on both instances
console.log(obj2.console.id); // == undefined
So if that kind of assignment should still work on the prototype, you need to use the first code block with Object.getPrototypeOf or MyClass.prototype.
What is the harm in copying the prototype of a function to another function like shown below.
function Person(){}
Person.prototype = {};
function Author(){}
Author.prototype = Person.prototype;
Object assignments in JS create a reference.
var o = {};
var c = o;
Now both the objects o and c are referring to the same object. Same rule applies when trying to assign the prototype of one object to another.
Author.prototype = Person.prototype;
Now the prototypes of both Author and Person refers to a single object. If you put some data to a prototype property of the Author, the same data will be there for the Person too. This is least expected for distinct objects.
One of the proper ways of doing this is
Author.prototype = Object.create(Person.prototype);
Here you create a brand new object for Author.prototype - but inheriting from Person object.
Because you're passing the prototype by reference, which means both are affected by all changes. Consider:
function Person(){}
Person.prototype = {};
function Author(){}
Author.prototype = Person.prototype;
Author.prototype.books = [];
var bob = new Person();
console.log(bob.books); // this will be an empty array, not undefined as one would expect.
The Ideal way of attaching methods to prototype is by creating an object which is created by instance.
function Person(){
this.test = "1";
}
function Author(){}
Author.prototype = new Person();
this way you create a new instance of person and returned object is fed into Author.
What if you simply copy it ?
If you simply copy the same instance is shared across prototypes , change in one prototype will be reflected in all.
function b(){
this.test = 'a';
this.test1 = function(){
console.log('test1');
}
this.test2 = function(){
console.log('test2');
}
}
function a(){
}
function c(){
}
a.prototype = new b();
var a1 = new a();
c.prototype = a.prototype;
a.test = 'asdf';
console.log(c.test);
Uniqueness of data to an instance will be missing.
var Object1 = {};
var Object2 = new Object();
var Object3 = Object.create({});
When i check whether the prototype is equal to Object.prototype:
The first two return true while the third one returns false.
Why is this happening?
Object.getPrototypeOf(Object1)===Object.prototype //true
Object.getPrototypeOf(Object2)===Object.prototype //true
Object.getPrototypeOf(Object3)===Object.prototype //false
Simply because if you take a look at the Object.create() in the documentation, you will that this method:
creates a new object with the specified prototype object and
properties.
And if you call it with :
Object.create({})
You are not passing a prototype but an empty object with no properties.
So as stated in comments you need to call it like this:
Object.create(Object.prototype)
The Object.create() method creates a new object with the specified prototype object and properties.
Behind the scenes it does the following:
Object.create = (function() {
var Temp = function() {};
return function (prototype) {
if (arguments.length > 1) {
throw Error('Second argument not supported');
}
if (typeof prototype != 'object') {
throw TypeError('Argument must be an object');
}
Temp.prototype = prototype;
var result = new Temp();
Temp.prototype = null;
return result;
};
})();
So the right use would be:
var Object3 = Object.create(Object.prototype);
Or if you want to make your example work:
Object.getPrototypeOf(Object.getPrototypeOf(Object3)) === Object.prototype // true
Here the prototype chain comes into play:
console.dir(Object3)
-> __proto__: Object (=== Your Object Literal)
-> __proto__: Object (=== Object.prototype)
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.
Whats the difference between (via prototypes)
var Todo = {};
Todo.prototype.name = "...";
Todo.prototype.hello = function() { ... }
Vs (variables & functions "outside" object)
var Todo = {}
Todo.name = "..."
Todo.hello = function() { ... }
Or even the below : variables & functions in object
var Todo = {
name: "...",
hello = function() { ... }
}
Think it like
A property or a function declared with prototype is an instance member of Todo.
A property or a function declared without prototype is a static member of Todo.
The first one doesn't make sense as you are dealing with an object instance (({}) instanceof Object === true), it won't have a prototype property (Object does).
You may be inquiring about the difference between these two patterns...
var ObjA = function() {
this.method = function() {};
};
var ObjB = function() {};
ObjB.prototype.method = function() {};
jsFiddle.
The former will use more memory when instantiated - each object has their own method. The latter won't each have their own method, the method lives on the prototype object, which is the next in command on the prototype chain when its attempted to be accessed on the parent.
Todo.prototype is also an object, so the difference is if you declare property with prototype, then every object who created from this prototype will have the property, otherwise, the property is only for Todo the object self.
A significant difference between method #1 and #2 (which is almost identical to example #3) is on new keyword that you need to use if you extend your function via prototype, e.g.
var Todo1 = function() {};
Todo1.prototype.name = "Foobar";
var Todo2 = {name: "Foobar" }
var a = Todo1;
console.log(a.name); // no property retrieved
var b = Todo2;
console.log(b.name); // Foobar
var c = new Todo1;
console.log(c.name); // Foobar