What do the square brackets after ES6 function do? - javascript

Recently I was looking for a way to rewrite an ugly switch/case statement and came across this Medium article.
I rewrote my switch/case into an es6 function like this:
const url = category => ({
'itemA': itemAService.getItemCategories(payload),
'itemB': itemBService.getItemCategories(payload),
})[category]
When I call this function with something like const response = url(category); it works, which is great! But then I got to wondering what exactly the [category] means at the end of the function. I thought maybe it was an Immediately Invoked Function, but that didn't seem right either.
The article mentions that it's an object literal, but when I went to the MDN docs I couldn't find anything that explained what this is or what it does, or even any examples that showcase this same thing.
So what does it do?

That shorthand is roughly equivalent to the following traditional function syntax:
function url(category) {
var obj = {
'itemA': itemAService.getItemCategories(payload),
'itemB': itemBService.getItemCategories(payload),
};
return obj[category];
}
It's easier to see what's happening when you create a named variable for the object.
The parentheses are needed around the object in the arrow function because if an arrow function begins with { it's treated as a body containing statements, rather than a value to return.
They could have put [category] immediately after the object literal, rather than after the close parenthesis, that might have been clearer.

It's not "after" the function, it is in the functions body. It could also be written as:
const url = category => {
const obj = {
'itemA': itemAService.getItemCategories(payload),
'itemB': itemBService.getItemCategories(payload),
};
return obj[category];
};
So this is basically just a dynamic property lookup in the object.

What confuses you here are the braces.
Imagine that you have an object expression and you use a property assessor on the variable which points to the object.
obj = {foo: 1, bar: 2}
return obj["foo"]; //returns 1
Now, how would you call a property assessor on an object literal? You need braces around them to complete the shorthand syntax.
return {foo: 1, bar: 2}["foo"]; // WRONG Syntax
return ({foo: 1, bar: 2})["foo"]; // CORRECT syntax
So, your function can be rewritten using the following traditional syntax.
function getUrl(category) {
return ({
'itemA': itemAService.getItemCategories(payload),
'itemB': itemBService.getItemCategories(payload),
})[category]
}

Related

what EXACTLY is the difference between const thisFunction = () => {} and const thisFunction = ({}) => {} [duplicate]

I keep seeing functions that look like this in a codebase I'm working on:
const func = ({ param1, param2 }) => {
//do stuff
}
What exactly is this doing? I'm having a hard time finding it on google, because I'm not even sure what this is called, or how to describe it in a google search.
It is destructuring, but contained within the parameters. The equivalent without the destructuring would be:
const func = o => {
var param1 = o.param1;
var param2 = o.param2;
//do stuff
}
This is passing an object as a property.
It is basically shorthand for
let param1 = someObject.param1
let param2 = someObject.param2
Another way of using this technique without parameters is the following, let's consider then for a second that someObject does contain those properties.
let {param1, param2} = someObject;
It is an object destructuring assignment. Like me, you may have found it surprising because ES6 object destructuring syntax looks like, but does NOT behave like object literal construction.
It supports the very terse form you ran into, as well as renaming the fields and default arguments:
Essentially, it's {oldkeyname:newkeyname=defaultvalue,...}. ':' is NOT the key/value separator; '=' is.
Some fallout of this language design decision is that you might have to do things like
;({a,b}=some_object);
The extra parens prevent the left curly braces parsing as a block, and the leading semicolon prevents the parens from getting parsed as a function call to a function on the previous line.
For more info see:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment
Beware, key errors during object destructuring assignment do NOT throw; you just end up with "undefined" values, whether it's a key error or some other error that got silently propagated as 'undefined'.
> var {rsienstr: foo, q: bar} = {p:1, q:undefined};
undefined
> foo
undefined
> bar
undefined
>

stringify function with function inside

is there any way to stringify function which has function inside? I want to stringify bar function.
const foo = (x) => {
return x + x;
};
const bar = (y) => {
return foo(y)
};
bar.toString() returns
function bar(y) {
return foo(y);
}
but I want something like:
const bar = (y) => {
const foo = (x) => {
return x + x;
};
return foo(y);
};
Is it even possible? I want to use bar as follow:
// I don't have control below this line
import bar from 'bar';
`<script>(${bar})(5)</script>`
Is it even possible?
Probably. It is not, at all, simple. To do it, you'd have to use a JavaScript parser like Esprima or the ones in Babel, ESLint, etc. to parse the source code that contains both bar and foo and use the resulting AST to identify the functions that bar calls, convert those functions to source code, convert bar to source code, and insert the functions it calls into the beginning of it. And even then, depending on what those functions call and what things they use that they close over, you may have more work to do.
It's tempting to think "Well, I could just use toString on the functions and then a bit of regular expression to figure out what functions bar calls." But JavaScript source code syntax is far too complex to correctly identify function calls in a function's code with a regex or two. You need a proper parser.
As someone said in the comments (now deleted), this sounds like an X/Y problem — like you're trying to solve some other problem by doing this odd conversion of bar. I would focus on solving that problem a different way instead.

Parentheses in an object literal

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);

