
ES6 in Depth: Destructuring - mnemonik
https://hacks.mozilla.org/2015/05/es6-in-depth-destructuring/
======
girvo
I'm currently building a rather large isomorphic React/Flux application in ES6
and ES7.

With our Flux implementation that we're using[0], async actions to external
services become amazingly simple.

    
    
      async sendRequest(payload) {
          try {
              let result = await this.api.sendRequest(payload);
              return await result;
          } catch (e) {
              if (e.name === 'ServerException') {
                  this.errorActions.serverError(e);
                  return null;
              }
              
              this.errorActions.genericError(e);
              return null;
          }
      }
    

Having a class system on top of prototype system, removing a lot of the
boilerplate is great. Browserify/CommonJS and Babel make for a phenomenal
build system, and being able to with few exceptions render everything on the
server correctly is brilliant.

Javascript has come a long way. The best part of destructuring is simplifying
`import` statements

    
    
      import { fooFunc } from './Bar';
    

I highly recommend checking it out, as the confluence of ES6/7, Babel, React
and Flux (with one way data-flow) feels like the future but here today. That,
and I'm stoked that functional programming concepts are taking off!

[0] [http://acdlite.github.io/flummox](http://acdlite.github.io/flummox)

~~~
jeswin
Last time I checked, one problem with async (translated with regenerator by
babel) was that transformed code is difficult to debug even with source maps.
The stack traces are often useless and breakpoints map incorrectly. Also,
since the async function is returning a Promise, you've to deal with Promise's
own stack trace eating behavior.

To summarize, the code looks neat. But debugging a non-trivial app still has
some way to go.

~~~
girvo
I don't disagree, but one nice thing with babel is a plugin called babel-
plugin-rewire; its a clone, if you will, of rewire -- a proxy for require
calls that allows you to overwrite them.

With it, I can unit test my actions and other async modules in complete
isolation, so the debugging burden is lessened. Id love to be able to remove
console.trace() calls in my async code in the error handling side, and I think
we will get there soon, but things can be worked around and the control flow
becomes far easier to understand than using generators by hand, or callbacks.

~~~
jeswin
> control flow becomes far easier to understand than using generators by hand.

IMO, control flow has no advantages over generators. It is exactly the same
thing, replace await with yield/yield-star. Btw, until aysnc debugging becomes
better, replacing yield with delegating yield even gives you stack traces
since the delegation is handled by the runtime (and control does not pass back
to the function (like Q.async, co, spawn) that's driving generators).

    
    
      //From my current project
      static *getInitialProps(name) {
          var project = yield* Project.getByName(name);
          return { project };
      }
    
    

async is a better way to do things, I agree. In fact, I asked this silly
question once on es-discuss and got educated.
[https://mail.mozilla.org/pipermail/es-
discuss/2014-September...](https://mail.mozilla.org/pipermail/es-
discuss/2014-September/039276.html)

~~~
girvo
Hey, I read that thread just the other week! Fascinating discussion, though I
still disagree that generators have the control-flow advantage: while I
personally don't see much of a difference, the other developers whom are newer
to JS and asyc execution in general have found async/await conceptually
simpler, despite the fact it's still compiled down to generators anyway.

You're right, in that it makes little difference in terms of semantics per se,
but having implemented both in the application we're working on (which is at
just under 10k semicolons, currently) async/await along with how Flummox/Flux
implements the dispatcher, it allows you to work with async methods without
propgating async through the entire call stack, and lets you think of the
runtime as being synchronous-ish. I'm looking forward to finishing this
project, as there's a tonne of stuff I want to write up about it; I think the
big issue with ES6/7 is that there isn't a lot of "in the trenches" writeups.

Thanks so much for the discussion on esdiscuss by the way, it really helped me
understand async execution and control flow in ES6/7.

PS. This is a neat project, HTTP servers with ES7 async/await. Similar in
concept to Koa, but using the newer syntax!
[https://github.com/quinnjs/quinn](https://github.com/quinnjs/quinn)

------
bakhy
No offense meant to anyone, but I really do not like this constant stream of
new features in JavaScript. There are some core issues with the language
which, of course, cannot be just fixed, and no new features will make them go
away. But there is also a beautiful simplicity and power in the way this (used
to be) funny little language does classes, objects, scoping... I feel like
these new features are bloating it, and for what? So that you can write an
assignment in 1 instead of 3 lines? Wow. It's as if, whatever programming
language is trendy, JavaScript must absorb as much of its features as it
can...

Don't get me wrong, destructuring is a nifty feature, but it's not really
necessary. Plus, things like that usually get thought through along with
everything else, when the language is designed. Not only is this increasing
the conceptual weight of the language (OK, maybe I exaggerate in this example
;) but there are many more features added all the time), but now you also have
a new set of potential pitfalls when doing potentially type-inappropriate
destructuring. (I see from the text that you sometimes get undefined, and
sometimes a TypeError?) Does JS need more of that?

Why not keep it simple? It may have its flaws, but the JavaScript mental
model, once you figure out some corner cases, is really simple and powerful. I
find it sometimes very elegant. This feature bloat reduces that, IMO, and
could hurt understandability of JS code.

~~~
jeswin
I down voted you, I owe you an explanation.

> I really do not like this constant stream of new features in JavaScript

ES6/Ecmascript Harmony was announced seven years back, and has been actively
discussed for the last four years. So the constant stream of new features you
see are JS engines implementing parts of the standard, which is now in its
final shape. ES-Discuss mailing lists* are open for community participation
(like someone mentioned below) and is led by people with a lot of experience
in their respective focus areas. Every new feature has had to pass through
many levels of debates, and have (mostly) been borrowed after being found
successful either in other languages or libraries.

> I feel like these new features are bloating it, and for what?

De-structuring is fairly easy to grasp, and is not even one of the main draws
in ES6. Since the last changes to JS, the environment and therefore
expectations have changed a lot: (a) JavaScript is now a server-side
technology in a big way, (b) async has become increasingly important, (c)
people are writing very complex apps in JS. Without the changes in ES6 (and
ES7/8 going forward), developers would have hated working with JS and
JavaScript could have slowly died.

If you have been following, you'd notice that JS has a very vibrant community
around it. One big reason for that is that they see the language evolving with
the technology that surrounds it. Finally, note that all the new enhancements
have come without breaking backward compatibility.

* I lurk there, not a contributor. Have learnt a ton from just subscribing to it.

~~~
bakhy
> I down voted you, I owe you an explanation.

I do think you might be breaking the guidelines of the site - downvote should
not be used just for disagreement, or? - but who cares, you're certainly not
the only one ;) In any case, thank you for the explanation! I appreciate it.

