Accessing object property through bracket notation regardless of the number of brackets - javascript

An object's properties can be accessed through bracket notation by doing the following:
let obj = {a: "test"}
obj["a"]
However, I was not aware that the same object's property could also be accessed by doing:
let v = ["a"] // An array
obj[v]
or
obj[["a"]]
or
obj[[[[[[[[[["a"]]]]]]]]]]
Just wondering, why is that?
I stumbled on this behaviour after storing an array into a variable and mistakingly using the variable/array, rather than the first item of the array, to access an object's property and surprisingly... it didn't throw an error but returned the value.

All object keys are strings. When you use bracket notation foo[bar] the variable you try to fetch will be converted to a string:
const bar = {
toString() {
return "hello";
}
}
const foo = {
hello: "world"
}
console.log(foo[bar]);
When arrays are converted to a string, join(",") is implicitly called on them. And if an array has a single value, the result is the single value as a string:
const arr = ["hello"];
console.log(arr.toString());
console.log(String(arr));
console.log(arr.join(","));
If you have nested arrays, each with one item, you'd still get a single string out of the conversion, since join() also converts all the members into strings, so with String([["hi"]]) you (roughly) get:
[["hi"]].join(",") -> String(["hi"]) -> ["hi"].join(",") -> String("hi")
So, if you supply an array as a key, it works, as long as you only have a single value in each array:
const foo = {
hello: "world"
};
const arr = [[["hello"]]];
console.log(foo[arr]);
console.log(foo[String(arr)]);
console.log(foo[arr.toString()]);
console.log(foo[arr.join(",")]);

console.log(['a'].toString()); // "a"
It's because a key needs to be a string and javascript does type coercion, converting the array to a string automatically.

Related

Are all JavaScript object property names (except Symbols) implicitly converted to strings? Even numbers?

MDN states here
JavaScript object property names (keys) can only be strings or
Symbols.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Working_with_Objects#object_initializers
However, it is stated here
Additionally, you can use a numeric or string literal for the name of a property or nest an object inside another.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Grammar_and_types#literals
These are seemingly conflicting statements. My question is,
What is MDN documentation actually saying here? Am I missing something?
What is allowed and what is not allowed as property names?
If object property names can only be strings then does that mean numbers get implicitly converted to strings.
The latter is not about keys per se but rather about what can be used as a key in object literals (so that the interpreter accepts a literal in your code) .
Take
var o = {};
var p = {};
o[p] = 1;
What key is used to store 1 in o? It's '[object Object]', as this is just p promoted to a string (p.toString())
On the other hand
var p = {};
var o = { p: 1, 7: 2 };
Despite p being an object, in the object literal { p: 1, 7: 2 }, p is just a string, the key name. The 7 is promoted to string and makes the second key. The two keys are then 'p' and '7'.
However if a key is enclosed in brackets
var p = {};
var o = { [p]: 1, 7: 2 };
its actual value is used, which means that now, o has two keys, '[object Object]' and '7'.
Edit Regaring the question about [object Object]. The default implementation of the .toString() for an empty object returns this exact string, [object Object].
var p = {}
console.log( p.toString() );
Thus, if an empty object is used as a key, the key name is [object Object]. Interestingly, overrding the toString() changes the key to the toString()'s value:
var o = {}
var p = {
toString: function() {
return 'foo bar';
}
}
console.log( p.toString() );
o[p] = 1;
console.log( Object.keys(o) );
In the above example, the indexing object toString() has been overridden to provide an alternate value (foo bar) and thus, using p as an index creates foo bar key in o (which is shown by calling Object.keys(o)).
Yes, number keys are converted to strings.
const obj = {2: "hello"}
const keys = Object.keys(obj)
console.log(keys)
console.log(typeof keys[0])
console.log(obj[2])
console.log(obj['2'])
// that's true for arrays too
const arr = ["world"]
console.log(Object.keys(arr))
console.log(arr["0"])

Why does the variable used in the [For.. in] loop return different values when the object is a JSON object vs an Array object?

