I'm confused what the object[foo] term is referring to. Any hints? I know that bar['unique_prop'] and bar.unique_prop refers to 2
var foo = {unique_prop: 1};
var bar = {unique_prop: 2};
var object = {};
object[foo] = 'value';
alert(object[bar]);
code on jsfiddle
This:
var foo = 'abc';
object[foo]
is equivalent to:
object.abc
However this:
var foo = {unique_prop: 1};
object[foo] = 'value';
Doesn't have much sense (object property names cannot be object literals). JS engine will use foo.toString() (returning e.g. "[object Object]"), so in fact you are doing this:
var foo = {unique_prop: 1};
object["[object Object]"] = 'value';
which is clearly a bug or a misunderstanding. This behaviour is explained in Member Operators on MDN:
Property names must be strings. This means that non-string objects cannot be used as keys in the object. Any non-string object, including a number, is typecasted into a string via the toString method.
You can however use:
object[foo.unique_prop] = 'value';
which is equivalent to:
object[1] = 'value';
It is the same as object.whatever_x_is where x is foo.toString() which will be the same as bar.toString() since (unless overridden) calling toString on a generic object will generate a generic "is an object" string such as "[object Object]"
object[foo] = 'value';
attempts to use an object as a member name, this causes a .toString() call so;
'value' is assigned to object["[object Object]"], when you attempt to read alert(object[bar]); the same toString() occurs as bar is also an object, so you reference object["[object Object]"] once again and get back 'value'.
Related
So I was playing around the other day just to see exactly how mass assignment works in JavaScript.
First I tried this example in the console:
a = b = {};
a.foo = 'bar';
console.log(b.foo);
The result was "bar" being displayed in an alert. That is fair enough, a and b are really just aliases to the same object. Then I thought, how could I make this example simpler.
a = b = 'foo';
a = 'bar';
console.log(b);
That is pretty much the same thing, isn't it? Well this time, it returns foo not bar as I would expect from the behaviour of the first example.
Why does this happen?
N.B. This example could be simplified even more with the following code:
a = {};
b = a;
a.foo = 'bar';
console.log(b.foo);
a = 'foo';
b = a;
a = 'bar';
console.log(b);
(I suspect that JavaScript treats primitives such as strings and integers differently to hashes. Hashes return a pointer while "core" primitives return a copy of themselves)
In the first example, you are setting a property of an existing object. In the second example, you are assigning a brand new object.
a = b = {};
a and b are now pointers to the same object. So when you do:
a.foo = 'bar';
It sets b.foo as well since a and b point to the same object.
However!
If you do this instead:
a = 'bar';
you are saying that a points to a different object now. This has no effect on what a pointed to before.
In JavaScript, assigning a variable and assigning a property are 2 different operations. It's best to think of variables as pointers to objects, and when you assign directly to a variable, you are not modifying any objects, merely repointing your variable to a different object.
But assigning a property, like a.foo, will modify the object that a points to. This, of course, also modifies all other references that point to this object simply because they all point to the same object.
Your question has already been satisfyingly answered by Squeegy - it has nothing to do with objects vs. primitives, but with reassignment of variables vs. setting properties in the same referenced object.
There seems to be a lot of confusion about JavaScript types in the answers and comments, so here's a small introduction to JavaScript's type system:
In JavaScript, there are two fundamentally different kinds of values: primitives and objects (and there is no thing like a 'hash').
Strings, numbers and booleans as well as null and undefined are primitives, objects are everything which can have properties. Even arrays and functions are regular objects and therefore can hold arbitrary properties. They just differ in the internal [[Class]] property (functions additionally have a property called [[Call]] and [[Construct]], but hey, that's details).
The reason that primitive values may behave like objects is because of autoboxing, but the primitives themselves can't hold any properties.
Here is an example:
var a = 'quux';
a.foo = 'bar';
document.writeln(a.foo);
This will output undefined: a holds a primitive value, which gets promoted to an object when assigning the property foo. But this new object is immediately discarded, so the value of foo is lost.
Think of it like this:
var a = 'quux';
new String(a).foo = 'bar'; // we never save this new object anywhere!
document.writeln(new String(a).foo); // a completly new object gets created
You're more or less correct except that what you're referring to as a "hash" is actually just shorthand syntax for an Object.
In the first example, a and b both refer to the same object. In the second example, you change a to refer to something else.
here is my version of the answer:
obj = {a:"hello",b:"goodbye"}
x = obj
x.a = "bonjour"
// now obj.a is equal to "bonjour"
// because x has the same reference in memory as obj
// but if I write:
x = {}
x.a = obj.a
x.b = obj.b
x.a = "bonjour"
// now x = {a:"bonjour", b:"goodbye"} and obj = {a:"hello", b:"goodbye"}
// because x points to another place in the memory
You are setting a to point to a new string object, while b keeps pointing to the old string object.
In the first case you change some property of the object contained in the variable, in the second case you assign a new value to the variable. That are fundamentally different things. The variables a and b are not somehow magically linked by the first assignment, they just contain the same object. That's also the case in the second example, until you assign a new value to the b variable.
The difference is between simple types and objects.
Anything that's an object (like an array or a function) is passed by reference.
Anything that's a simple type (like a string or a number) is copied.
I always have a copyArray function handy so I can be sure I'm not creating a bunch of aliases to the same array.
var foo = new Object();
var bar = new Object();
var map = new Object();
map[foo] = "foo";
map[bar] = "bar";
alert(map[foo]); //This alerts bar, but what happens to foo?
This is one of those questions to help with your understanding of how keys are assigned in JS. Of course, I immediately tried the following:
for(var prop in map){
console.log(prop + ' ' + map[prop]);
} //[object Object] bar undefined
And then simply:
map //This returns Object {[object Object]: "bar"}
What?
UPDATE:
Thanks for the answers but what happened to foo? Is there some kind of delete mechanism happening I never heard of?
Object bracket notation coerces the value its given to a string. Since your giving it an object, it calls the object's toString method and saves the key as [object Object].
Foo is overridden by bar because you are saving to the same key, [object Object].
Ie. your object looks like this:
{
"[object Object]": "bar"
}
When you use your objects foo and bar as property names, .toString() is implicitly run on them. So you end up with:
map['[object Object]'] = "foo";
map['[object Object]'] = "bar";
alert(map['[object Object]']);
I know what the following code DOES, I'm just unable to wrap my head around the WHY.
var myObject = new Object();
var foo = new Object();
var bar = new Object();
myObject[foo] = 'foo';
myObject[bar] = 'bar';
console.log(myObject[foo]); // returns bar
console.log(myObject); // returns [object Object]: "bar"
console.log(myObject[blah]); // returns "blah not defined" error.
By this measure, how is myObject[blah] undefined yet myObject[foo] and myObject[bar] are defined - and not only defined, but set to the same value?
According to http://www.w3schools.com/js/js_objects.asp an object's properties can be accessed via object.property or object[property] however if I add in myObject.foo = "foobar"; before the console logs, myObject[foo] does not get set to "foobar" but myObject.foo does.
If I console.log myObject.Object or myObject.object or myObject[object] or myObject[Object] - it all comes back as undefined.
Both foo and bar have values of Object {}. So they aren't being set.
The problem is with types here.
foo is an object, you can't access a property of an object with another object as a key.
Although foo will be converted to a string and no error will occur, be aware that what will finally be evaluated is this:
myObject[foo] = 'foo';
myObject[bar] = 'bar';
Translated to
myObject["[object Object]"] = 'foo';
myObject["[object Object]"] = 'bar';
By this measure, how is myObject[blah] undefined yet myObject[foo] and
myObject[bar] are defined - and not only defined, but set to the same
value?
This message error does not tell you that the key does not exist, as it would not be an error. What it tells you instead is that blah is a variable you are trying to use whereas you never defined it before, thus throwing an undefined reference error.
Also, unrelated but {} syntax is always preferred to new Object()
You're referencing the variable blah which is not defined.
foo and bar are defined but it's very strange to use them as keys in another object. JavaScript doesn't support HashMaps by default, instead it will perform a to-string cast, this produces "[object Object]".
So myObject[foo] and myObject[bar] are both just myObject["[object Object]"]
Did you intend myObject["foo"] and myObject["bar"]"?
The syntax is either:
obj["property"];
// or
obj.property;
// or
var prop = "property";
obj[prop];
By this measure, how is myObject[blah] undefined
myObject[blah] is undefined because your trying to add the object blah which is not existing or undefined (thats what the interpreter is complaining when you open the console)
so if you would have said
var blah= new Object();
myobject[blah] would have worked fine.
yet myObject[foo] and myObject[bar] are defined - and not only defined, but set to the same value
The reason being similar to the above one, since you have defined foo and bar by declaring them as objects
var foo = new Object();
var bar = new Object();
the reason they have the same value is because you assigned them with those values
myObject[foo] = 'foo';
myObject[bar] = 'bar';
According to http://www.w3schools.com/js/js_objects.asp (Try to use MDN https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#Objects)... however if I add in myObject.foo = "foobar"; before the console logs, myObject[foo] does not get set to "foobar" but myObject.foo does.
This part has been answered well in the other two answers, but for the sake of completion.
when you say myObject.foo , it creates a new 'property' ,if not already present, called 'foo' for 'myobject'.
The other way when you say
myobject["foo"] //-> NOTE foo is a string. A property is created.
However when you have
var foo = new object();
myobject[foo] // this is not correct since foo is an object here not a string
//or a property. inshort adding objects as property not a good idea.
If I console.log myObject.Object or myObject.object or myObject[object] or myObject[Object] - it all comes back as undefined.
myObject.Object // there is no property called 'Object', 'object' in myobject
However, if you say
myObject.object = "added object property"; //OR
myObject["object"] = "added object property";
then try the above console.log commands it should work, but note that properties are case sensitive
As a beginner, working with existing code, I've come across the following:
var foo = {};
which I believe is equivalent to:
var foo = new Object();
I was under the impression that any variable was automatically an Object, so what would be the purpose of specifically coding that, rather than just declaring an empty variable:
var foo;
I was under the impression that any variable was automatically an Object
That is a mistaken impression. If you declare a variable and do not assign it a value, it is initialised to undefined (as mention in the spec and described in detail), which is not an object (it's a primitive value):
var foo;
console.log(foo); // undefined
If you were to attempt to set a property of an undefined variable you would receive a TypeError:
var foo;
foo.example = "hello"; // TypeError: Cannot set property 'example' of undefined
However, if you assign an empty object to that variable, the above code will succeed:
var foo = {}; // Now refers to an empty object (equivalent to `new Object()`)
foo.example = "hello";
console.log(foo); // '{example:"hello"}'
I believe your understanding probably stems from the common misunderstanding that "everything in JavaScript is an object". This is not true. Many things in JavaScript are objects, but primitive values are not. For example:
var a = "hello"; // String primitive, not an object
var b = new String("hello"); // String instance, object
var c = 10; // Number primitive, not an object
var d = true; // Boolean primitive, not an object
var e; // Undefined value, not an object
var f = null; // Null value, not an object
James's answer is correct. In case he's throwing too much info too quickly (refer back to it later, if so), here's a quick summary.
You are correct in this: var foo = {} and var foo = new Object() are not only equivalent, the results are identical. Only use object literals. (ignore new Object(), new Array(), etc).
var foo; does nothing other than declare a variable. It is 'undefined' by default.
var foo = new Object() also gives the new object a prototype property which can be used to manipulate object inheritance and call standard object functions defined by javascript
see here for details on how you can manipulate the object.protype:
https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/prototype
I can't figure out myself why foo.bar in the example is undefined, could you explain?
var foo = "foo";
foo.bar = "baz";
console.log(foo.bar); // undefined
Q2: How do I add references to properties and methods to the String instance foo?
Help is appreciated, thanks.
-- EDIT --
Note: the question is about a generic String instance, not the String global object. So using "classic" prototyping as someone suggested is not an option, because this way every String instance would have a property called bar, while I want to augment only certain instances.
This is a duplicate of Why can't I add properties to a string object in javascript?.
Basically, you're creating a string, and this is a primitive type in javascript.
You cannot add properties to primitive types in javascript.
By using a real String object for foo:
var foo = new String('foo');
foo.bar = 'baz';
console.log(foo.bar); // 'baz'
foo.bar = "baz"; is the same as undefined = "baz";
You can add functions to string by using it's prototype;
String.prototype.bar = function() {
return "baz";
};
foo.bar() ; //# => baz
When you specified var foo = "foo"; you are asking foo to be interpreted as a string. String can only have a literal as a value. It cannot have any other sub-properties. ( just extend this logic to any other oo programming language you know and it will become clearer). Instead you could do something like this.
var fooObject = new Object()
fooObject.foo = "foo"
fooObject.bar = "baz"
You're talking about "prototyping", which is the ability to add custom properties to objects in javascript.
This is an excellent and concise tutorial on prototyping:
http://www.javascriptkit.com/javatutors/proto.shtml
To extend the String class, modify its prototype:
String.prototype.bar = "baz";
var foo = "foo";
console.log(foo); // "foo"
console.log(foo.bar); // "baz"
In your question, you were modifying an instance of the String class instead of the String class itself.