io.js supports ES6 quite well: Node.js (still needs flags for certain features)
I consider it time that we migrate to ES6 for our modules, one by one, measuring the impact as we go.
Considerations:
We would essentially be dropping native support for node.js, with native support only for io.js (this could actually be a good thing)
We will need to use io.js with flags due to some useful features (such as classes) still being staging features
Workarounds:
We could build our modules with a es6 directory that contains our source files, and a es5 directory that contains downgraded compiled files (via tracuer, or es6to5, or whatever).
We could then either:
have package.json's main point to the ES5 version, with the user having to do require('the-module/es6') to get the ES6 copy
or have package.json's main point to a detection script that detects if we have the necessary support, and includes the ES6 version if so, otherwise defaulting to the ES5 version (this will probably cause issues with things like browserify, so the latter is preferable)
CoffeeScript could still be relevant only if the correct decisions about its future were made. Currently, jashkenas position is stucked in a plain denial about the rise of ES6 and nothing concrete is being done for a CS version 2.0.
when reading about the Babel link you sent I was going to be like, “Ben, have you heard of 6to5…?” and then oh yeah that’s what it got renamed to. I skimmed through the log but sounds like you were able to hack it to your purposes.
So one person/group is working on a CoffeeScript ES6 compatible fork? There are already so many flavors of CoffeeScript this is hard to imagine there wouldn’t be one in the works. Is it like, that it’d be ridiculously complex for the CoffeeScript compiler to do this the way it’s written currently? (pardon I just haven’t started searching for it myself yet if it exists )
I think starting on the ES6 journey now would be a good move because there is a fair amount of work to be done. I think we should keep it as minimal as possible to start with so we don’t get too far ahead of vendor native support.
Right now I think we should start with ES6 classes, arrow functions and keep on using node’s require for importing which works great in iojs with --es_staging --harmony_classes --harmony_arrow_functions). Eventually when native comes we can switch everything over to use import and unless there is a requirement to be browser compatible then all this can be done without having to transpile ES6 to ES5.
Example:
// App.js
"use strict";
let Utils = require('./utils');
class App {
constructor() {
new Utils().sayHello();
}
}
// Utils.js
"use strict";
class Utils {
sayHello () {
console.log("hello");
}
}
module.exports = Utils;
@pflannery would be cool to see what the performance difference is like on your tests.
There’s a few things still do:
Update documentation for all b/c breaks (renamed methods)
Evaluate if maps for configuration and options and state is actually a good idea (it may not be)
Evaluate if we should move the tests over to ES6 too
Test it with browserify and webpack
Update the base files with the new formats from the es6 branch
Figure out a new documentation generator, and one that supports our new configuration and state system
Replace csextends with a new babelextends
Notes:
./es6guardian.js (which is the main file) will try and use the ES6 version, if that fails, it will use the Babel compiled ES5 version instead. For now, it will always fail as V8 does not support the rest arguments which we use heavily.
It doesn’t make sense building a partial harmony build that is applicable only for iojs, as node will be left behind and it is too much pain in the ass for us. Best for us to just wait for V8 to catchup. Reasoning for this is pain for us, and a lot of ES6 is still not optimised, so Babel compiled ES5 is often faster for now.
The conversion to ES6 is not something that can be done automatically. The new features in ES6 require you to think about your code differently and consequently requires you to write it differently (hence all the b/c breaks). Waiting for a CoffeeScript to ES6 compiler is not going to be beneficial.
My testing is outputting a similar memory overhead.
Unfortunately Babel does something similar as the coffee script ctor saga (without the memory leakyness) but this time every class that uses rest parameters in its constructor ends up being called Object in v8 heap snapshots so it’s difficult to find these classes when diagnosing problems…
Luckily there is a solution to point 2 above which is to use
var instance = new (Function.prototype.bind.apply(TaskGroup, [null].concat(args)))
this is what Traceur uses to create classes that have rest parameters in their constructors. So for now I’ve just replaced the ES5 output where the rest class creation occurs.
Now that I can see Task and TaskGroup classes in the heap I will have a dig around and see why there is still so much memory usage.
So the transition of TaskGroup to ES6 is complete (pushed but not yet published).
The CoffeeScript TaskGroup executed 50,000 tasks in 13 seconds, the ES6 TaskGroup did it in 8 seconds, with optimisations it went down to 3.5 seconds. This is in the browser with domains disabled.
Applying the updated TaskGroup to the latest DocPad, the largesite generation of 1000 jade files with a single layout went from 65 seconds with 500mb of memory, to 48 seconds with 350mb of memory. With domains disabled, it goes down to 40 seconds with 300mb of memory.
I’ll do some more reviews of this, and see what else can be done. @pflannery thanks for your efforts on this, keen to hear your thoughts too
I’m tempted but I find its quicker to use babel’s implementation. We’re just have to remember that when viewing the heap its worth swapping the _applyConstructor method over when using rest parameters to create classes to use:
var _applyConstructor = function(Constructor, args) { return new (Function.prototype.bind.apply(SomeClass, [null].concat(args))); }
It’s also worth taking things like this.state = {...} and enclosing it to this.state = new SomeClassState(), that way you can see how many and how much memory is being allocated to this.state, otherwise this.state gets heaped in to Object …once again though it will decrease performance but no one cares when trying to find leaks…
I’d imagine that this shouldn’t be an issue with proper ES6 support anyway, as the only time we use apply/call on a constructor is when we are simulating rest arguments new this(...args) translates to new this.apply(this, args). With proper ES6 support, the former would work without the need for apply/call.