I have divided this question into two sub-parts, because they are related, and to make it easier to understand.
var outside = ['laptop', 'tv', 'car'];
for (p in outside) {
var message = p;
console.log(message);
}
Now, here we have an Array, and the variable p jumps through all properties, in this case, array items, and assigns to itself the index number of the items. So, 1..2..3. This allows me to then do this:
var outside = ['laptop', 'tv', 'car'];
for (p in outside) {
var message = outside[p];
console.log(message);
}
..use the variable p as a numerical substring to return the property name of each array item.
Now, two questions here:
If the variable goes through properties, like its description
says, then why does using it on an array, the variable assigns the
index numerical value of the items?
Why does using outside[number] returns the name of the
property?
One explanation that would make sense is if each array item is actually an object, and obviously the substring [number] returns the name of that object, where as the for..in variable jumps through each property of the array item objects, and their properties are actually the index number, so that's what it returns.
Is that explanation right, or completely wrong?
The second part of my question is using JSON objects.
var marry = '{"age":"30","height":"180","eyecolor":"blue"}';
var maryobject = JSON.parse(marry);
var out = "";
for (i in maryobject) {
out = i;
console.log(out);
}
Now, here the variable i returns the property names of the object.
However, using the variable i as a substring like I did before with the Array, returns the value of the properties:
var marry = '{"age":"30","height":"180","eyecolor":"blue"}';
var maryobject = JSON.parse(marry);
var out = "";
for (i in maryobject) {
out = maryobject[i];
console.log(out);
}
Now, two questions here too:
Why does [i] indicate the position of the property, since [i] is
not a numerical value?
Why does maryobject[i] returns back the value of the property?
Arrays are Objects that have fake numeric properties. That's one way of looking at it, anyways. Check this out:
const a = ['neat', 'no', 'not', 'really'];
console.log(a['0']); console.log(typeof a); console.log(a instanceof Array);
console.log('-'.repeat(50));
for(let i in a){
console.log(typeof i);
}
const o = {0:'I', nice:"can't'", prop:'believe', 3:'this', key:'is a question'};
console.log('-'.repeat(50));
console.log(o[0]); console.log(typeof o); console.log(o instanceof Array);
console.log('-'.repeat(50));
for(let p in o){
console.log(typeof p);
}
Notice that you're able to access an Object with a Numeric index. Objects really take String properties. When a Numeric property is used they cast it to a String. In for(let prop in obj), prop is the actual property value, which is a String. Yeah, scoping off those loop variables is a good idea too.
The Spec writes:
An Object is logically a collection of properties.
Properties are identified using key values. A property key value is either an ECMAScript String value or a Symbol value. All String and Symbol values, including the empty String, are valid as property keys. A property name is a property key that is a String value.
An integer index is a String-valued property key that is a canonical numeric String and whose numeric value is either +0 or a positive integer ≤ 2^53 - 1. An array index is an integer index whose numeric value i is in the range +0 ≤ i < 2^32 - 1.
And about arrays:
An Array object is an exotic object that gives special treatment to array index property keys. A property whose property name is an array index is also called an element.
That is, arrays are objects optimized for property names whose numeric value is a sufficiently small positive integer.
Put differently
a = [4];
is like
a = {'0': 4};
except for having a different prototype and being an exotic object.
If the variable goes through properties, like its description says, then why does using it on an array, the variable assigns the index numerical value of the items?
As we just learned, an array is an object whose property names are array indices.
Why does using outside[number] returns the name of the property?
An expression of the form a[b] returns the value of the property whose name is equal to b of the object a. For instance:
const a = {foo: 'bar'};
a['foo']; // returns 'bar'
The same is true for arrays:
const a = ['bar']; // a = {'0': 'bar'}, except for being exotic and having a different prototype
a['0']; // returns 'bar'
a[0]; // also returns 'bar', since EcmaScript automatically converts number to string where required
Why does [i] indicate the position of the property, since [i] is not a numerical value?
[i] does not "indicate a position". a[i] is a property access expression, and it accesses the value of a property whose name is equal to the value of i.
Since property names are strings (or Symbols), it is up the to runtime environment to find the memory address this property is stored at. How it does this is unspecified - the runtime may chose whatever optimizations it wants as long as the results are correct.
Of course, every modern JavaScript runtime will translate a[i] into a direct memory access if a is an array and i a number (with appropriate bounds checking, of course). But it must also handle the general case where i is an arbitrary string (or Symbol).
For instance, the following is an entirely legal EcmaScript program:
const a = [42];
a.foo = 'bar'; // shorthand for a['foo'] = 'bar'
for (const key in a) {
console.log(key, a[key]);
}
and prints both properties:
0 42
foo bar
Both Arrays and parsed JSON objects (even functions) are objects in a sense in JavaScript. as #StackSlave said, you can define objects with numerical names for properties; however, they are not valid JSON. This demo shows that:
var o = {
0: "a"
};
var s = '{0:"a"}';
console.log(JSON.stringify(o));
console.log(JSON.parse(JSON.stringify(o)));
try {
console.log(JSON.parse(s));
} catch (error) {
// preventing error in console
console.log(error.message);
}
Now, if you open up this page's console in developer tools, you will see the line which parses the stringified object, is shown as {0: "a"} in contrast to the snippet's result which considered the parsed property name a string. That's because of the V8, and is not a standard to rely on.
So you see, what consoles show us, are what JS engines interpret. In the standard arrays (lowercase) and Arrays (capitalized) are two things. But JS interpreters and engines sometimes do what they think are best.
The for...in for this matter iterates over enumerable properties of an object and in old days (or different engine) you may have actually received some kind of error because the right way to iterate over an array (lowercase) was for(i=0; i<a.length; i++). Period. Nowadays, with the advent of objectified Arrays, the right ways of iterating over these objects' values arguably are Array.prototype.forEach() and for...of:
"Given that for...in is built for iterating object properties, not recommended for use with arrays, and options like Array.prototype.forEach() and for...of exist, what might be the use of for...in at all?"
for...in - JavaScript | MDN
You can also read more about enumerability, iterativeness and ownership of objects and their properties here.
At the end if you want to define a property without the default behavior, you can define it with Object.defineProperty() like this.
Some trickery here:
var o = {};
Object.defineProperty(o, "first_name", {
value: "John",
writable: false,
enumerable: false,
configurable: true
});
Object.defineProperty(o, "last_name", {
value: "Doe",
writable: false,
enumerable: false,
configurable: true
});
Object.defineProperty(o, "name", {
enumerable: true,
configurable: false,
get() {
return `${this.first_name} ${this.last_name}`;
},
set(value) {
let [first_name, last_name] = value?.split(' ') ?? ["N/A", "N/A"];
Object.defineProperty(this, "first_name", {
value: first_name
});
Object.defineProperty(this, "last_name", {
value: last_name
});
}
});
// Look at browser's console for table
console.table({
"first_name": o.first_name,
"last_name": o.last_name,
"name": o.name
});
for (let i in o) {
console.log(`${i}:`, o[i]);
}
o.name = "No way";
// Look at browser's console for table
console.table({
"first_name": o.first_name,
"last_name": o.last_name,
"name": o.name
});
for (let i in o) {
console.log(`${i}:`, o[i]);
}

