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.
Related
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
};
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"
In my main template file i have this
var Product = function(){
this.is_active = true,
this.is_bom_enabled = false;
};
Now later i need to modify the Product, mainly i need to add more properties.
I tried
Product.someOtherThing = value; // only work for `Product` and not for new instances
after all
var SONY = new Product();
SONY.is_active; // true;
SONY.someOtherThing; //undefined
How can i change properties of Product later ?
Product is being used as a constructor function. When you use a function as a constructor function (via the new operator), it does this:
new creates a new, blank object, and assigns FunctionName.prototype as the object's prototype.
new calls your constructor function with this referring to that new object.
(In the normal case.) The result of the new expression is a reference to that new object.
So what your code is doing in the Product function is assigning properties to the object created via new. If you want to add to that object, you simply add to it:
var p = new Product();
p.someOtherThing = "some value";
If you want to create properties on the prototype, you assign those to the Product.prototype object.
Product.prototype.someCommonProperty = "some value";
When you refer to a property on an object, the JavaScript engine first looks at the object itself to see if it has the property. If it doesn't, the engine looks at the object's prototype to see if it has the property (and then the prototype's prototype, etc.). So adding properties to the prototype makes them available via the instances that use that prototype.
So let's look at your Product:
var Product = function(){
this.is_active = true,
this.is_bom_enabled = false;
};
var p = new Product();
The object that p refers to has it's own properties is_active and is_bom_enabled. They don't come from its prototype. If we do this:
Product.prototype.someCommonProperty = "some value";
...then if you do p.someCommonProperty, the engine first looks to see if p has its own property someCommonProperty and, since it doesn't, the engine looks to the prototype. Since the prototype has it, it gets the value from there.
Note that use of properties from prototypes is assymmetrical: Getting a property value from an object will use the prototype's version, but setting a property value will always set it on the actual object. So:
console.log(p.someCommonProperty); // "some value" -- from the prototype
p.someCommonProperty = "foo"; // Adds the property to `p`, doesn't change the prototype
console.log(p.someCommonProperty); // "foo" -- from `p`, not the prototype
var Product = function(){
this.is_active = true,
this.is_bom_enabled = false;
};
Product.prototype.someOtherThing = value;
var SONY = new Product();
SONY.is_active; // true;
SONY.someOtherThing; //value
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