9 ms·
ES6 in Depth: Destructuring
- gcb0 11y agothat'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 11y agoOther 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 11y agoI 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 11y agoThat 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 11y agoMy 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]
- deleted 11y ago[deleted]
- tracker1 11y agoYay, you now have a variable declaration you aren't even going to use... Assuming "_" in this case is a variable, and not some magic keyword in the language, which is probably more stupid than just ignoring the rest instead of throwing an error. (forgive my Python ignorance)
- DasIch 11y ago_ is just a name and using it for values you are not going to use is after assignment is a convention in Python, Haskell and probably other languages that support pattern matching or destructuring. Keep in mind this also comes up in cases like: foo, _, bar = something() In general you can't avoid doing something like it, why "optimize" for this one special case?
- tracker1 11y agoWhy throw an error for something that isn't needed? This is always something I like about JS... it doesn't throw an error in many situations, where the most likely case would be to suppress it anyway, or when there's no point in throwing one...
- KMag 11y ago> Why throw an error for something that isn't needed? Because it appears the programmer's assumptions have been violated at this point. There's a syntactical way for destructuring assignment of a variable-length list, and it wasn't used. Maybe the programmer was being lazy, or maybe the programmer's assumptions were violated. How do you feel about [a, b, c, d] = [1, 2] ? Should it throw? > it doesn't throw an error in many situations, where the most likely case would be to suppress it anyway, or when there's no point in throwing one... PHP rightly gets lots of flak for attempting to lumber on after the programmer probably made a mistake. JavaScript isn't as bad, but it does hide a fair number of programmer mistakes. For small web apps where failure or incorrect data isn't a big deal, maybe hiding small bugs is the right thing. For large applications, hiding likely programmer laziness or minor bugs is not a good feature.
- implicit 11y agoIt's really valuable for mistakes to result in clear errors rather than confusing but "valid" programs. Even worse: let [a, b, c, d] = [2]; a receives 2. b, c, and d receive undefined.
- nightpool 11y agoWait, side note, but when you think of functional programming your first point of reference is PHP? More then Javascript (which I'm assuming you're probably familiar with, to have such a strong opinion about its syntax)? Am I interpreting this wrong?
- gcb0 11y agoit's not that far away in the sense that one started as functional language and was made c-like, and the other started c-like and was made functional :)
- ffn 11y agovar [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.
- deleted 11y ago[deleted]
- verbin217 11y agoI 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 11y agoArray 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.
- tracker1 11y agovar temp = NaN; var wtf = temp[0]; console.log(wtf); //undefined What else would you expect it to be? JavaScript in general guards against throwing errors as much as possible... I'd much rather see this behavior in practice than having to put extra try/catch blocks everywhere... Not to mention what this would do to async code.
- bzbarsky 11y agoThat looks like a typo in the example. This code: var {wtf} = NaN; would set wtf to undefined, for the reasons described in the article, since it would end up doing (NaN)["wtf"]. But trying to destructure NaN into an array should throw an exception.
- Ono-Sendai 11y agoWow, that's some pretty complicated stuff. I can only imagine the monstrous code that could be written using this.
- BinaryIdiot 11y agoAs with every language these can certainly be abused (perhaps more easily than other aspects) but I also see great usefulness in these.
- girvo 11y agoI'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 11y agoLast 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 11y agoI 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.
- istvan__ 11y agoI 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 11y agoBenefits are already known and experienced.
- istvan__ 11y agoI 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".
- thomasvarney723 11y agoI recognize destructuring from Clojure. Did it origniate there? Or is this a specific case of pattern matching?
- nahiluhmot 11y agoCommon Lisp also has destructuring-bind[0], although its not provided with every implementation. [0] https://www.cs.cmu.edu/Groups/AI/html/cltl/clm/node252.html https://www.cs.cmu.edu/Groups/AI/html/cltl/clm/node252.html
- bshimmin 11y agoOther 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 11y agoFor 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 11y agoYou'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 11y agoa, b = {a: 1, b: 2}.values
- 11y ago
- dandelany 11y agoI'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 11y agoYeah, 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 11y agoWhoops, 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!
- deleted 11y ago[deleted]
- lucian1900 11y agoAt least with let you can reliably shadow the previous binding, which in most cases will be equivalent.
- z3t4 11y agoI 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 11y agoThe 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 11y agoArticles 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 11y agoSo.... How would a lack of destructuring avoid this? You would still have two undefined variables, plus a bit more typing.
- z3t4 11y agoIt 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);
- darklajid 11y agoI'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'?
- Skinney 11y agoA lot of people use transpilers like Babel or Traceur. They compile ES6 to ES5 (or lower) for use in older browsers.
- tracker1 11y agoI use BabelJs (formerly 6to5), and Traceur before that, which transpile/morph your ES6/7 code into something usable with older browsers... Since it will probably be 5+ years until I can actually rely on async/await sugar to be native and not have to worry about legacy browsers, I don't think this is going away any time soon. I actually do a lot ov server-side code in JS (via node/io.js) as well, so the build step isn't so bad.... breaking your code up into separate modules that build separately is a good idea though, so your build times aren't too much.
- joshuacc 11y agoFor 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 11y agoBabel 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
- bakhy 11y agoNo 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.
- itajaja 11y agoI agree with that. All these features are amazing, but the fact that these modification must be only additive make their implementation feel clunky. Javascript was born with enormous defects that it's too late to remove, and adding new features, regardless of their undoubtable usefulness, make the whole language more difficult to handle. The number of concepts required to read ES6 is far more than the one required for ES5, and this number is just destined to get higher and higher, with no possibility to decrease.
- itajaja 11y agoAdding 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
- arcatek 11y agoYou shouldn't use the term "destructor" as an alias of "destructuring expressions" - destructors are an entirely different thing[1], which is not applicable to JS. [1] http://en.wikipedia.org/wiki/Destructor_%28computer_programming%29 http://en.wikipedia.org/wiki/Destructor_%28computer_programm...
- tracker1 11y agoDoes 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 11y agoBoth 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 11y agoI 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_ 11y agoNo, 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 11y agoYou could just write: let [tenth_item] = somearray.slice(9)
- bmn_ 11y agoYou'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]
- ilaksh 11y agoThe 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 11y agoThe 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.