What is function with brackets mean?

Does anyone know what is test[name] mean?
function test(value){
copy(value||{},this);
}
test[name] = function(){
return "test"
}
This will be easiest to explain with an example:
var name = "foo";
test[name] = function(){
return "test"
};
This would add a property named "foo" to the object test, and the value of that property is a function. It doesn't matter in this case that the object test is actually a function, you can assign properties to functions just like any other object in JavaScript.
You could call this function using any of the following methods:
test[name]()
test["foo"]()
test.foo()
Note that test[name]() will not work if the name variable is assigned to something different, for example name = 'bar'.
Javascript has two sets of notation for accessing objects, dot notation (obj.property) and bracket notation (object[property]). More on that at MDN.
test[name] = function (){} assigns an anonymous function to the name property on the the test object (which itself is a function). In this case (as noted by the comments) the variable name is being used to access the property.
This may seem a little strange at first, but it's helpful to remember that in javascript, functions are objects.
All functions in Javascript are also objects. This adds a property to the test function object with a value which is an anonymous function.
For example:
function test(){
return "foo";
}
// test is a function, so it is also an object and
// it can have properties assigned to it
test.x = function(){
return "bar";
};
test(); // "foo"
test.x(); // "bar"
Of course just like with any object you can also use bracket notation:
var name = 'hello';
test[name] = function(){
return "HELLO!";
};
test.hello(); // "HELLO!"
In JavaScript, functions are objects. They have properties. test[name] sets a property (named whatever the name variable holds) to a function.
when you have a javascript object with defined properties you can access the property either with the dot notation obj.property or with the square brackets notation obj[property]
the property could also be a function so if you have an object:
var test = {
foo : function(arg){ console.log(arg) },
bar : 'hello'
};
you can call test.foo('bar') also by doing test['foo']('bar')
This is especially useful in iterations or when you dont know a priori what the name of the property is going to be. For example:
var fun = 'foo';
test[fun]('hello world');
Naturally it's up to you to do proper checks such as
if ('function'==typeof test['foo']){ test['foo']('bar'); }
Also note that you can do checks on the fly like so:
test[ fun || 'foo']('hello');
Taken from the Mozilla page
One can think of an object as an associative array (a.k.a. map, dictionary, hash, lookup table). The keys in this array are the names of object members
There are two ways to access object members: dot notation and bracket notation (a.k.a. subscript operator).
So
test[name] = function (
means: there are (if everything is ok) two objects: test and name (and we know that at least test is present, because you defined it one line before: function test(value))
take the test object (if there isn't a test object an error will happen). Then access the key/value pair with the key calculated from the name object and there put a function.
Now, how the key is calculated from the name object? The same page from before tells us:
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.
Note that the description is a little wrong... test[null] == test["null"] and test[undefined] == test["undefined"], so perhaps the truth is that under the covers something like String(key).valueOf() is done (the String function will convert null to "null" and undefined to "undefined")
Some examples (where => means "is equivalent to, with this values")
var name = 'foo';
test[name] => test['foo']
var name = 123;
test[name] => test['123']
var name = 123.3;
test[name] => test['123.3']
var name = new Date();
test[name] => test['Wed Aug 14 2013 17:35:35 GMT+0200 (...)']
var name = null;
test[name] => test['null']
var name = undefined;
test[name] => test['undefined']
var name = [];
test[name] => test['']
var name = [1,2,3];
test[name] => test['1,2,3']
var name = {};
test[name] => test['object Object']
and so on...
The brackets are how you reference a property via a key into the hash that javascript objects are.

object Key in javascript class/dictionary?

I have a Javascipt object which I use as dictionary
var obj={
xxx:'1'
yyy:'2'
}
However -
xxx and yyy should be a jQuery object.
something like :
var obj =
{
$('#div1'):'1' ,
$('#div2'):'2'
}
is it possible ?
also, How can I get the "value" for key $('#div2') ?
p.s.
I the $.data cant help me here since its also a key value
and i need in the key - object Type also.
Object keys can only be strings ( or Symbol), period. See Member Operators - Property Names # MDN.
Choose a reasonable string representation, and use that. In this case, I'd say the selector string looks like a decent choice:
{
'#div1': '1',
'#div2': '2'
}
also, How can I get the "value" for key $('#div2') ?
Use one of the member operators, either dot notation
var obj = { /* stuff */ };
var value = obj.propertyName;
console.log(value);
or bracket notation (more useful for property names not known until runtime):
var value = obj['propertyName'];
Use a WeakMap which works like a dictionary where the key can be anything. Note that you cannot list all the keys of the map
const aMap = new WeakMap;
const anObject = {};
aMap.set(Number, "It's the Number class")
aMap.set(anObject, "It's an object")
console.log(aMap.get(Number)) // It's the Number class
console.log(aMap.get(anObject)) // It's an object

Get values from object if the name of the object is stored as a variable?

I have a JSON object return with the following format:
"miscObject": {
"205": [
{
"h": "Foo",
"l": "Bar"
}
]
}
miscObject contains somewhere over 1500 entries, each named incrementally.
How can I get to the values of 'miscObject.205.h' and 'miscObject.205.l' if I have "205" stored in variable without having to loop through all of the objects inside miscObject?
It seems that you're talking about Javascript objects rather than a JSON string.
x[y] and x.y are mostly interchangeable when accessing properties of Javascript objects, with the distinction that y in the former may be an expression.
Take advantage of this to solve your problem, like so:
var num = '205';
console.log(miscObject[num].l);
// ^^^^^
// \
// instead of `.num`, which wouldn't be the same as
// `num` is not the literal name of the property
Use the member lookup syntax
var miscObject = $.parseJSON(theJsonString);
var name = '205';
miscObject[name].h;
Object values can be accessed 2 ways--using the 'dot' notation as you mentioned, or by using []'s:
The following should work:
var result = miscObject["205"].h;
var miscObject = JSON.parse(your-JSON-object);
var value = miscObject['205'].h
You can do this to get the object:
num = '205'
miscObject[num]

Categories