As for the rest, I partly agree. I am too late to voice any constructive
feedback. It was just a general comment on the direction things seem to be
going. And I'd rather not participate in the es-discuss, because JavaScript is
not my professional focus any more, so I do not have the time or the required
expertise. I understand that my comment seems kind of like taking a passing
shit on someone's hard work, I'm sorry about that. I hope everyone takes it as
nothing more than what it is - just an opinion ;)

All in all, I feel the language is getting overstretched and overcomplicated.
And when I see destructuring, I wonder if it's really worth it. Too much
complexity can be a problem.

~~~
DanBC
> I do think you might be breaking the guidelines of the site - downvote
> should not be used just for disagreement, or?

Well, it's not mentioned in the guidelines, and there's a bunch of people who
do use downvote to disagree. There's a few posts from PG; one where he says
that people upvote to agree so it's understandable that they downvote to
disagree, and others where he sees it as a problem to be addressed.

It's probably a good idea for people to upvote any posts they think have been
unfairly downvoted. This sometimes happens, but probably not often enough.

~~~
bakhy
Maybe downvotes could be split in two.

1\. a downvote button, which is available to everyone, but affects only the
ranking on the comment list (no graying out due to too many downvotes)

2\. a "flag" button, which is available to people with higher karma and serves
for moderating.

