Quotes as array index [""] (Javascript) - javascript

I found this notation in a javascript code:
this.numbers[""].x
I can't understand what the apices mean. What do they do?

It means that numbers is an object which contains a key which is the empty string. Using [''] will access the property of the object which is the empty string (and then .x will access the x property inside it).
It sounds very strange, and it is, but it's syntactically legal to construct such a thing:
const numbers = {
'': {
x: 'valueOfX'
}
};
console.log(numbers[''].x);
(If you ever see this sort of thing in code that you have control over, I'd suggest considering refactoring it to be less confusing)

Related

Why is the difference between this 2 typescripts objects

I am new to the typescript, and trying to learn the language
Below I have 2 objects I want to understand what is difference between them, they are definitely different as nothing is printed on console.
Code
let some = {'flag' : {copy : 'true'}};
let other = {'flag' : {"copy" : 'true'}};
if(some === other)
console.log("Same")
(Other variable have copy in quotes)
Also if I hover on to the "copy" attribute, IDE is showing string property in both cases.
let some = {'flag' : {copy : 'true'}};
let other = {'flag' : {"copy" : 'true'}};
Your two objects, some and other are basically the same, but object comparison in JS will always return false. This is because the variables are not storing the objects, it's storing a reference to the object. This means that even if the objects have the same structure and the same values for every key, any equality comparison with both == and === will be false. The exception is when you're comparing the same object to itself, ie some === some is true(as it should be).
You can read more about it here
As for your question about quotes around keys, that is the proper syntax for JSON objects. By that I mean, if you're writing something that will be parsed as JSON, then you should use double quotes around all your keys. If it's in JS, you can omit the quotes, in fact, prettier will usually remove the quotes even if you type them. In JS, both the objects above are functionally exactly the same.
One place you will need to use quotes is when your object keys have symbols like -, +, spaces, etc.
const obj = {
"key with spaces": "value",
"Jan-Dec": "123"
}

What happens when same key is defined more than once with different values in an object

This is a question about Javascript(and possibly other languages) fundamentals.
I was developing a project and at some point a realized I had defined the same key more than once in an object but no error appeared on console. After some research I couldn't find a clear/official statement to what happens in this situation or how this affects the program. All I could find out is the output in a simple example:
let obj = {key1:1, key1:2, key1:3};
console.log(obj.key1);// >> output = 3
console.log(Object.keys(obj));// >> output = ['key1']
My question is: The value is just redefined and all previous declarations are erased? Is this a limitation or some kind of error of Javascript?
The value is just redefined and all previous declarations are erased?
Yes. When an object literal contains duplicate keys, only the final key in the object literal will exist on the object after the object is evaluated.
Is this a limitation or some kind of error of Javascript?
It's permitted, but it's nonsense. Like lots of things in programming, there are things which are syntactically allowed but don't make any sense in code.
But you can use a linter to prevent from making these sorts of mistakes, eg ESLint's no-dupe-keys.
There's one case where something very similar to duplicate keys can be common, which is when using object spread, eg:
const obj = { foo: 'foo', bar: 'bar' };
// Now say we want to create a new object containing the properties of `obj`
// plus an updated value for `foo`:
const newObj = { ...obj, foo: 'newFoo' }
This sort of approach is very common when working with data structures immutably, like in React. It's not exactly the same thing as an outright duplicate key in the source code, but it's interpreted the same way - whatever value gets interpreted last in the key-value list (regardless of if the key is spread or static) will be the value that the final object contains at that key.

Why are arrays / braces used in object literal definitions to allow dynamic computing of keys in Javascript / ES2015?

I came across this example in a MDN doc, for example:
class Search1 {
constructor(value) {
this.value = value;
}
[Symbol.search](string) {
return string.indexOf(this.value);
}
}
If I pull up node, and run just the line included as part of the object literal, it doesn't work:
> Symbol.search
Symbol(Symbol.search)
> [Symbol.search]
[ Symbol(Symbol.search) ]
> [Symbol.search]('somthing')
TypeError: [Symbol.search] is not a function
I think I've also seen this syntax in a few other places, like e.g. in the react docs:
handleChange(event) {
this.setState({ [event.target.id]: event.target.value });
}
Is this just a use of destructuring syntax? It doesn't seem like it.
brackets are used when you have variable as key and not a plain string.
const obj = {
"someId": 'abc',
};
const e = {
target: {
id: "someId"
}
};
console.log(obj[e.target.id]);
Apart from above mentioned, it is also used to access the numeric keys (Just like array) and when key is computed. See - https://javascript.info/object#square-brackets
Turns out, that's just part of the spec.
It looks a bit like array de-structuring, but it's not.
In the case of [event.target.id], you're assigning the value that event.target.id points to be a key in the object passed to setState(). If you tried to do this without the brackets ([]), it would not work, not how you expect anyway.
In the case of [Symbol.search](string), here you're using the Symbol.search symbol (see symbols) as a key which is dynamically evaluated immediately to its actual, unique value. The dynamic evaluation is allowed because this value becomes the key in an object literal definition. The value which the key points to is a function being defined here, which takes string as its first and only parameter, and operates on that. This is a hook for allowing an object to define how it behaves when used as a parameter, in this case to the .search() function. See here.
Thanks for #randomSoul's answer, for completing it I might say that braces also make you to have a string key with spaces like below:
const myOBJ = {
'my key': 'my assigned String Value'
}
Then you can call that key value pair with this braces syntax like:
console.log(myOBJ['my key'])
This is rarely used in JavaScript, but the main purpose of using braces for getting the value from object literal is for getting dynamically computed keys of object. Like that you have an object that each key is represented user id, and you based on that you want to decide to get the specific user id that you got from your url params or somewhere else then you would be able to get user data like below:
console.log(lastFiveUserData[myUserId].age)

Valid property names, property assignment and access in JavaScript

Updated Question
What, exactly, qualifies as a valid property name in Javascript? How do various methods of property assignment differ? And how does the property name affect property access?
Note
The answers to my original question (seen below) helped to clear some things up, but also opened a new can of worms. Now that I've had a chance to become a bit more familiar with JavaScript, I believe I've been able to figure a lot of it out.
Since I had a hard time finding this information consolidated into one explanation, I thought it might be helpful to expand my original question, and attempt to answer it.
Original Question
Originally, there was some confusion with the MDN JavaScript guide (object literals). Specifically, I wondered why they claimed that if a property name was not a valid JavaScript identifier, then it would have to be enclosed in quotes. Yet, they offered example code that showed that the number 7 could be used — without quotes — as a property name.
As it turns out, the guide simply left off one important part, and Pointy updated it (changes in bold):
If the property name would not be a valid JavaScript identifier or number, it must be enclosed in quotes.
I also wondered why property names were allowed to deviate away from the "may not start with a digit" rule, that applies to identifiers. That question actually reveals the complete misunderstanding that I had of property names, and is what lead me to do some more research.
Answer for 1st question:
Yes, the statement given in the MDN guide is not 100% accurate, but in your daily work it'd be better to follow it as rule. You really don't need to create properties names which are numbers.
Answer for 2nd question:
A property name may not start with a digit but a property name that is a number without any other characters in its name is fine.
This exception exists because the properties with number for name as the same as indexes.
Let's try this:
var obj = {7: "abc"};
obj[7]; // works fine
obj.7; // gives an error (SyntaxError)
Now try to call Array.push on the object and observe what happens:
Array.prototype.push.call(obj, "xyz");
console.log(obj);
console.log(obj[0]);
// Prints
Object {0: "xyz", 7: "abc", length: 1}
"xyz"
You can see that few new properties (one with name 0 and another with name length) have been added to the object. Moreover, you can use the object as an array:
var obj = { "0": "abc", "1": "xyz", length: 2 };
Array.prototype.pop.call(obj); // Returns: "xyz"
Array.prototype.pop.call(obj); // Returns: "abc"
You can use array's methods on objects and this is called Duck Typing.
Arrays are nothing more than objects with some predefined methods.
From MDN:
Array elements are object properties in the same way that length is a property, but trying to access an element of an array with dot notation throws a syntax error, because the property name is not valid. There is nothing special about JavaScript arrays and the properties that cause this. JavaScript properties that begin with a digit cannot be referenced with dot notation and must be accessed using bracket notation.
Now you can understand why a number for property name is valid. These are called just indexes and they are used in JavaScript arrays. And since JavaScript needs to be consistent with other languages, numbers are valid for indexes/properties names.
Hope this makes it clear.
Here are some interesting articles:
JavaScript identifiers (in ECMAScript 5)
JavaScript identifiers (in ECMAScript 6)
Short Answer
Object property names can be any valid identifier, numeric literal, or string literal (including the empty string).
With that said, there are some potentially confusing intricacies to keep in mind about JavaScript property names, as described below.
And unless you're working with valid (non-negative integer) array indexes, it's a good idea to explicitly assign all numerical property names as strings.
Negative Numbers
What might look like a negative number is actually an expression — something property names do not support.
// SyntaxError
const obj = { -12: 'nope' };
Fortunately, bracket notation handles expressions for us.
// Successful property assignment.
const obj = {};
obj[-12] = 'yup';
Typecasting
All property names are typecasted into strings before being stored.
const obj = {
12: '12'
};
console.log(typeof Object.keys(obj)[0]); // -> string
Parsing
But even before typecasting occurs, keys are parsed according to the syntax used, and transformed into a decimal literal.
const obj = {
// Valid string literal
'022': '022',
// Interpreted as decimal
6: '6',
// Interpreted as floating-point
.345: '0.345',
// Interpreted as floating-point
1.000: '1',
// Interpreted as floating-point
8.9890: '8.989',
// Interpreted as decimal
000888: '888',
// Interpreted as octal
0777: '511',
// Interpreted as hexadecimal
0x00111: '273',
// Interpreted as binary
0b0011: '3',
};
/* Quoted property name */
console.log(obj['022']); // "022"; as expected
console.log(obj[022]); // undefined; 022 is an octal literal that evaluates to 18 before our lookup ever occurs
/* Valid (non-negative integer) array index */
console.log(obj[6]); // "6"; as expected
console.log(obj['6']); // "6"; as expected
/* Non-valid array index */
console.log(obj[0x00111]); // "273"; we're accessing the property name as it was assigned (before it was parsed and typecasted)
console.log(obj['0x00111']); // undefined; after parsing and typecasting, our property name seems to have disappeared
console.log(obj['273']); // "273"; there it is, we found it using the evaluation of our original assignment

JavaScript property access using brackets

If I declare the following in my Chrome console:
var object = {0:0, 1:1}
I can call object[0] and object[1] and get their values. I can also call object["0"] and object["1"]. Next, if I declare:
var object = {"0":0, "1":1}
I can also make all four of the above calls. But if I declare:
var object = {a:0, 1:1}
I get a ReferenceError of "a is not defined" when I call object[a], but object["a"] returns 0, even though the property name in the declaration is not a string. I guess JavaScript thinks I'm calling a variable that doesn't exist in the first example. But why do calling object[0] and object["0"] both work? It seems that JavaScript is doing some kind of automatic conversion for numbers (presumably since they can't be variable names), but what are the rules for this? And is this behavior universal to other places it might come up or just to the bracket notation for objects?
When you use brackets, the expression inside the brackets is evaluated. What's the value of the expression
a
?? Well, if "a" isn't a declared variable, it's nonsense. When you use . notation, the identifier (and it must be an identifier) following the operator is treated as a string. It's just the way the language works.
The reason you're getting a ReferenceError for object[a] is because a literal a is a variable in javascript. "a" is a string containing the letter a.
You can use the dot notation object.a or the bracket notation with object["a"]
object.a; //=> 0
object["a"]; //=> 0
object[1]; //=> 1
object["1"]; //=> 1
Or you can use a variable for access
var x = "a";
object[x]; //=> 0
var y = 1;
object[y]; //=> 1
You are correct.
a there is a token which the engine assumes is a variable.
If you type "a" JS knows it's a string-primitive.
If you type 0, JS knows it's a number-primitive.
So on top of obj.a, obj["a"], obj[0], obj["0"], you can also say:
var a = 0;
obj[a]; // 0
Your app is exploding, because a hasn't been defined yet, and now you want to use it.
And yes, this is the expected behaviour.
What's inside of the brackets isn't seen as a "part" of the object -- it's a way of saying "give me the value of the object which is referenced by this key", where the key might be a number or string (or something that can be coerced into a string or number).
In the future, with maps and weakmaps, you would actually be able to use other objects/functions as keys as well.
var obj = new Map(),
func = function () { },
el = document.getElementById("myId");
obj[func] = 1;
obj[el] = 2;
Right now, these things technically work... ...but only because they're converted to their string values... ...so if you had two functions which were written the same (but technically two different objects), you would overwrite values, currently.
Inside of a map, they'd be treated as separate objects.
Using DOM elements is even worse, right now, as it might be useful to store hundreds of those and bind references to them, so that you don't have to keep looking for them... ...but for now, you need to make a unique ID number/key for each one, and store that, and remember the keys, and then create a child object to hold the data you want...
Whereas in the future, with maps, you could say:
var my_els = document.querySelector(".lots-of-els");
for (let el of my_els /* also the future */) {
console.log( my_map_of_data[el].info );
}

Categories