I'm reading a Nodejs book and I came accross to this syntax below
function createClient (ns, opts) {
return createClient[ns] || (createClient[ns] = nos(net.connect(opts)))
}
Didn't quite understand the createClient[ns] = nos(net.connect(opts)) part, What it means? Why should I use it?
Is this syntax documented anywhere?
This is taking advantage of the ability to chain the assignment operator to assign values to multiple variables at once. Stated another way, the return value of an assignment expression is the value that was assigned.
The simplest demonstration is to the log the output of an assignment operation:
console.log(test = 'foo');
This behavior is mentioned in the MDN docs for the assignment operator:
Chaining the assignment operator is possible in order to assign a
single value to multiple variables
In your code snippet, the intention seems to be "return the value of createClient[ns] unless it's false (e.g. unassigned), otherwise assign the value of nos(net.connect(opts)) to createClient[ns] and return that value."
This is a simple form of caching and implies that nos() is an expensive operation that shouldn't be repeated unless necessary.
Here's a simplified example:
let values = {};
function assignIfNotSet(arg) {
return values['example'] || (values['example'] = arg);
}
console.log('before:');
console.log(values);
let result = assignIfNotSet('foo');
console.log('after:');
console.log(values);
console.log('result: ' + result);
Related
I am trying to refactor some code and wanted to use destructuring to make the code a bit easier to read. I am passing in an object into the remove() function, however one of the variables is returning:
'inStock' is assigned a value but never used no-unused-vars
According to the eslint docs:
'A read for a modification of itself is not considered as used.'
https://eslint.org/docs/rules/no-unused-vars
I was wondering whether if there's a way to resolve this issue without:
modifying eslint (no-unused-vars) ?
adding /* eslint-disable no-unused-vars */ comments inline ?
Thanks in advance
before refactoring:
remove(product) {
if (product.quantity > 0) {
product.quantity --;
product.inStock ++;
}
}
after refactoring:
remove({ quantity, inStock }) {
if (quantity > 0) {
quantity --;
inStock ++;
}
}
Problem
Your refactor is based on the misunderstanding that quantity-- and inStock++ has the same behaviour as product.quantity-- and product.inStock++. The core of this misunderstanding is thinking that the destructured fields still refer to the fields on the object. Let's take a look at what destructuring really is.
Take this example, with destructuring:
const obj = { foo: 0 };
const { foo } = obj;
Without destructuring, it'd look like this:
const obj = { foo: 0 };
const foo = obj.foo;
The destructuring syntax is just a shortcut of the second example.
What this illustrates is destructuring defines a new variable and assigns the value (not the reference) of the field you're destructuring to that variable. When you make mutations to that variable, you'll only mutate the value of the variable but not the value of the object field. Here's a runnable example, you'll see that obj.foo hasn't changed from 0, even though we increment foo which has been destructured from obj:
const obj = { foo: 0 };
var { foo } = obj;
foo++;
console.log(obj); // { "foo": 0 }
Going back to your linting error: the linter is correct, not only raising the fact that the variable is unused but revealing this problem that I've explained.
Solution
There are two immediate solutions I can think of:
Don't destructure. With your example, while destructuring does shorten the amount of code there is, it could introduce confusion as to what is mutated and make the code less understandable – you've experienced it yourself.
Reassign the incremented/decremented value back to your object. You'll need to prefix the operator to the operand because this returns the value after the operation. Postfixing returns the value before the operation (aka the original value).
remove({ quantity, inStock }) {
if (quantity > 0) {
product.quantity = --quantity;
product.inStock = ++inStock;
}
}
If you ever learn about functional programming, specifically pure functional programming, there might be other patterns you can apply to your scenario but that's beyond the scope of this answer. These patterns won't do mutations which could make it easier to understand how data flows through and is changed by your system.
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
>
I have a function that returns a variable, I'd just like to know what's the correct syntax for doing something (such as doing math or writing a new variable using that returned variable.
Example Function:
function exampleFunction(number) {
var data_filter = number + number;
return data_filter;
}
The function returns data_filter, and I'd like to know what's the correct syntax for doing something with it, such as inputting it in another function.
What you have here is fine.
As one of the comment suggests typically you assign the result to a variable. Take a simple example here:
let myNumber = 10;
let myHalvedNumber = halveNumber(myNumber);
console.log(myNumber)
console.log(myHalvedNumber);
function halveNumber(numberToHalve){
return numberToHalve/2;
}
The best way to think about it in practice is to treat the function/input combination { f(x) } as a proxy for the result itself. This means that both of these examples are correct and how you choose to employ it is your own preference.
//These two approaches are identical and both valid:
//1) Assign to a variable for further use - typically better for variable re-use:
let myHalvedNumber = halveNumber(10);
aSecondFunction(myHalvedNumber);
//2) Use the result directly in a second call - typically shorter and arguably easier to maintain/read
aSecondFunction(halveNumber(10));
function halveNumber(myNumber){
return myNumber/2;
}
function aSecondFunction (myNumber){
console.log("the number is: " + myNumber)
}
In JavaScript consider I am trying to append a new value and return it.
I have below example regarding overriding parameter value
The below function receives a string value as param and overriding the param with new value and returning it.
function test(value) {
value = value + "hi";
return value;
}
console.log(test("Hello"));
The below function receives a string value as param. I would like to append a new value and return it. So I assigned value to a local variable and then appended strong to a new variable and returning it.
function test(value) {
let temp = value;
temp = value + "hi";
return temp;
}
console.log(test("Hello"));
I am calling it and passing value
test(“Hello”);
Which one is recommended from above?
It's purely a matter of style. Some people think you should leave parameter values alone, others think it's fine to change them.¹
From a practical perspective, it doesn't cause any harm. That is, there is no hidden side-effect to doing so. In particular, since JavaScript is purely pass-by-value, reassigning the parameter can't have any effect on whatever argument was used to fill in that parameter:
function test(value) {
value = value + "hi";
return value;
}
let a = "let's say ";
let b = test(a);
console.log(b); // "let's say hi"
console.log(a === b); // false, `a` was not modified
Your version with temp can be simpler, though:
function test(value) {
let temp = value + "hi";
return temp;
}
(or even
function test(value) {
return value + "hi";
}
but I figure it's highly simplified for the question.)
¹ (I happen to be in the latter camp, but that's neither here nor there.)
Yes, this is not at all wrong and is often done by many programmers across many languages. It is a common practice.
You can use it in cases where you want to use the parameter value inside the function but after making certain modifications to it.
For example, I might want to add two numbers using a function add(a, b) where a and b can be strings or integers or floats.
But just to be sure about it, I can define the add function in the following way:
function add(a,b) {
a = parseFloat(a);
b = parseFloat(b);
return a + b;
}
and this is perfectly fine. This way I can be always sure that there will be no exceptions thrown or in case parameters were passed as strings, it doesn't returns 12 (if I said add(1,2)) when really it should have been 3.
By making parameter overriding a common practice and incorporating it into your coding style, you spare the browser from creating or defining new variables just to modify those variable values. This might not mean much in small applications, but in large scale heavy applications, it might make a noticeable difference especially on low end devices.
The short answer is: it's only a matter of style.
However, this isn't always right. When passing objects, they will be passed by reference, meaning that every change you'll make to the parameter will affect the original object:
const obj = {originalValue: true};
function modifyObject(input) {
input.originalValue = false; // The change happens here
return input; // So it will take place regardless of this line
}
console.log('before:', obj);
modifyObject(obj); // See? we don't even retrieve the return value
console.log('after:', obj);
If we were talking about Java, then creating a new variable would be good practice. As there is something called the Garbage Collector that collects unused variables, etc. and discards them. So keeping a link to the original variable wouldn't allow the collector to discard the variable. (I read this somewhere, but some people said to me it doesn't really work this way, so read more about this online if you want)
In JavaScript, however, it doesn't really matter. It depends on you. Your style. It also depends on the situation as it can be useful sometimes. But really it doesn't really matter. Do as you like.
If you want to simplify it you can do as #T.JCrowder said:
function test(value){
return value+ “hi”;
}
That's about it.
Using ES6 Template literals
function test(value){
return `${value} hi`;
}
I have a variable data that could be either a single value (let's say it could be object, string, or number) or an array. I want to execute a function on every element of the array, or on the single value. Currently, I have:
let result;
if (Array.isArray(data)) {
result = data.map(d => f(d))
} else {
result = f(data);
}
I could use a ternary to the same effect, but I'd like to remove the conditional. Is there an idiomatic way of doing this?
You can add additional dimension and use .flat() to get rid of it
[5].flat() // [5]
[[5]].flat() // [5]
this way you can write:
[data].flat().map(f);
but it doesn't make sense and just wastes computional time; conditional should be faster here.
Try to just make your input consistent and always pass an array to this place (even with single element).
You can use an "esoteric looking" function I call fmap in this example (although the name isn't really correct):
function fmap (f, x) {
return (function (g) {
return g(f);
})((x && x.map && x.map.bind(x)) || function (h) { return h(x); });
}
const upper = x => x.toUpperCase();
console.log('fmap over array:', fmap(upper, ['a', 'b', 'c']));
console.log('fmap over string:', fmap(upper, 'a'));
WTF??
fmap takes a function and a value (f and x). It invokes an IIFE to determine if the value x already implements a map method. If it does, it binds that map method to the value. If the value doesn't implement a map method, it creates an anonymous function which takes in another function and calls that other function with the value x.
Finally, it returns the result of the computation.
Further thoughts
To be honest, my personal opinion is to use a conditional! It is much easier to understand and to reason about. Anyway, it is doable without a conditional statement or the ternary operator.