Can You Use It Yet?
Module support exists in all of the evergreen browsers, but some of them still require setting a flag to use it. Native ECMAScript module support will hopefully ship in Node.js in spring 2018. In the meantime @std/esm enables use of ECMAScript modules in current Node releases.
Block Scoped Declaration (let and const)
Replacing var with let gives each iteration of the loop a distinct variable binding. So each event handler captures different variables with the values that were current when the event handler was installed:
The hardest part about adding block scoped declaration to ECMAScript was coming up with a rational set of rules for how the declaration should interact with the already existing var declaration form. We could not change the semantics of var without breaking backwards compatibility, which is something we try to never do. But, we didn’t want to introduce new WTF surprises in programs that use both var and let. Here are the basic rules we eventually arrived at:
Most browsers, except for IE, had implemented const declarations (but without block scoping) starting in the early naughts. Firefox implemented block scoped let declaration (but not exactly the same semantics as ES2015) in 2006. By the time TC39 started serious working on what ultimately became ES2015, the keywords const and let had become ingrained in our minds such that we didn’t really consider any other alternatives. I regret that. In retrospect, I think we should have used let in place of const for declaring immutable variable bindings because that is the most common use case. In fact, I’m pretty sure that many developers use let instead of const for variable they don’t intend to change, simply because let has fewer characters to type. If we had used let in place of const then perhaps var would have been adequate for the relatively rare cases where a mutable variable binding is needed. A language with only let and var would have been simpler then what we ended up with using const, let, and var.
I’ve heard several JS programmers comment that at first they didn’t like arrow functions but that they grew upon them over time. Your mileage may vary. Here are a couple of good articles that address arrow function reluctance.
Actually, ES modules weren’t inspired by Node modules. But a lot of work went into making them feel familiar to people who were used to Node modules. In fact, ES modules are semantically more similar to the Pascal modules that Dave remembers then they are to Node modules. The big difference is that in the ES design (and Pascal modules) the interfaces between modules are statically defined while in the Node modules design module interfaces are dynamically defined. With static module interfaces the inter-dependencies between a set of modules are precisely defined by the source code prior to executing any code. With dynamic modules, the module interfaces cannot be fully understood without actually executing the code of the modules. Or stated another way, ES module interfaces are declaratively defined while Node module interfaces are imperatively defined. Static modules systems better support creation of ahead-of-time tools such as accurate module dependency linters or module linkers. Such tools for dynamic module interfaces usually depends upon applying heuristics that analyze modules as if they had static interfaces. Such analysis can be wrong if the actual dynamically interfaces construction does things that the heuristics didn’t account for.
The work on the ES module design actually started before the first release of Node. There were early proposals for dynamic module interfaces that are more like what Node adopted. But TC39 made an early decision that declarative static module interfaces were a better design, for the long term. There has been much controversy about this decision. Unfortunately, it has created issues for Node which have been difficult for them to resolve. If TC39 had anticipated the rapid adoption of Node and the long time it would take to finish “ES6” we might have taken the dynamic module interface path. I’m glad we didn’t and I think it is becoming clear that we made the right choice.
ES2015 needed to include promises because they were being rapidly adopted by the developer community (include by new browser APIs) and we wanted to avoid the problem of competing incompatible promise libraries or of a browser defined promise API that didn’t take other host environments into consideration.
The real benefit of ES2015 promises is that they provided a foundation for better async abstractions that do bury more of the BS within the runtime. Async functions, introduced in ES2017 are the “better way” to do async. In the pipeline for the near future is Async Iteration which further simplifies a common async use case.