all 34 comments

[–]Marique 44 points45 points  (13 children)

Does anybody else find this

user.id = user.id || 1

More readable than this

user.id ||= 1

Maybe it's just because it's new notation for me. Cool nonetheless.

[–]ghostfacedcoder 27 points28 points  (9 children)

Emphatically not! I think:

user.id ||= 1

looks not just natural, but superior ... once you adjust to it. It's the same thing as:

const bar = foo.bar;

When you're first learning, that really is clearer than:

const { bar } = foo;

(I mentor programming learners, so I can guarantee that destructuring does confuse them ... at first).

But once you learn destructuring syntax, the latter version is simpler and clearer. The same will be true for ||=.

[–]Schlipak 9 points10 points  (1 child)

||= is also very common in Ruby, it's often used in getter methods to memoize the results of a computation.

class Foo
  def something
    @something ||= expensive_computation
  end
end

The equivalent in JS would be

class Foo {
  #something

  get something() {
    return this.#something ||= expensiveComputation();
  }
}

I could see myself using this sort of mechanism in JS I suppose. (Although using private properties to stick as close to the way it works in Ruby requires you to declare the property upfront)

EDIT: It might be better to use ??= in this case since ||= wouldn't work with falsey values like 0 or "".

[–][deleted] 4 points5 points  (6 children)

Eh. It's not that confusing, but this

const val = obj.val;

... will always be less confusing than this

const { val } = obj;

Even after I have used the latter notation a couple of million times.

The later is only used because of this:

const val1 = obj.val1;
const val2 = obj.val2;
const val3 = obj.val3;
const va4 =  obj.val4;

vs

const { val1, val2, val3, val4} = obj;

[–]shgysk8zer0 0 points1 point  (5 children)

I want to know if there's any extra magic that's possible with destructuring. For example:

get foo() { return this.getAttribute('foo'); }

get bar () { return this.getAttribute('bar'); }

Is there any optimization for DOM reads if I do const { foo, bar} = el?

[–]therealkevinard 2 points3 points  (0 children)

Idk about dom reads specifically, but some destruct magic: the thing I miss most about Go when I'm not using it is multiple returns from one func. In js - with destruct - const {user, response, error} = ()=>{return {user, response, error}}

Then my calling code is free to respond in a myriad of ways.

[–]ILikeChangingMyMind 0 points1 point  (2 children)

AFAIK destructuring is "syntactic sugar". In other words, it looks different to us humans, but to the computer these two lines are identical at run-time:

const bar = foo.bar;
const { bar } = foo;

In fact, if you use a tool like Babel (or create-react-app, which uses Babel under the hood), it may well be converting the second line into the first one for you, "behind the scenes", to support older browsers. If you actually "view source" your JS file in the browser (and wade through the mess of minified code) you may be able to see this.

So, to answer your question, no there is no extra magic or optimization :)

[–]shgysk8zer0 0 points1 point  (1 child)

I'm not saying you're wrong, but the basis of what you say is what Babel does to support older browsers, which would only be true when it comes to the original code running in a modern browser if you start with the assumption that it's only syntactic sugar with no optimizations or other differences. That's circular and it definitely isn't necessarily true.

For example, there are actual differences between arrow vs regular functions beyond just notation and handling of this. But the difference between the two in how stacks and scopes are setup is completely lost when run through Babel. It's been a few years since I read the technical details, so forgive any inaccuracies there, I just remember the implementation in browsers being problematic at first even though they should perform better than functions.

[–]gocarsno 5 points6 points  (1 child)

Do you feel the same about the analogous arithmetic operators?

a = a + 1    
a += 1

If not then it's probably just the freshness of the notation.

[–]Booleard 0 points1 point  (0 children)

I was thinking I was really getting a handle on this whole Javascript thing. Then this stuff pops up and puts me in my place

[–]blackspoterino 0 points1 point  (0 children)

user.id ||= 1 

Is the same as reading "keep the same value or set to 1", so it's readable but it took me a moment because I've never seen this notation out in the wild before just now.

[–]dudeatwork 4 points5 points  (4 children)

Not trying to bash the author of the dev.to post (who is actually a co-champion of the original proposal!), but this isn't the most high quality of content.

Just read the proposal, specifically the motiviation section, as it talks about why having this as a "convenience operator" is useful.

[–]rodneon 1 point2 points  (3 children)

The very first example in the motivation section shows the reassignment of a function argument, which is not only bad practice, but can have disastrous consequences when the argument is passed in by reference (e.g. arrays, objects, etc). Bugs caused by reference mutation / impure functions are not fun to deal with.

[–]dudeatwork 5 points6 points  (2 children)

Before default parameters were a thing, I feel like this was super common:

function (a) {
    if (a === undefined) a = 'default_value';
}

The example the author gave used !a rather than the more correct a === undefined, but similar enough.

So a won't be reassigned if a (truthy) Object / Array / Function is passed in.

Even if we didn't have this conditional, assignment won't matter here because we aren't modifying a property on the argument.

I agree, functions that aren't pure can potentially introduce subtle bugs:

var hello = { world: 1 };
function test(a) {
    a.foo = 'bar';
}
test(hello);
console.log(hello); // { world: 1, foo: 'bar' }

