The article is not very good, with both factual mistakes and missing important sub-properties
> 2. Array helper functions
`map`, `filter`, `reduce`, `some` and `every` were actually added in ES5.1. Only `find` and the unlisted `findIndex` come from ES6.
Meanwhile Array.from which was added in ES6 goes unlisted.
> 3. Arrow functions
While the reduction in definition boilerplate is great, missing is the very important (and convenient!) fact that arrow functions resolve `this` lexically (as a normal variable rather than a special case).
> 4. Classes
Would have benefited from mentioning the most convenient feature of classes by far: the `super` proxy to calling a method of the superclass on `this` (most useful with constructors): the ES5
<Superclass>.prototype.aMethod.call(this)
becomes:
super.aMethod()
which also helps a lot when moving methods around.
> 6. Template strings
Missing: you can "tag" template strings to invoke user-defined functions on the contents and do intermediate processing and interpolation yourself e.g.
sql`foo ${bar}`
will invoke `sql(['foo ', ''], <value of bar>)`.
> 7. Default function arguments
Missing note: unlike e.g. Python, default arguments are dynamic and sequential (similar to let<star>[0]) so you can define a `function foo(a, b=a)`.
> 8. Rest and spread operators
Actually the opposite for once, mentions that rest and spread operators can be used with object content, sadly that's currently a stage3 proposal, definitely not part of ES6.
> 9. Destructing
Missing notes:
* the destructuring is recursive (you're not limited to a single level)
* you don't have to "[assign values] to variables of the same name as properties" when destructuring objects:
let {name: a} = a_call();
will bind the value of the property "name" to the "a" local variable.
* you can provide default values to the destructuring pattern e.g.
let [a, b=3] = [1]
this can be combined with renaming for object destructuring
[0] http://docs.racket-lang.org/reference/let.html#%28form._%28%... can't use an actual star there because HN comment formatting remains a complete trash fire and thus decides to emphasize half my comment and swallow the mark of the first list item. And still does not print let∗.
I agree that the article needs some work, but there really is nothing wrong with still using var. I'm in the Kyle Simpson camp and just use var for everything except if I'm actually creating a constant or block scoping comes into play or I need to prevent the hoisting of a variable. That way, it's clear what I'm doing and what my intentions are.
> That way, it's clear what I'm doing and what my intentions are.
It's the exact opposite since you're defaulting to the most permissive binding style.
Defaulting to `const` would actually make it clear what your intent is: you use `let` if you need mutable bindings and you use `var` if you need hoisting.
If you default to `var` your intent is apparently to make use of mutable bindings and hoisting everywhere, at which point I can only expect that you put your variable declarations at the bottom of your function blocks after the `return` statement.
Defaulting to const does nothing. If you or someone else decides later that it should be reassignable, then they'll change it and not think about it. If const is rare in your code, people will be aware of what const means in that context.
If you're putting your variable declarations after your return statement, then you probably have other problems. I also don't know why you would ever expect someone to do that.
> Defaulting to const does nothing. If you or someone else decides later that it should be reassignable, then they'll change it and not think about it.
Which they'll do regardless. `const` is an indication that the binding is not updated in the rest of the scope and thus that said scope can be perused without needing to consider an update to the binding itself. It is a guarantee of SSA, nothing more and nothing less.
> If const is rare in your code, people will be aware of what const means in that context.
The same as above which you apparently assert is "nothing"?
> If you're putting your variable declarations after your return statement
Why would I do that? I'm not adamantly using `var` when I have no reason to.
If everything is a constant then nothing is a constant.
Code bases change and some things that a coder did not foresee needing to be reassignable may later need to be reassignable. There aren't frequent cases that switching them becomes a problem, and as a result, people switch them. Since they do get switched so often, people stop thinking about when it really matters or if there was some intent to it originally being written that way. If everything is a constant, then the cases where it matters and the cases where it doesn't get blended together and it's impossible to differentiate the two.
When real, true constants are the only things declared with const, then it's abundantly clear what the intention of the coder was and that the value should never be changed nor should it ever be switched over to let/var.
Likewise with let, in the few cases where it's used it draws others' attention to the use of block scoping or the avoidance of hoisting. It's rare that those things really come into play, so the few cases where it's present really stand out.
tl;dr Block scoping and constants are the exception, not the DEFAULT, so you should write code accordingly.
> If you're putting your variable declarations after your return statement
What I meant here is that people shouldn't keep using var just so they can continue writing bad code.
> There aren't frequent cases […] Since they do get switched so often
They don't. In fact they almost never do.
> tl;dr Block scoping and constants are the exception
That's literally the opposite of reality. Constant bindings (single-assignment) and block-scoped bindings are by far the most common state of affairs even if not formally stated.
> What I meant here is that people shouldn't keep using var just so they can continue writing bad code.
Maybe you should tell the you of two comments ago who's apparently arguing for exactly that?
What's with the hostility? I'm just trying to express my opinion. You can disagree and state your point without being insulting.
Sure, when I say frequent, I don't mean its happening all the time all over the place, I just means it can and does happen without consequence. Block scoping and constants, in the sense that it's important that they're block scoped and constant, are the exception. Most variables are single-assignment not out of necessity. And I bet most people think in function scope, regardless of how the compiler works.
It all still comes down to intent. If you're always defaulting to using const, your intention is not really showing through.
I think a good case can be made that examples meant to illustrate a single new feature are better if the only new features they use are that one and any others that are necessary to make a reasonable example.
That is one thing which miffed me a bit about Python 3.6's f-strings. Whether their addition is useful or not may be up for debate but the inability to process them in userland seems one hell of a missed opportunity.
"Who doesn’t like explicit inheritance, like in Java language, instead of writing magic code for prototypal inheritance?"
There's nothing magical about prototypal inheritance code, and I'd suggest the class keyword is more magical since it's "primarily syntactical sugar over JavaScript's existing prototype-based inheritance" [1]. Furthermore, encouraging classical inheritance constructs without mentioning the classic pitfalls is somewhat harmful [2].
I never understood the whole confusion about prototypal inheritance. It's not harder to grok than classical inheritance.
"Which Java developer doesn’t miss classes when switching to a JS project?" ... That's the biggest problem here. Java != JavaScript. You shouldn't blindly carry concepts from one language to the other.
Adding syntactic sugar with the only goal of pleasing developers coming from other language goes against my view of what features should be added to a language. That's code bloat.
> I never understood the whole confusion about prototypal inheritance. It's not harder to grok than classical inheritance.
I'd guess, mostly based on my fuzzy memory of my first exposure to prototypal inheritance, that this confusion stems almost completely from the fact that prototypal inheritance is simply different from classical inheritance. Having come from a C++ world, I didn't realize there was another kind of inheritance, until I met JavaScript.
> I never understood the whole confusion about prototypal inheritance. It's not harder to grok than classical inheritance.
In theory no, but Javascript has such bad support for it that it's syntactically inconvenient and you have to understanding significantly more details of the implementation to manage it than just "you add the other class after the extends".
Pretty simple really: lots of people learn Java or other languages with similar inheritance models in school or very early in their careers from books/peers. It takes a while, and it's used in a lot of places.
JS style inheritance is a bit more unique (compared to other languages commonly used on the job). Even though its arguably simpler, it's not something nearly as many people know, especially relative to how many people know JS.
Thus it becomes this big scary thing. A bit like Monads that take minutes to learn but are scary because no one knows them.
Many comments are saying that the article is not good as it is not accurate. (e.g. @masklinn)
I agree. The author could have spent a bit of time reasarching this since bullet-point-articles are probably the easiest to write down.
However, it's interesting (yet sad) how its catchy title "Top ES6 features by example" is making darn popular.
The current "GitHub/your blog is your resume!!" movement has led to people who have little business writing didactic resources (not because of programming skill, but lack of research, teaching experience, and writing abilities) to do so in hordes.
It's hard to complain without sounding like a jerk because these are free to read, but they can waste people's time or confuse them.
With such lagging browser support forcing you to compile ES6, it generally makes more sense to jump to TypeScript. You get all the E56 plus things like interfaces and enums and, of course, strict typing.
A couple of years in some cases. Many large sites still have to support IE10 and up.
Android support was incredibly bad until recently for example, so anyone with a large mobile audience will have issues as well (I still see 100,000s of Android 4.4 requests for example)
Not everyone can just support Edge/FF/Chrome evergreen versions.
Strict typing isn't always desirable, depending on the application of the language. With it you give up a certain level of developer productivity; appropriate if you're using Javascript for backend logic with Node, not so much if you're building simple views with React.
I don't think so, or at least it doesn't feel like it. Typescript's type inference is so good that you only need types maybe 15% of the time. It also has shortcuts like constructor property initializers and support for async even when targeting ES5.
I would bet money that you actually write less code with Typescript than regular javascript because of all the streamlining and shortcuts. And with the type inference you almost get static typing for free.
I hear this argument come up frequently against TS and all I can say is I'm fairly sure it's from people that haven't used it.
Sometimes it's also from people who use JavaScript in a more functional context, e.g. with currying. Suddenly it requires a lot of effort to still have TypeScripts benefits.
https://gist.github.com/donnut/fd56232da58d25ceecf1
I've been working with Typescript for the past six months. Javascript is starting to shine as a functional programming language, and the communities who develop the useful tools that we actually need are moving in that direction too.
Typescript feels like a step in a completely different direction. The problems that TS tries to solve have other solutions. Solutions more idiomatic to javascript development. Redux style architectures, grouping code by feature, libraries over frameworks, GraphQL and so on. And these solutions are solid and dependable. Unlike the made up brittle and often incorrectly defined types that Typescript and Flow demand. If you want to use Typescript, then I hope you really really REALLY like Generics, because they're absolutely everywhere in Typescript. They're the solution to the fundamental flaw with TypeScirpt, it wasn't designed with the actual job that Javascript does in mind.
It's Javascript for people who don't like Javascript and don't care to learn it. Well that's fine, each to their own. But people sing it's virtues without pointing out any of the flaws in both the approach or the implementation (maybe because they simply don't see anything wrong), and that really rustles my jimmies.
I personally prefer a style/practice that doesn’t work well with TS, but I appreciate it exists and typedef files for libraries are very nice for developing in things like VSCode.
> you give up a certain level of developer productivity
IMO the opposite is true; without types you give up some developer productivity in the form of time spent writing tests and debugging issues that the type system would obviate automatically. In my view, loose typing adds the illusion of productivity because the developer might presume they are working faster because they don't have to reason about type restrictions, however they simply end up enforcing type restrictions manually (and relatively poorly compared to a solid type system) using "typeof" and "instanceof" checks throughout their code. In general, assigning multiple types to the same variable is considered a code smell anyway, so strict typing simply enforces what is already considered a best practice.
Of course, this doesn't mean that choosing typescript in particular is always the right engineering decision, but, all other things being equal, a strict type system is almost always preferable to a loose one.
Maybe it just depends on the application. I've been doing frontend development with React and vanilla Javascript for the past 2.5 years, and I can't recall a single user-facing bug caused by improper dynamic typing.
I would push back on the idea that it depends on the application, I think what's more likely happening is that your experience has codified best practices that help to minimize these types of bugs, but you're still manually doing the work to protect against these error with typeof/instanceof and unit tests that validate input parameters.
> Strict typing isn't always desirable... you give up a certain level of developer productivity
I mostly agree, I just wanted to point out that it's funny (to me, anyway) that the same argument applies in both cases. If you don't have type scripting, you also give up a certain level of developer productivity.
When you have to hunt down type bugs a linter or compiler would have caught, you can waste large amounts of time. I think type bugs are usually fast to find & fix, and only occasionally take time.
I like using a type checker of some sort (Closure compiler is probably my fave) for release builds, and leave it off for dev builds. I like this option even more than TypeScript, because you don't necessarily need to have a build step for running dev code at all. Then you get the best of both worlds, you can iterate quickly without adding or checking types, then you can spend a little time checking types before merging a branch. Any time a mysterious bug pops up, first order of business is check types.
Even for React, I'd still rather have the option to type check than not have it. Flow is nice because once setup & running, it's really fast. I've seen it catch long standing type bugs that nobody thought were there.
Typescript has a config option (allowJs: true) that does exactly this -- types where you want them, regular ES6 when you don't. I type things like component Props/State, reducers, global state, and domain models. For everything else I tend to use regular, idiomatic JS.
I don't think it's lagging - it's just working at "internet spec speed". That seems like an eternity, but time flies like an arrow (remember supporting IE5.5?!)...
I already use all ES6/7 features (plus modules support with <script type="module">) for many of my personal projects and experiments: no transpilers, no build tools. I like knowing that in 15 years I'll be able to double click an index.html and run/edit my old experiments without worrying about how I'd to find the right version of some long-dead build tool - just like I do with my javascript experiments from 2002!
The typing is nice, but shit like enums are a plague in TS. Non-standard crap that will probably never be standard, and in some cases actually are present (in a compiled form) in the output. No, just no.
And then you have all of the fun stuff like their original non-standard module system, which is now super confusing in type definitions since it lives side by side with the "real" thing. I'm sure we're gonna have lots of fun having 2 ways of defining private members too. Woo. I assume decorators are gonna be entertaining too since they were based on the older version of the spec (I assume. I did not verify this claim).
I love using TS in my side projects because I avoid "the bad parts" (same as with vanilla JS when you think about it), but when we introduce it at work, Im half considering making a lint rule that blocks all the non-standard features that are not related to type checking. Will save some pain in the future.
Quite a bit of negativity in the comments here. I liked the article. Typically I rely on code samples in articles like this, but I found the paragraph descriptions very readable.
55 comments
[ 3.8 ms ] story [ 19.9 ms ] thread> Standard ECMA-262 5.1 Edition / June 2011
> 2. Array helper functions
`map`, `filter`, `reduce`, `some` and `every` were actually added in ES5.1. Only `find` and the unlisted `findIndex` come from ES6.
Meanwhile Array.from which was added in ES6 goes unlisted.
> 3. Arrow functions
While the reduction in definition boilerplate is great, missing is the very important (and convenient!) fact that arrow functions resolve `this` lexically (as a normal variable rather than a special case).
> 4. Classes
Would have benefited from mentioning the most convenient feature of classes by far: the `super` proxy to calling a method of the superclass on `this` (most useful with constructors): the ES5
becomes: which also helps a lot when moving methods around.> 6. Template strings
Missing: you can "tag" template strings to invoke user-defined functions on the contents and do intermediate processing and interpolation yourself e.g.
will invoke `sql(['foo ', ''], <value of bar>)`.> 7. Default function arguments
Missing note: unlike e.g. Python, default arguments are dynamic and sequential (similar to let<star>[0]) so you can define a `function foo(a, b=a)`.
> 8. Rest and spread operators
Actually the opposite for once, mentions that rest and spread operators can be used with object content, sadly that's currently a stage3 proposal, definitely not part of ES6.
> 9. Destructing
Missing notes:
* the destructuring is recursive (you're not limited to a single level)
* you don't have to "[assign values] to variables of the same name as properties" when destructuring objects:
* you can provide default values to the destructuring pattern e.g. [0] http://docs.racket-lang.org/reference/let.html#%28form._%28%... can't use an actual star there because HN comment formatting remains a complete trash fire and thus decides to emphasize half my comment and swallow the mark of the first list item. And still does not print let∗.It's the exact opposite since you're defaulting to the most permissive binding style.
Defaulting to `const` would actually make it clear what your intent is: you use `let` if you need mutable bindings and you use `var` if you need hoisting.
If you default to `var` your intent is apparently to make use of mutable bindings and hoisting everywhere, at which point I can only expect that you put your variable declarations at the bottom of your function blocks after the `return` statement.
If you're putting your variable declarations after your return statement, then you probably have other problems. I also don't know why you would ever expect someone to do that.
Which they'll do regardless. `const` is an indication that the binding is not updated in the rest of the scope and thus that said scope can be perused without needing to consider an update to the binding itself. It is a guarantee of SSA, nothing more and nothing less.
> If const is rare in your code, people will be aware of what const means in that context.
The same as above which you apparently assert is "nothing"?
> If you're putting your variable declarations after your return statement
Why would I do that? I'm not adamantly using `var` when I have no reason to.
Code bases change and some things that a coder did not foresee needing to be reassignable may later need to be reassignable. There aren't frequent cases that switching them becomes a problem, and as a result, people switch them. Since they do get switched so often, people stop thinking about when it really matters or if there was some intent to it originally being written that way. If everything is a constant, then the cases where it matters and the cases where it doesn't get blended together and it's impossible to differentiate the two.
When real, true constants are the only things declared with const, then it's abundantly clear what the intention of the coder was and that the value should never be changed nor should it ever be switched over to let/var.
Likewise with let, in the few cases where it's used it draws others' attention to the use of block scoping or the avoidance of hoisting. It's rare that those things really come into play, so the few cases where it's present really stand out.
tl;dr Block scoping and constants are the exception, not the DEFAULT, so you should write code accordingly.
> If you're putting your variable declarations after your return statement
What I meant here is that people shouldn't keep using var just so they can continue writing bad code.
They don't. In fact they almost never do.
> tl;dr Block scoping and constants are the exception
That's literally the opposite of reality. Constant bindings (single-assignment) and block-scoped bindings are by far the most common state of affairs even if not formally stated.
> What I meant here is that people shouldn't keep using var just so they can continue writing bad code.
Maybe you should tell the you of two comments ago who's apparently arguing for exactly that?
Sure, when I say frequent, I don't mean its happening all the time all over the place, I just means it can and does happen without consequence. Block scoping and constants, in the sense that it's important that they're block scoped and constant, are the exception. Most variables are single-assignment not out of necessity. And I bet most people think in function scope, regardless of how the compiler works.
It all still comes down to intent. If you're always defaulting to using const, your intention is not really showing through.
https://github.com/getify/You-Dont-Know-JS/blob/master/this%...
The whole serie of book is worth it and free to read on github
There's nothing magical about prototypal inheritance code, and I'd suggest the class keyword is more magical since it's "primarily syntactical sugar over JavaScript's existing prototype-based inheritance" [1]. Furthermore, encouraging classical inheritance constructs without mentioning the classic pitfalls is somewhat harmful [2].
[1] https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...
[2] https://medium.com/javascript-scene/javascript-factory-funct...
"Which Java developer doesn’t miss classes when switching to a JS project?" ... That's the biggest problem here. Java != JavaScript. You shouldn't blindly carry concepts from one language to the other.
Adding syntactic sugar with the only goal of pleasing developers coming from other language goes against my view of what features should be added to a language. That's code bloat.
I'd guess, mostly based on my fuzzy memory of my first exposure to prototypal inheritance, that this confusion stems almost completely from the fact that prototypal inheritance is simply different from classical inheritance. Having come from a C++ world, I didn't realize there was another kind of inheritance, until I met JavaScript.
In theory no, but Javascript has such bad support for it that it's syntactically inconvenient and you have to understanding significantly more details of the implementation to manage it than just "you add the other class after the extends".
JS style inheritance is a bit more unique (compared to other languages commonly used on the job). Even though its arguably simpler, it's not something nearly as many people know, especially relative to how many people know JS.
Thus it becomes this big scary thing. A bit like Monads that take minutes to learn but are scary because no one knows them.
However, it's interesting (yet sad) how its catchy title "Top ES6 features by example" is making darn popular.
It's hard to complain without sounding like a jerk because these are free to read, but they can waste people's time or confuse them.
What lagging browser support? Most of those things have been there for years...
Android support was incredibly bad until recently for example, so anyone with a large mobile audience will have issues as well (I still see 100,000s of Android 4.4 requests for example)
Not everyone can just support Edge/FF/Chrome evergreen versions.
I would bet money that you actually write less code with Typescript than regular javascript because of all the streamlining and shortcuts. And with the type inference you almost get static typing for free.
I hear this argument come up frequently against TS and all I can say is I'm fairly sure it's from people that haven't used it.
Typescript feels like a step in a completely different direction. The problems that TS tries to solve have other solutions. Solutions more idiomatic to javascript development. Redux style architectures, grouping code by feature, libraries over frameworks, GraphQL and so on. And these solutions are solid and dependable. Unlike the made up brittle and often incorrectly defined types that Typescript and Flow demand. If you want to use Typescript, then I hope you really really REALLY like Generics, because they're absolutely everywhere in Typescript. They're the solution to the fundamental flaw with TypeScirpt, it wasn't designed with the actual job that Javascript does in mind.
It's Javascript for people who don't like Javascript and don't care to learn it. Well that's fine, each to their own. But people sing it's virtues without pointing out any of the flaws in both the approach or the implementation (maybe because they simply don't see anything wrong), and that really rustles my jimmies.
Not a bad one, but very intentionally different.
I personally prefer a style/practice that doesn’t work well with TS, but I appreciate it exists and typedef files for libraries are very nice for developing in things like VSCode.
IMO the opposite is true; without types you give up some developer productivity in the form of time spent writing tests and debugging issues that the type system would obviate automatically. In my view, loose typing adds the illusion of productivity because the developer might presume they are working faster because they don't have to reason about type restrictions, however they simply end up enforcing type restrictions manually (and relatively poorly compared to a solid type system) using "typeof" and "instanceof" checks throughout their code. In general, assigning multiple types to the same variable is considered a code smell anyway, so strict typing simply enforces what is already considered a best practice.
Of course, this doesn't mean that choosing typescript in particular is always the right engineering decision, but, all other things being equal, a strict type system is almost always preferable to a loose one.
I mostly agree, I just wanted to point out that it's funny (to me, anyway) that the same argument applies in both cases. If you don't have type scripting, you also give up a certain level of developer productivity.
When you have to hunt down type bugs a linter or compiler would have caught, you can waste large amounts of time. I think type bugs are usually fast to find & fix, and only occasionally take time.
I like using a type checker of some sort (Closure compiler is probably my fave) for release builds, and leave it off for dev builds. I like this option even more than TypeScript, because you don't necessarily need to have a build step for running dev code at all. Then you get the best of both worlds, you can iterate quickly without adding or checking types, then you can spend a little time checking types before merging a branch. Any time a mysterious bug pops up, first order of business is check types.
Even for React, I'd still rather have the option to type check than not have it. Flow is nice because once setup & running, it's really fast. I've seen it catch long standing type bugs that nobody thought were there.
I already use all ES6/7 features (plus modules support with <script type="module">) for many of my personal projects and experiments: no transpilers, no build tools. I like knowing that in 15 years I'll be able to double click an index.html and run/edit my old experiments without worrying about how I'd to find the right version of some long-dead build tool - just like I do with my javascript experiments from 2002!
And then you have all of the fun stuff like their original non-standard module system, which is now super confusing in type definitions since it lives side by side with the "real" thing. I'm sure we're gonna have lots of fun having 2 ways of defining private members too. Woo. I assume decorators are gonna be entertaining too since they were based on the older version of the spec (I assume. I did not verify this claim).
I love using TS in my side projects because I avoid "the bad parts" (same as with vanilla JS when you think about it), but when we introduce it at work, Im half considering making a lint rule that blocks all the non-standard features that are not related to type checking. Will save some pain in the future.