Reputation: 4938
I am new to JavaScript, and would like to understand more about the order it instantiates parent/child objects. More specifically, I would like to understand the following snippet from the compiler/browser perspective.
var parent = {
child: {
field1: "value1",
field2: "value2"
},
someOtherField: parent.child.field1, // => Error
someOtherField: this.child.field1 // => Error
};
Both Error
lines in the snippet above will generate the same error
Uncaught TypeError: Cannot read property 'field1' of undefined
So it seems when the browser looks at someOtherField
line, parent.child
is still undefined
, and therefore parent.child.field1
is illegal. Could someone share some insights on why this is happening, and what is the exact sequence of instruction is the browser trying run with the above code snippet? Thanks!
Upvotes: 0
Views: 120
Reputation: 147453
In the code (dealing with one issue at a time):
var parent = {
child: {
field1: "value1",
field2: "value2"
},
someOtherField: parent.child.field1 // => Error
};
The variable parent is created and initialised to undefined before any code is executed (so–called "hoisting"). Once execution begins, inspection of the progressive results appear to be that first the object is assigned to parent, then the internal properties are resolved.
So the first error message is like:
TypeError: undefined is not an object (evaluating 'parent.child.field1')
since parent is an object. From this result it's impossible to tell whether parent has a child property or not since it may have a child property initialised to undefined, or it might not have a child property at all. Either way, parent.child
resolves to undefined and attempting to resolve a property of undefined returns a type error.
Note that given:
var parent = {
one: 'one',
two: parent.one
}
console.log(parent.one, parent.two) // one undefined
it can be seen that at the point of assigning a value to parent.two, parent.one was undefined.
As for:
someOtherField: this.child.field1
the value of this will be the global object (window in a browser), and it doesn't have a child property so you get a similar error:
TypeError: undefined is not an object (evaluating 'this.child.field1')
since there is no global child, so this.child returns undefined and attempting to resolve a property of undefined throws a type error.
Note also that:
someOtherField: this.parent.child.field1
is equivalent to:
someOtherField: parent.child.field1
and throws the same error.
The bottom line is that you shouldn't attempt to assign properties of partially completed objects. I'm sure there's an appropriate reference in ECMA-262, it will take a little while to find it.
Upvotes: 1
Reputation: 74234
This has nothing to do with parent/child object instantiation. It has everything to do with hoisting.
All declarations in JavaScript are hoisted. Remember that variable declaration and variable definition are two separate things. For example, we can declare a variable x
using var x
. We define x
using x = someValue
. We can combine variable declaration and variable definition into a single construct as var x = someValue
. However we are still declaring the variable and then defining it.
As I said all declarations in JavaScript are hoisted (i.e. they are lifted to the top of the function scope that they are declared in). For example, consider:
alert(x); // declared but not defined (undefined)
var x = null;
alert(x); // declared and defined (null)
Here the variable declaration var x
has been hoisted to the top of the scope. Variables that have been declared but have not been defined have the value undefined
by default.
Compare the above code with:
try {
alert(x);
x = null;
alert(x);
} catch(e) {
alert(e); // ReferenceError: x is not defined
}
Here we have not declared x
. Hence x
is not hoisted. Thus when we try to print the value of x
we get a ReferenceError
. Note that it is not undefined
because we haven't even declared it.
Now let's consider your code:
var parent = {
child: {
field1: "value1",
field2: "value2"
},
someOtherField: parent.child.field1 // => Error
someOtherField: this.child.field1 // => Error
};
This is the same as:
var parent; // hoisted; undefined
parent = {
child: {
field1: "value1",
field2: "value2"
},
someOtherField: parent.child.field1 // => Error
someOtherField: this.child.field1 // => Error
};
Before defining parent
, it is undefined
. Hence parent.child.field1
is undefined.child.field1
which is an error. Also this
does not point to the object literal. Your mistake is that parent
is not defined until after the object literal has been assigned to the variable (which is after parent.child.field1
is evaluated).
What you can do is:
var parent = {
child: {
field1: "value1",
field2: "value2"
}
};
parent.someOtherField = parent.child.field1;
Hope that helps.
Upvotes: 0