Javascript — objects containing functions with parameters

This is a fairly simple question, but I can't seem to find an example online. I've read up on objects and functions (i.e. here), but can't seem to find an example of a function within an object that accepts parameters.
In JavaScript, we can create an object, a nested object, and a method defined by a function:
var testObject = {
nestedObject : {
isNumber : function(number) { return !isNaN(number) };
}
}
How can I call my function with a specific parameter? Is the following correct?
testObject["nestedObject"].isNumber(number)
Thanks!
You kind of have the right idea but you need to refactor your object. This is how it would be written and called.
var testObject = {
nestedObject: {
isNumber :function(number) { return isNaN(number) }
}
}
then
testObject.nestedObject.isNumber(3);
Looks like there were just a few syntax errors in your code. Functions defined within an object behave the same way as regular functions defined outside of an object. The only difference is that you will have to use dot notation to access the functions on objects. So, try this code out:
testObject = {
nestedObject: {
isNumber: function(number) { return !isNaN(number) }
}
}
Now, you can call isNumber like this:
testObject.nestedObject.isNumber(4) // returns true
Note: Assuming you want your function isNumber to return true if the input is a number, we have to negate ( using ! ) the result of isNaN, as it returns true for values that are NaN.
Edit: When accessing properties (functions or otherwise) of an object, you can use dot notation as above, or you could use bracket notation, or some combination of the two. Here are a few examples:
testObject.nestedObject.isNumber(4) // dot notation
testObject['nestedObject']['isNumber'](4) // bracket notation
testObject['nestedObject'].isNumber(4) // combination of both
testObject.nestedObject['isNumber'](4) // alternative combination of both
There is not necessarily a right way to use bracket vs dot notation, but I think dot notation looks a little cleaner. I guess the only advice is to try to be consistent in however you decide to write it.
In this line:
testObject[nestedObject].isNumber(number)
nestedObject will be evaluated and its value will be passed as key of the property of testObject. What you need is to make it literal, to tell JavaScript that that is the property key.
Just to expand the information given by Tyler, these two are equivalents:
testObject["nestedObject"].isNumber(number)
And:
testObject.nestedObject.isNumber(number)

JS: using eval on a function while trying to pass an array as parameter, but it throws an error

i want to create a dynamic generated form using javascript, everything works fine, until i try to pass an array as parameter. When i do this, an error happens. Coulr anyone explain what this is?
Heres my code:
var loadFrm = function(component) {
for(nItem in component) {
var myComponent = "add" + firstToUpper(component[nItem].type);
var callComponent = myComponent + "(" + component[nItem].opt + ");";
eval(callComponent);
}
}
var json = [
{
type: "scale",
opt: {content: [{label: "male", value: "m"}, {label: "female", value: "f"}]}
}
];
loadFrm(json);
Edit Here's the error:
missing ] after element list
[Break on this error] addScale([object Object]);
If you use a debugger to look at the string callComponent, you'll probably find it looks something like this:
addScale([object Object])
...which isn't what you want. That's because you're effectively calling toString on your opt object, and the default toString on objects just looks like that. The eval error is because that's invalid syntax.
Generally speaking, any time you think you need to use eval, there's almost certainly a better answer. In this case, it looks like you're trying to call a function and pass in opt. Assuming these functions are "globals", you can do that like this:
var loadFrm = function(component) {
var nItem, functionName;
for (nItem = 0; nItem < component.length; ++nItem) {
functionName = "add" + firstToUpper(component[nItem].type);
window[functionName](component[nItem].opt);
}
}
Live example
Notes on the above:
Don't use for..in to loop through arrays unless you really know what you're doing. for..in does not enumerate the indexes of an array, it enumerates the properties of an object.
We look up the function by name using window[functionName]. This works because "globals" are actually properties of the window object, and you can look up properties using a string name for them using bracketed notation.
Having gotten the function via window[functionName], we just call it directly, passing in the object opt rather than a string form of it. I assume addScale expects to see an object.
I moved all of the vars to the top of the function because that's where they really are (details).
If you can, I'd recommend moving addScale and the related functions to their own object rather than putting them on window. The window namespace is already pretty crowded. Here's the live example modified to not add any symbols to window at all, instead putting the addScale function on an object called functions and using it from there.
Off-topic: The syntax var loadFrm = function(component) creates an anonymous function that it then assigns to a variable. This is used a lot, but unless you're creating different functions based on a condition, e.g.:
var f;
if (...) {
f = function() { ... };
}
else {
f = function() { ... };
}
...it's not actually useful. (If you are creating different functions based on a condition like that, then it's not only useful, it's necessary.) I recommend using named functions whenever possible, because a function with a name helps your tools help you by showing you the function name in error messages, call stacks, etc.
Off-topic 2: You have a variable called json, but FYI, it's not using JSON notation. It's using a combination of JavaScript array and object literal notation, which is a superset of JSON. You'll see a lot of people confused about this, I mention it because you said you're new and so it's worth nipping in the bud. :-) JSON is purely a notation. (A very useful one.)
Use this:
fn = eval(functionName);
fn(objParameter)

Categories