What is wrong with alert({s[prop]}) but fine with this placeholder={s[prop]}
It says I am missing a ',' after 's' and ':' after ']'
In React, the { }s around attributes are essentially expression delimiters - they indicate that what follows between the brackets is an expression. So, if you have const str = 'foobar', then:
placeholder={str}
evaluates to
placeholder='foobar'
But, in alert, you're not writing JSX - you're writing plain JS. When in an expression context, { indicates the start of an object literal. But the following is not a valid object literal:
const obj = {
s[prop]
}
because objects require keys and values (usually). Perhaps you wanted to do
alert(s[prop])
The only time an object literal doesn't require a value is when you're using the shorthand syntax, when you have a variable in the current scope and want to define an object with a property with the same name as the variable, and the same value as a variable, eg:
const str = 'foobar';
const obj = { str };
This results in an object like { str: 'foobar' }.
In any other situation, you'll need to define the property name in addition to the value, eg
{ somePropertyName: s[prop] }
alert(s['use your key'])
Please use in this way. Avoid using {} in alert(), If you use you will see [object Object] instead of actual dynamic content.
try with
alert(s[prop])
In JSX to print any JS variable it must be wrap inside curly braces, but not required when using in JS, alert is the JavaScript function.
Related
During my coding I made a mistake by calling a function like this
someFunction( 'abc' [someValue] )
I had forgotten the colon inside the function call.
After I found the error I played around.
An assignment like this does not throw an error as well.
let a = 'abc'[someValue];
I would expect a syntax error here. Is there an explanation for this?
A string in Javascript can behave as an object and, as such has properties such as .length and methods such as .slice. So, for any property access on an object in Javascript, one can use either the dot notation as in:
str.length
or the [] syntax as in:
str["length"]
or using a variable:
let len = "length";
str[len]
So, what you have with:
'abc' [someValue]
Is just that syntax. A string followed by a property access. That is legal Javascript. It attempts to get the property from that object with the name of whatever string is in the someValue variable.
Here's are a couple working examples:
// simple property access
let prop = "length";
console.log("abc"[prop]);
// method call
console.log("one fine day"["slice"](4, 8));
One would not generally code this way with a string, but it's perfectly legal as it's just part of how one can access properties on an object in Javascript.
Because that's not a syntax error. The engine thought you were trying to get a letter from that string, so if someValue was a number it will work perfectly fine
let a = "abc"[0]
console.log(a, "abc"[1]) //a, b
I know you can use template literals to supply the first parameter of a method, for instance:
const f = x => "hello ," + x;
f`world` // returns 'hello, world'
So I can somehow understand why this code works:
String.raw`bla bla`
However, I can't seem to understand why the same method call with parenthesis throws an error:
String.raw(`bla bla`)
throws: Uncaught TypeError: Cannot convert undefined or null to object
My questions are:
Why exactly the first snippet works? why can I replace parenthesis with template literals in a method call?
Why String.raw only works when it's being called this way?
... I can't seem to understand why the same method call with parenthesis throws an error
It's not the same method call.
This:
String.raw`bla blah`
...calls raw passing in the template.
But this:
String.raw(`bla blah`)
...processes the template, creating a string, and then calls raw with that string. Exactly as though you'd written:
const str = `bla blah`;
String.raw(str);
...but without the constant.
That's not the same thing at all.
Why exactly the first snippet works?
Because that's how tagged template literals work. They're their own thing.
Why String.raw only works when it's being called this way?
Because it's designed to be run as a tag function, but you're calling it like a non-tag function and not passing it the information a tag function receives when called as a tag function.
It might help if you see what a tag function receives:
function foo(strings, ...tokenValues) {
console.log("strings: ", JSON.stringify(strings));
console.log("strings.raw: ", JSON.stringify(strings.raw));
console.log("Token values (" + tokenValues.length + "):");
tokenValues.forEach((val, index) => {
console.log(`${index}: ${typeof val}: ${JSON.stringify(val)}`);
});
}
const token = "tokenValue";
const obj = {
foo: "bar",
num: Math.random()
};
foo`bla \nbla ${token} bla ${obj} done`;
Note how the function receives an array with the non-token parts of the template (the "strings"), followed by arguments with the values of each token. Also note that those values are the actual values, not strings of the values.
More about template literals and tag functions:
MDN
Exploring ES6: Template literals
Or if you want to call String.raw as a function you need to call it like this
String.raw({raw: `xyx`})
As mentioned in https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/raw documentation
Two ways of calling String.raw
String.raw(callSite, ...substitutions)
String.raw`templateString`
Are parentheses in an object literal simply the grouping operator?
node-stringify will convert:
[ { a: 1 } ]
to the string:
[({'a':1}),({'a':2})]
Can I take it that the parentheses here have no impact to the data, ie it is totally the same even if the parentheses are absent?
Yes, (...) in this case is simply being used for grouping an expression. Omitting the parentheses would have no impact on your current data structure.
Parentheses can become more useful in situations where object literals might be interpreted as a block statement instead, e.g. when evaluating an expression in the developer console or when used inside ES6 Arrow functions:
const first = () => {a: 1}
const second = () => ({a: 1})
console.log(first()) //=> undefined
console.log(second()) //=> {a: 1}
I suspect this is why node-stringify has nominated to include them in its output ― to avoid ambiguity wherever possible.
The parenthesis are so the the result works with eval which, specifically, is how they test the functionality in the spec file.
On the GitHub page itself they state:
// The parenthesis is to make the result work with `eval`
console.assert(stringify({a: 1, b: 2}) === '({a:1,b:2})');
To explain further: Normally, eval interprets the { token as the start of a block, not as the start of an object literal. By wrapping the object in parentheses eval interprets it as a full expression and, thusly, returns the parsed object literal correctly, which is important for the authors tests and otherwise not important for other implementations (as you've already noticed).
I'm not sure why node-stringify is putting parentheses around the objects like you describe. But yes, the data structure is the same with or without the parentheses.
Here's an example of JSON.stringify in the browser:
var data = [
{
'a':1
},
{
'a':2
}
];
var stringified = JSON.stringify(data);
console.log(stringified);
Sorry for my ignorance on JavaScript basic concepts.
It boils down to this:
Literal - A value found directly in the script. Examples:
3.14
"This is a string"
[2, 4, 6]
Expression - A group of tokens, often literals or identifiers, combined
with operators that can be evaluated to a specific value. Examples:
2.0
"This is a string"
(x + 2) * 4
There is a very clear difference b/w the above two in Javascript.
I happen to read this article. And I am familiar with the difference b/w function declaration & function expression and when to use one over other or vice-versa.
From the same article:
....You might also recall that when evaluating JSON with eval, the string
is usually wrapped with parenthesis — eval('(' + json + ')'). This is
of course done for the same reason — grouping operator, which
parenthesis are, forces JSON brackets to be parsed as expression
rather than as a block:
try {
{ "x": 5 }; // "{" and "}" are parsed as a block
} catch(err) {
// SyntaxError
}
({ "x": 5 }); // grouping operator forces "{" and "}" to be parsed as object literal
So, what difference does it make to parse something as an object literal other than parsing them as a block?
And for what purpose should I consider to make use of grouping character, in context of parsing?
First, don't eval JSON, use JSON.parse on the String source
A block is a "group of expressions" for example,
let x = 0;
if (true) {
// this is a block
++x;
}
However, equally this is also a block
let x = 0;
{ // hi there, I'm a block!
++x;
}
This means when the interpreter sees block notation, it assumes a block even if you do something like this
{ // this is treated as a block
foo: ++x
}
Here, foo acts as a label rather than a property name and if you try to do more complex things with the attempted object literal, you're going to get a Syntax Error.
When you want to write an Object literal ambiguously like this, the solution is to force the interpreter into "expression mode" explicitly by providing parenthesis
({ // this is definately an Object literal
foo: ++x
})
A group that begins with a { and ends with a } is treated as either object literal or a block depending on context*.
Within an expression context the group is interpreted as an object literal. Writing a block within an expression context will generate a syntax error:
// Valid code:
foo = {a:b};
({a:b});
// Syntax errors:
foo = {var a = b};
({var a = b});
Outside of an expression context the group is interpreted as a block. Depending on exactly how the code is written, an object literal written outside of an expression context is either a syntax error or will be interpreted as a label.
*note: In the ECMAscript spec the word "context" is used to mean something specific. My use of the word here is with the general meaning in computer science with regards to parsing.
someFunction(link) {
someOtherFunction('div' + link);
}
By calling someFunction("Test"), the string "divTest" gets passed to someOtherFunction(). But I want the value of the variable "divTest" to be passed.
How can that be done?
Make your variables members of an object. Then you can use [] to access the objects members using a string:
var byname = {
divabc: ...,
divxyz: ...
};
function someFunction(link) {
someOtherFunction(byname['div'+link]);
}
someFunction('abc'); // calls someOtherFunction(byname.divabc)
For this kind of dynamic construction/access of variable names you should use the alternative object notation where:
object.member === object["member"]
This way you could construct your variable name as a string and use it inside square brackets for accessing object members.
eval will do this, but it's usually indicative of some other problem with the program when you want to synthesize identifiers like this. As Ionut says it's better to use the [] notation. I like to link to this whenever questions like this come up.
You should be able to accomplish this with the 'eval' function.
Try this:
var divFoo = "bar";
function someFunction(link) {
someOtherFunction(this['div' + link]);
}
function someOtherFunction(value) {
alert(value);
}
someFunction("Foo");
As wybiral said, all you need is eval:
someFunction(link) {
someOtherFunction(eval('(div' + link + ')');
}
Basically what it does is evaluates the contents of a string as code. Obviously eval is a dangerous little tool since it allows executing arbitrary code so take care when using it.