Ruby's ||= (or equals) in JavaScript? - javascript

I love Ruby's ||= mechanism. If a variable doesn't exist or is nil, then create it and set it equal to something:
amount # is nil
amount ||= 0 # is 0
amount ||= 5 # is 0
I need to do something similar in JavaScript now. What's the convention or proper way to do this? I know ||= is not valid syntax. 2 obvious ways to handle it are:
window.myLib = window.myLib || {};
// or
if (!window.myLib)
window.myLib = {};

Both are absolutely correct, but if you are looking for something that works like ||= in ruby. The first method which is variable = variable || {} is the one you are looking for :)

You can use the logical OR operator || which evaluates its right operand if lVal is a falsy value.
Falsy values include e.g null, false, 0, "", undefined, NaN
x = x || 1

The operator you asked about has been proposed as a feature in JavaScript. It is currently at Stage 4, and it will be introduced in the next ECMAScript standard, which is expected to be published in 2021.
You can use it now using the plugin-proposal-logical-assignment-operators Babel plugin. I have never used that plugin, so I have no idea how well it works.

If you're working with objects, you can use destructuring (since ES6) like so:
({ myLib: window.myLib = {} } = window);
...but you don't gain anything over the accepted answer except confusion.

As of 2021, you can use ||= with identical behavior to Ruby as long as you are transpiling or don't care about Opera/IE.
Logical OR assignement, ||= is now supported natively in javascript on all major browsers except Opera and IE. Current caniuse matrix. MDN reference.
Typescript added support for the operator in version 4. If you need to support IE/Opera you can use the babel plugin to transpile for broad compatibility.

Logical nullish assignment (??=)
x ??= 23
Documentation & Browser compatibility

Ruby's ||= operator short circuits assignment. It can be thought of like this:
return a || a = b
So in javascript, this looks very similar:
return a || (a = b);
It seems as pointed out in the comments below however, that this literal ruby form is less efficient than the standard javascript idiom a = a || b.
For reference:
http://www.rubyinside.com/what-rubys-double-pipe-or-equals-really-does-5488.html

You can achieve the desired behaviour using |= operator in javascript for integers only. But you have to define the variable first.
let a = 0
a |= 100
console.log(a) // 100
For objects
let o = {}
o.a |= 100
console.log(o) // {a: 100}
For Arrays
let arr = []
arr[0] |= 100
console.log(arr) // [100]

Related

What is the point of using the double logical not "!!" operator in JavaScript?

