I would like to change a property of an object, inside an object. But, when I did that, other object property that created using the same prototype also changed.
The code is as follows:
var a = {
x: { y: 'foo' }
}
var b = Object.create(a)
var c = Object.create(a)
console.log(a.x.y) // 'foo'
console.log(b.x.y) // 'foo'
console.log(c.x.y) // 'foo'
b.x.y = 'bar'
var d = Object.create(a)
console.log(a.x.y) // 'bar'
console.log(b.x.y) // 'bar'
console.log(c.x.y) // 'bar'
console.log(d.x.y) // 'bar'
I think the problem is because all objects referring the same x, therefore changing y from any object reflected in all objects. Can anyone explain what really happened here, perhaps with reference and suggestion for a workaround?
x is an object, that is why it is referenced by a pointer and not by the value like the string is.
Try the following as a workaround:
b.x = { y: 'bar' } // instead of b.x.y = 'bar'
this creates a new object x which will be different from others
Try this:
var a = function() {
this.x = { y: 'foo '};
}
var b = new a();
var c = new a();
b.x.y = 'bar';
You will just reference the same object (reference pointer to a place in memory), and modify the object that each object reference to. What you probably want to do is create a new object that are isolated.
Object.create creates a new object and sets its prototype to the first parameter passed in. In your case that's an instance of an object (a), but you use that same instace as the prototype for b & c. So... when accessing members of the prototype of b, you really accessing members of a (via prototypical inheritance). Modifying that applies to all inheriting objects
In order to achieve the inheritance you tried, while using Object.create AND separating all instances, do this:
function a() {
this.x = { y: 'foo' }
}
var b = Object.create(new a())
var c = Object.create(new a())
//console.log(a.x.y) // a is not an object, it's a function, or a "type"
console.log(b.x.y) // 'foo'
console.log(c.x.y) // 'foo'
b.x.y = 'bar'
var d = Object.create(new a())
//console.log(a.x.y) // a is not an object, it's a function, or a "type"
console.log(b.x.y) // 'bar'
console.log(c.x.y) // 'foo'
console.log(d.x.y) // 'foo'
Related
Problem: I am really new to object oriented programming. Have been done a lot of reading lately about objects and I am honestly confused about how to access properties of a sub object from within the main object.
If that is not clear I will shown you what I mean:
JSFiddle: http://jsfiddle.net/rn3btqp0/3/
Example:
// Pseudo code
obj1 = {
xy: {
x: 'foo',
y: this.x + 'bar'
}
}
// Working
obj2 = {
xy: (function() {
x = 'foo',
y = console.log(this.x + 'bar')
})()
}
// need to access stuff like this
obj1 = {
xy: {
x: 'foo',
y: this.x + 'bar'
}
z: xy.y
}
Question: As you can see, Im utterly confused with the proceedings of what I believe is called inheritance (correct me im mistaken). What exactly is a valid way of freely accessing properties of objects within an object?
If anything in my question seems unclear or not well structured, please do let me know so that I can make appropriate adjustments.
First, none of this is inheritance. This is more rightly called "composition", where one object is composed of other nested objects.
Looking at your code...
Example 1
// Pseudo code
obj1 = {
xy: {
x: 'foo',
y: this.x + 'bar'
}
}
This example is what people usually want to do, but you simply can't. Using object literal initializers, there's simply no way to refer to the object being created; you need to wait until after it's created. Specifically, this has no meaning that will pertain to the object being created.
You need to do this:
// Pseudo code
obj1 = {
xy: {
x: 'foo'
}
}
obj1.xy.y = obj1.xy.x + 'bar'
You could give y some default value if you wish, but none that relies on accessing obj1 or obj1.xy.
Example 2
// Working
obj2 = {
xy: (function() {
x = 'foo',
y = console.log(this.x + 'bar')
})()
}
With this one, it doesn't actually work. The x = and and y = are creating global variables. There's nothing being added to obj2.xy; it is undefined.
Example 3
// need to access stuff like this
obj1 = {
xy: {
x: 'foo',
y: this.x + 'bar'
}
z: xy.y
}
Just like Example 1, this can't be done. The this.x + 'bar' has been explained above, but the z: xy.y won't work even if y did have a useful value. This is because obj.xy doesn't yet exist. There's simply no implicit way to access objects being created while they're being created.
This works but usually not best
There are some tricks that can be done, like using anonymous constructors, but the syntax gets a good bit more verbose.
I'll show it here, but I don't think this is often the best way.
var obj1 = new function() {
this.xy = new function() {
this.x = 'foo',
this.y = this.x + 'bar'
}
this.z = this.xy.y
}
Now any object being created is actually using a temporary anonymous function as a constructor. This is because both functions are invoked using new. As such, the value of this in the functions will be the object being created, so we can refer to it. The functions implicitly return the new object, since that's how JS works when invoking a function using new.
Traditional approach
You're really better of just creating the objects individually. It's a little more verbose, but much clearer.
var obj1 = {
xy: {
x: 'foo'
}
}
obj1.xy.y = obj1.xy.x + 'bar'
obj1.z = obj1.xy.y
Using named constructors
If you like the previous one with the functions, then make actual constructor functions and invoke them.
function MyObj() {
this.x = 'foo'
this.y = this.x + 'bar'
}
function MyOuterObj() {
this.xy = new MyObj()
this.z = this.xy.y
}
var obj1 = new MyOuterObj()
function myConstructor (arg) {
this.myName = arg;
this.totalNumber = 0;
this.foo = {
bar: {
someBoolean: false,
someNumber: 5
},
baz: {
someBoolean: false,
someNumber: 10
}
};
}
myConstructor.prototype.getNumber = function () {
console.log(this); //successfully returns the child object
for (var i in this.foo) {
//console log tests
console.log(this); //still returns the child object with all properties, including the myName 'whatever'
console.log(this.foo); //returns the 'foo' object with all nested properties
console.log(i); //returns 'bar' and 'baz', respectively
console.log(this.foo.hasOwnProperty(i)); //returns true
//where it all goes wrong
console.log(typeof(i)); //returns 'string'
console.log(this.foo.i); //returns undefined, even though 'this.foo' definitely has 'bar' and 'baz' properties
//what I'm trying to accomplish
/*
if (this.foo.i.hasOwnProperty('someBoolean') && this.foo.i.someBoolean === true) {
this.totalNumber += this.foo.i.someNumber;
} //returns 'TypeError: Cannot read property 'hasOwnProperty' of undefined
*/
}
return this.totalNumber;
};
var myChild = new myConstructor('whatever');
myChild.getNumber();
What I'm trying to accomplish is using a constructor to create a child. The having nested objects inside that child, with various properties that I will change later in my code. Then using a method of the constructor to access data within the nested objects of that child. Everything works until I get two-deep in nested objects.
I've tried passing every variable, object and property around with various "var this == that"s and "var prop == i"s and etc. Nothing I do seems to work.
foo has no property named i.
You want foo[i], to get the property with that name.
it should be console.log(this.foo[i])
As foo doen not contain "i" property.
Your confusion lies in the way that for-each/for-in loops are normally used in other programming languages such as Java, C#. Here's the difference:
// java
for(int x in list)
x = x+1;
// javascript
var x;
for(x in list)
list[x] = list[x] + 1;
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"
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.
When using object constructors, properties can be directly assigned to the value of previously defined properties:
var foo = new (function() {
this.bar = 5;
this.baz = this.bar;
})();
alert(foo.baz) // 5
I would like to refer to a previously defined property within an OBJECT LITERAL:
var foo = {
bar : 5,
baz : bar
}
alert (foo.baz) // I want 5, but evaluates to undefined
I know that I could do this:
var foo = {
bar : 5,
baz : function() {
alert(this.bar); // 5
}
But I want to assign baz directly to a value rather than a function. Any ideas?
No, you won't be able to use any properties of the object literal before it has been created. Your closest option is probably to use a temporary variable like so:
var tmp = 5,
foo = {
bar : tmp,
baz : tmp
}
If you are free to use ECMAScript 5 features, you could write a getter function for the baz property that instead returns the value of bar:
var yourObject = {
bar: 5
};
Object.defineProperty(yourObject, 'baz', {
get: function () { return yourObject.bar; }
});
You can also just build a literal by parts:
var foo = {bar:5};
foo.baz = foo.bar;
If you need to fit this inside an expression (instead of through multiple statements) you can try abusing the comma operator or you can make a helper function:
(Warning: untested code)
function make_fancy_object(base_object, copies_to_make){
var i, copy_from, copy_to_list;
for(copy_from in copies_to_make){
if(copies_to_make.hasOwnProperty(copy_from)){
copy_to_list = copies_to_make[copy_from];
for(var i=0; i<copy_to_list.length; i++){
base_object[copy_to_list[i]] = base_object[copy_from];
}
}
}
}
var foo = make_fancy_object(
{bar: 5},
{bar: ["baz", "biv"]}
);
//foo.baz and foo.biv should be 5 now as well.