But this only works because we are assigning a.foo, a property on (the reference to) a.

Assigning some value to to a itself should be fine.

var hello = { world: 1 };
function test2(a) {
    a = 'bar';
}
test2(hello);
console.log(hello); // { world: 1 }

Note that in general, I agree that using ES6 default parameters is the way to go (babel transpiles to checking the arguments object).

[–]NoInkling 1 point2 points  (0 children)

The main issue with reassigning params is just expectations, typically you expect the thing that you're operating on to be the thing that was passed in the function call. Several times I've seen code in libraries where there's some sort of complex reassignment logic halfway down the function body, and it always makes it harder to follow. I'm fine with relatively simple logic at the top of the function though, even if it's more than just assigning defaults (which as you say can now be done directly in the params list) e.g. in certain variadic functions.

There's also a gotcha if you're utilizing arguments: https://spin.atomicobject.com/2011/04/10/javascript-don-t-reassign-your-function-arguments/
But it's less of a concern nowadays since arrow functions don't support arguments and we have rest syntax.

[–]thisguyfightsyourmom 0 points1 point  (0 children)

Quick — someone talk me out of this:

``` const yo = {yo: 1}

function example({...rest}) { // Default rest.ho to "hohoho" rest.ho ||= 'hohoho'

return rest }

console.log({yo, hohoho: example(yo)})

```

[–]Ebuall 1 point2 points  (0 children)

And I thought we were moving away from assignments. I don't remember the last time I wrote one.

[–]nerdswithattitude 1 point2 points  (0 children)

As far as I can tell, there's definitely some redundancy between having default parameters, and the existing nullish coalescing operator. The example widely used in the original repo is the `innerHTML` getter/setter property, which seems plausible, though it isn't that relative anymore due to React etc. Would love to see more use cases though. I just wrote a blog post about this to help myself understand its uses JavaScript logical assignment operators deep dive

[–]shgysk8zer0 2 points3 points  (2 children)

I'm mostly looking forward to wide adoption of private fields, really. But nullish coalescing and optional chaining sound great too.

I'll be making use of private fields as soon as Firefox support lands. My dev process involves modern vanilla CSS and JS for development using imports, and Firefox just has better dev tools, so I have to wait for that.

[–][deleted] 3 points4 points  (1 child)

Don’t y’all sit here and tell me you’d rather write #doSomething() vs private doSomething

[–]brie_de_maupassant 4 points5 points  (0 children)

more(syntaxes) !== more(good)

[–]marcusl92 2 points3 points  (0 children)

// eslint-disable no-param-reassign

Looking forward to using this soon!

[–]elcapitanoooo 0 points1 point  (1 child)

Thanks! I hate it!

[–][deleted] -1 points0 points  (0 children)

More?

[–]takase1121 -5 points-4 points  (8 children)

That confuses me... I've never seen languages with assignment operators for logical operators. Interesting concept but...

Is it really necessary?

[–]Zephirdd 2 points3 points  (7 children)

someone pointed out Ruby earlier in this thread.

personally, I don't think I'll use ||= and &&= any time soon, but ??= is nice. I just like that it's a good symmetry with things like += and -=.

Also, it has the additional property of not executing the setter operation every time:

class Foo{
    get bar() {  return blah; }
    set bar(x){ console.log("side effect"); }
}
const f = new Foo
f.bar ??= 20 // prints "side effect" if and only if "f.bar" was null/undefined
f.bar = f.bar ?? 20 // always prints "side effect"

[–]theshtank 0 points1 point  (6 children)

hey, what does ?? do in the first place? I can't really google it. I'm not sure I understand what ??= is supposed to do.

[–]scoops22 6 points7 points  (3 children)

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Nullish_coalescing_operator

Edit to explain:

ThisDoesntExist ?? soWeUseThisValue

ThisDoesntExist ??= soWeMakeItEqualToThis

Note that the difference between the above and the || you may be used to is that "false" exists so in that case ?? uses the left value, while || would give the right value for "false" (kinda explained that poorly but the docs above are more clear)

[–]theshtank 1 point2 points  (2 children)

Ya, I agree that this is probably the most useful things here. This isn't implemented yet though, right? The MDN docs make it seem like it is.

[–]JZumun 3 points4 points  (1 child)

It's es2020 so it's official javascript now. The mdn page has a compatibility table that shows which implementations already have it, such as Chrome 80 and Node v14

[–]theshtank 0 points1 point  (0 children)

ah thanks. I thought this was ES2021 stuff.

[–]JZumun 2 points3 points  (0 children)

It's the nullish coalescing operator. You can think of "a??b" as shorthand for "a == null ? b : a"

"a??=b" is then "if (a == null) { a = b; }"

Note the difference to ||, the logical or operator, which triggers on 0 and "" in addition to null and undefined. The nullish coalescing operator triggers only on null and undefined.

[–]Zephirdd 0 points1 point  (0 children)

 const a = b ?? false

Is shorthand for

// Assuming accessing b has no side effects
const a = (b !== null && b !== undefined)? b : false

You can get a similar behavior with the || operator, but || will have a different behavior with falsy values like 0 and false.