I quite regularly see some code like this:
const a = !!b && b.c === true;
I suppose the idea is to not have the a variable nullable, but in that case what is the difference with this code:
const a = b?.c === true
Is there a fundamental difference in between the two?
This is more a JavaScript side of a problem...
The && operator returns the first falsy value, so 0, '', undefined, NaN or null would be the value of const a. If you want a boolean then the !! syntax is the most common way to ensure it being a Boolean.
If I'm not completely wrong on this the optional chaining (?.) just stops the execution on undefined or null values and returns undefined.
If the situation is such that b, if it exists (isn't undefined or null), will be an object, then no, there isn't any difference between those two.
The largest reason why you probably see someVar && someVar.someProp (or !!someVar && someVar.someProp) and variations is that optional chaining is pretty new syntax. It will only exist in recently-updated codebases (running TypeScript 3.7 or above).
But if a variable may be falsy, but is not necessarily an object - for example, if it's 0, NaN, false, or the empty string, then those constructs are not equivalent. Optional chaining with ?. will short-circuit to undefined only if expression to the left of the ?. is null or undefined. Other falsy values will continue to have their properties be evaluated as normal.
Both expressions have the same result.
The crucial difference is: compatibility
In pure JavaScript, the ?. operator is really recent: See the Browser compatibility chart on MDN. My current browser, for example, (today is 2020-03-11, and my Linux system is running Firefox 73) does not support the b?.c syntax.
The b?.c === true version could simply not be written before ES2020 and will not work as is on your client's browser if he/she hasn't updated to recent builds to this day: "recent" would mean "bleeding edge"...
As mentioned by jonrsharpe in his comment, the ?. operator is also available through transpiled languages (TypeScript, CoffeeScript, Babel, etc.), with various dates of support.

JavaScript variable assignment with OR vs if check [duplicate]

This question already has answers here:
What does the construct x = x || y mean?
(12 answers)
Closed 6 years ago.
In JavaScript I recently realized you could use the OR || logical operator for assignment, and I want to know if it's considered bad practice.
In particular I have some functions that have optional array input, if the input is null or undefined I should just set it to an empty array [], if it has content it should take the content.
I found that using the assignment using the OR operator handles that perfectly in a single line, it's clean. However, it feels like the kind of thing that might be considered bad practice, or may have some horrible pitfalls I'm not considering.
Another approach is a simple if check, which is fairly safe in general.
I want to know if using the || approach seen below has any pitfalls I'm not considering, although it works in this scenario I would appreciate knowing if it works well to keep using this in the future, or to stop using it altogether.
https://jsbin.com/nozuxiwawa/1/edit?js,console
var myArray = ['Some', 'Strings', 'Whatever'];
// Just assign using OR
var pathOne = function(maybeAnArray) {
var array = maybeAnArray || [];
console.log(array);
}
// Assign using IF
var pathTwo = function(maybeAnArray) {
var array = [];
// Covers null and undefined
if (maybeAnArray != null) {
array = maybeAnArray;
}
console.log(array);
}
console.log('Path one:');
pathOne(myArray); // ['Some', 'Strings', 'Whatever']
pathOne(null); // []
console.log('\nPath two:');
pathTwo(myArray); // ['Some', 'Strings', 'Whatever']
pathTwo(null); // []
IMHO the use of the OR || for the purposes of assignment is perfectly valid and is good practice. We certainly use it in our projects and I've seen it used in lots of 3rd party projects that we use.
The thing you need to be aware of is how certain JavaScript objects can be coerced to be other values. So for example, if you're ORing values such as "", false or 0 then they are treated as false... this means that when you have the following:
function f(o) {
var x = o || -1;
return x;
}
Calling:
f(0)
...will return -1... but calling
f(1)
Will return 1 ... even though in both cases you passed a number - because 0 is treated as false -1 is assigned to x.
...that said, as long as you're aware of how the OR operator will treat the operands that you use with it - then it is good JavaScript practice to use it.
i prefer the first option, it's clear for my eyes, but when i need to share my code with others will think about to use second, will be more clear for any.
Now i'm using sonar, and prefer the second option too, will more easy to comprend for machine in inegration works.
Last idea is to use
if(maybeAnArray !== void(0))
Two reasons:
use cast and type conditionals
void(0) will works same for all browsers
Expect it helps yopu
When given the option, I prefer concise code (which must still be readable).
I would say || is common enough that it is considered good practice. Once one has seen it a few times it reads just fine.
In my opinion there are few reasons why you should rather use the second option:
First of all it's much more readable - new developers that are still learning can have problems with understanding notation like var myArray = someArrayArg || [];
If you are using some kind of code checkers like JSLint, they will return warnings and/or errors like Expected a conditional expression and instead saw an assignment. for the statement with var myArray = someArrayArg || [];
We already have something like var myArray = someArrayArg ? someArrayArg : []; that works pretty well

Is Babel's implementation of ES6 object destructuring correct?

So basic desctucturing is fine, {a, b} = obj transpiles to a = obj.a; b = obj.b.
My question is around a bit of an odd syntax that I accidentally ran across and I'm wondering if someone can point me at spec since I can't find it:
({a, b} = obj).c
That does the two a, b assignments and then returns obj.c. It's actually quite useful to me for a byte stream decoder, as I can write:
let width = ({bytes} = intDecode(bytes)).number;
My issue is that I haven't seen this syntax anywhere and don't want to rely on something that is either incorrectly implemented or in proposal stage.
There is nothing special in Destructuring Assignment: it's evaluated as any other Assignment with = operator.
So it returns rval.
Which means you can rely on your syntax.
Some details:
The Destructuring Part is evaluated in the 6[1]:
Let status be the result of performing DestructuringAssignmentEvaluation of assignmentPattern using rval as the argument.
and after this item the assignment evaluation happens as usually, like in the a = b = 42; case.
References:
12.14.4 Assignment Operators / Runtime Semantics: Evaluation
12.14.5.2 Destructuring Assignment / Runtime Semantics: DestructuringAssignmentEvaluation
Yes, it is expected to work like this (see #zerkms' answer for details). That you haven't seen the syntax anywhere is because it's not exactly good practice to access properties on the result of an assignment expression, as it makes the code quite unreadable. Whether you assign to a normal variable or a destructuring expression doesn't make much difference here.
However, you could quite easily transform the code into a reasonable destructuring assignment:
let {bytes, number:width} = intDecode(bytes);
or actually, more closely to your original code:
let width;
({bytes, number:width} = intDecode(bytes));

Create array and push into it in one line

The following is just a theoretical JavaScript question. I am curious if the following can be converting into a single statement:
if(!window.foo){
window.foo = [];
}
window.foo.push('bar');
everyone has probably written this code before, but can it be done in one line?
At first I thought something like this would work:
(window.foo || window.foo = []).push('bar');
but that doesn't work because of an invalid assignment. Next I tried chaining something on the push, but that doesn't work because push does not return the array.
Any thoughts on if this can be done in plain JavaScript?
(the result by the way should be that window.foo = ['bar'])
You've got your assignment backwards*. It should be:
(window.foo = window.foo || []).push('bar');
The || operator in JavaScript does not return a boolean value. If the left hand side is truthy, it returns the left hand side, otherwise it returns the right hand side.
a = a || [];
is equivalent to
a = a ? a : [];
So an alternative way of writing the above is:
(window.foo = window.foo ? window.foo : []).push('bar');
* see comments for details
Your code works just fine if you add parentheses so that it does what you intended:
(window.foo || (window.foo = [])).push('bar');
Without the parentheses, it thinks that it should evaluate window.foo || window.foo first, and then assign the array to the result of that, which is not possible.
This question got me playing with different options for fun. It's too bad push returns the length instead of the original array reference, but for even shorter expressions it can be helpful to have something that can be immediately iterated, mapped, etc.
window.foo = (window.foo||[]).concat(['bar']); // always returns array, allowing:
(window.foo = (window.foo||[]).concat(['bar'])).forEach( ... )
(window.foo = window.foo||[]).push('bar'); // always returns length
window.foo && window.foo.push('bar') || (window.foo = ['bar']); // playing around
2021 Update
#zzzzBov's helpful answer,
(window.foo = window.foo || []).push('bar');
can be further simplified using the new ||= operator, logical OR assignment1,
(window.foo ||= []).push('bar');
1 See tcs39/proposal-logical-assignment, currently in Stage 4, and supported by major browsers.
The shortest way to do this is using Logical Nullish Assignment:
(window.foo ??= []).push('bar');
Source: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Logical_nullish_assignment
You can use .concat instead of .push, since .concat returns the array and .push returns the array's length.
window.foo = (window.foo || []).concat('bar');
This way the code is easier to read and understand.
Shorter version with a new ||= operator: (may be slightly harder to read though)
window.foo ||= [].concat('bar');

Strange JavaScript assignment with logical or

I'm building a JavaScript parser and got an error when testing it on jQuery on line 496:
isArray: Array.isArray || function( obj ) {
return jQuery.type(obj) === "array";
},
I have reduced it down to this:
a = b || function() {}
Is this valid ECMA-262 or is it a feature that has been added since then? Either way, how do I express it in terms of a parser?
My abstract syntax tree for an assignment expression is:
data Assignment
= CondExpr CondExpr
| Assign LeftExpr AssignOp Assignment
| AssignFuncDecl FuncDecl
This doesn't support assignments in the above format.
I can explain my AST more if needed. Thanks for any help you can give!
You should look for "ecma bnf". Here's one of the links.

Categories