...because, it seems perfectly human to want to express disagreement with a
downvote. I can totally get it, and would/will probably have a hard time
restraining myself ^^

------
darklajid
I'm not really into web/frontend stuff. I'm interested, but it's not part of
my day job and so I dabble only, play a bit here and there.

My last experiments caused lots of browser based problems
(string.prototype.contains doesn't exist in IE9 etc..) and ES6 features were a
no-go for the same reason. How do you actually use things like the fat arrow
etc. in applications today?

Is that limited to server-side code for now (and for quite some time..)? Do
you decide to ignore a number of ~relevant~ browsers? Build tools to generate
a 'lower' dialect/subset?

Any intro to this specific topic, i.e. 'making sure your ~modern~ code works
in last years browsers'?

~~~
joshuacc
For runtime features like String.prototype.contains you can use a polyfill
library like es6-shim[1]. For new syntax, you can use a compiler like
Babel[2].

[1]
[https://github.com/paulmillr/es6-shim/](https://github.com/paulmillr/es6-shim/)
[2] [https://babeljs.io/](https://babeljs.io/)

~~~
lobster_johnson
Babel comes with Core.js [1] built in, which is (I believe) a better and more
exhaustive shim library than es6-shim and the others.

[1] [https://github.com/zloirock/core-js](https://github.com/zloirock/core-js)

------
dandelany
I've been writing ES6 with Babel for a couple of months now, and I've fallen
in love with destructuring - it's my second favorite part, besides arrow
functions. My only complaint, having run into this a few times in real code,
is that you can only use destructuring with _assignment_ , not with existing
variables. So this works:

    
    
      let myThing = {a: 4};
      let {a} = myThing;
    

But this does not:

    
    
      let a = 5;  
      let myThing = {a: 4};
      {a} = 5;
    

I understand there are some problems of ambiguity here, but it seems to me
this could be made to work somehow?

~~~
loganfsmyth
Yeah, I wish that were handled better too. Since the parser is expecting a
statements, it parses it like

    
    
        {
          a
        }
    
        = 5
    

As in, a block with just an "a" in it, followed by an assignment to nothing,
which throws an syntax error.

You can however do

    
    
        ({a} = 5);
    

to make the parser switch to expecting a destructuring pattern instead of an
block statement.

~~~
dandelany
Whoops, my last line contains a major typo and should read `{a} = myThing;`.
And you've dutifully carried over my typo to your example :D

But regardless, the corrected version of your example: `let a; ({a} = {a:5});`
works like a charm and is very handy! Thanks for the tip!

------
thomasvarney723
I recognize destructuring from Clojure. Did it origniate there? Or is this a
specific case of pattern matching?

~~~
bshimmin
Other languages have this too, eg. Ruby:

    
    
      a, b = [1, 2]
      a, b = {:a => 1, :b => 2}
    

Similarly in Python, at least for the array example (there's probably some way
of destructuring a dictionary in Python, but I don't know it myself).

PHP also has `list()`...

~~~
petepete
For arrays, yes, but that won't work for Ruby hashes (which aren't ordered):

    
    
        [1] pry ~ »  a, b = {a: 1, b: 2}
        {
            :a => 1,
            :b => 2
        }
        [2] pry ~ »  a
        {
            :a => 1,
            :b => 2
        }
        [3] pry ~ »  b
        nil

~~~
bshimmin
You're quite right, I meant to add `values_at` in the hash example! And the
syntax is still not so nice:

    
    
      irb(main):005:0> a, b = {:a => 1, :b => 2}.values_at(:a, :b)
      => [1, 2]
    

Or:

    
    
      irb(main):006:0> a, b = (h = {:a => 1, :b => 2}).values_at(*h.keys)
      => [1, 2]
    

Edit: Ruby's hashes _are_ ordered since 1.9, incidentally.

~~~
petepete
a, b = {a: 1, b: 2}.values

------
itajaja
Adding to babel and traceur, Typescript is catching up with all ES6 features,
so you can use destructors (discussed
[here]([https://github.com/Microsoft/TypeScript/issues/240)](https://github.com/Microsoft/TypeScript/issues/240\)))
in Typescript v1.5--beta

~~~
tracker1
Does typeScript support generator syntax and a Promise shim yet? What about
generators? These are pretty much required to get to async/await goodness.

I ask because I am genuinely interested, and haven't looked into it for a
while, only passively in the context of Angular 2. The addition of attributes
(@ tagging) is specifically interesting to me as I think it's a cleaner syntax
visually than mounting methods onto a function after declaration.

With BabelJS, I can use flow to get type annotations, but not sure about meta
annotations (like attribute declarations in .net)

~~~
sesutton
Both async/await and generators are on the roadmap[0] for the next version.

[0]
[https://github.com/Microsoft/TypeScript/wiki/Roadmap](https://github.com/Microsoft/TypeScript/wiki/Roadmap)

------
crousto
I love it!

As someone else also mentioned in the comments, it is unapologetically
dynamic. In many ways, this reminds me of Perl. The array destructuring
assignment is such a common pattern in that language (eg.
[http://perldoc.perl.org/functions/caller.html](http://perldoc.perl.org/functions/caller.html))

What's even more exciting/terrifying is that ES6 goes even beyond the patterns
allowed in Perl!

~~~
bmn_
No, it's worse. I'm talking about the part where you want to pull only parts
off a compound:

ES6:

    
    
        let [,,,,,,,,, tenth_item] = somearray;
    

Perl:

    
    
        my (undef x 9, $tenth_item) = @somearray;
    

The ES language designers got that wrong. You carefully have to count the
number of commas, and use an invisible nothing in between. Instead they should
have made it so that you assign to undefined, or perhaps null, or make up a
new special identifier named _ (like it is used in some other languages) and
then the assignment operation is smart enough to discard the value.

This is not only better because now there is a visible thing to see and talk
about, but also allows the comma operator to be much less restricted and frees
the programmer to be more expressive. In Perl, multiple commas are collapsed
similar like multiple whitespace collapses in HTML. The expression a,,,,,,b is
identical to a,b – also it does not matter where in the expression the
multiple commas are.

In ES5 and later multiple commas _at the end_ are collapsed into one, but
otherwise multiple commas are kept. This is lame because inconsistent.

~~~
sesutton
You could just write:

    
    
             let [tenth_item] = somearray.slice(9)

~~~
bmn_
You're missing the point here. Slice works with only part of the compound
about to be ripped apart. However, destructuring with commas is generic.
Example that has no simple slice method call:

    
    
        let [,second,,fourth,,sixth]

------
istvan__
I think this is a great language feature, using it a lot in OCaml and Clojure.
I am not sure how much this will improve JS but I guess we can just wait and
see.

~~~
jsprogrammer
Benefits are already known and experienced.

~~~
istvan__
I mean how much the developers are picking up this. I see it as a pattern that
developers sometimes reject new features on the basis that "they don't need
it".

------
ffn
var [wtf] = NaN; console.log(wtf); // undefined

I don't know about you, but this sensibly gives a TypeError when I did it in
my console on Firefox 38. Please let this article be more dated than my
firefox browser, because making NaN auto-coerce into an iterator sounds
incredibly stupid.

~~~
verbin217
I suspect it's just accessing NaN[0]. Either form of property access ([] or .)
implied coercion to object (not iterable) in ES5. It makes sense that
destructuring would build on that behavior.

~~~
bzbarsky
Array destructuring doesn't use [0] access. It iterates the right-hand side.
On the one hand, that means that trying to array-destructure NaN does in fact
throw. On the other hand, it means you can array-destructure a generator,
which is a very desirable thing to be able to do.

------
Ono-Sendai
Wow, that's some pretty complicated stuff. I can only imagine the monstrous
code that could be written using this.

~~~
BinaryIdiot
As with every language these can certainly be abused (perhaps more easily than
other aspects) but I also see great usefulness in these.

------
gcb0
that's awful syntax IMHO.

like php's list/map but with several levels.

those are just asking for bugs that are hard to spot even on code reviews.

~~~
implicit
Other languages have implemented this idea for decades now. It's a great
feature.

The main thing that bothers me is that the default behaviour is too
permissive.

    
    
        let a = [1,2,3,4,5];
        let [b] = a; // ok.  b receives 1
    

The equivalent Python will fail because there are "too many values to unpack."

~~~
jeswin
I prefer the JS version. If you're a dynamic language, be unapologetically so.

    
    
      /* I can write code independent of getCheapestSellers() impl */
      var [bestPrice, secondBest] = getCheapestSellers("product_name");

~~~
DasIch
That has nothing to do with being dynamic or not. Besides your example
translates quite nicely to Python as well:

    
    
        [bestPrice, secondBest, _*] = getCheapestSellers('product_name')

~~~
jeswin
My question is, why? If one wanted this type of guarantee consistently, they
might as well use a statically typed language.

Add: To clarify, the error shows up at runtime in Python. So you're going to
need tests anyway. Just as in JS.

Btw (unrelated), you can do the same thing in JS as well [a, b, ...rest] = [1,
2, 3, 4, 5]

------
z3t4
I fail to see this doing anything besides turning the syntax backwards!? Maybe
it's useful for those of us who are used to write from right to left, like in
Arabic, but I would still use var foo = bar[0] over var[foo] = bar any day. It
gets even stupider with objects: var foo = bar.a, vs, var {a: foo} = bar.

JavaScript is a good language because it's so simple and easy to learn, adding
stuff like destructor's just make it more confusing! Making a language more
confusing just because of strong preference with syntactic sugars is a
mistake. I would like to see a forking of the language before this goes
mainstream. Maybe called CJS, where C stands for Clean or Classic JavaScript.

~~~
arcatek
The point is apparent is you use actual use cases.

    
    
      let {title, content, email} = articles[0];
      let [all, ident, domain] = email.match(/[^@]*@.*/);
      return `<h1>${title} (${ident} at ${domain})</h1><p>${content}</p>`;
    

Secondly, there is no destructor in JS (because of dynamic memory management),
and there will probably never be. Destructuring is nothing like destructors.

Thirdly, the language doesn't have to fork. You can still write ES5 code. But
you should probably not because, believe it or not, ES2015 is a really good
thing, full of improvements. And syntaxic sugars are part of what makes using
it so much nicer than ES5.

~~~
z3t4
Articles should already be an associative array in the first place.

I can understand why you think the email example looks better, but whoops,
there's a bug, you now got two undefined variables!

~~~
woah
So.... How would a lack of destructuring avoid this? You would still have two
undefined variables, plus a bit more typing.

~~~
z3t4
It would probably have looked like this:

    
    
      var email = article.email,
        at = email.indexOf("@"),
        ident = email.substr(0, at),
        domain = email.substring(at + 1, email.lengt);

------
ilaksh
The part of this that is disengenuous and a little silly is that they are just
slowly adding in features from CoffeeScript/IcedCoffeeScript and nobody
mentions that.

And in order to do it you have to use something like Babel.

Why not just move to CoffeeScript, or IcedCoffeeScript, or ToffeeScript, or
even LiveScript?

The only explanation I can see is that people just aren't capable of learning
the full new languages and so the standards people are leading you by the hand
like you just got off the short bus.

AND, the standards people don't want to admit that individuals did their jobs
for them years ago, don't want to use someone else's design for implementing
those features and so insist on slowly coming up with their own independent
implementations for engines.

This is a microcosm that demonstrates the relationship between technology and
society. There is just a huge lag between the newest and best ideas or systems
and what the group or individuals or systems can grasp or absorb.

Take this existing many-year gap and multiply by 10X or 100X and you will
start to perhaps understand the concept of the technological singularity.

~~~
dragonwriter
The standards people very often are the same people, or represent the same
organizations, that implemented the features in other places ahead of it being
standardized as part of ES.

