Last active
May 2, 2025 15:13
-
Star
(349)
You must be signed in to star a gist -
Fork
(13)
You must be signed in to fork a gist
-
-
Save danielgtaylor/0b60c2ed1f069f118562 to your computer and use it in GitHub Desktop.
Revisions
-
danielgtaylor revised this gist
May 26, 2015 . 1 changed file with 68 additions and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -82,6 +82,33 @@ Notice the backticks instead of single quotes. It takes some time, so make sure your editor syntax highlights these properly or I guarantee you'll get stuff like `#{name}` printed out by accident. ## Ranges CoffeeScript has a concept of a range object that acts like an array of items from some starting point to some ending point. It's similar to Python's `range()` built-in and is super useful for loops. ES6 doesn't have this, so you'll need to either use the new `Array.from` and `array.keys()` or make a custom `range` function with your own loop logic. ```js let range; # Coffee (inclusive): [0..10] range = Array.from(new Array(11).keys()); # Coffee (exclusive): [0...10] range = Array.from(new Array(10).keys()); # Coffee (non-zero): [2...10] range = Array.from(new Array(10).keys()).slice(2); # Coffee (decrementing): [10...2] range = Array.from(new Array(10).keys()).slice(2).reverse(); # Then use it in a loop: for (let i of range) { console.log(i); } # Coffee: evens = (x for x in [0..10] by 2) let evens = Array.from(new Array(6).keys()).map(i => i * 2); ``` ## Functions Javascript has some new function types! With ES6 you can use both fat arrows and generators. Fat arrows behave like they do in CoffeeScript and keep `this` passed through to the function body. They support both an implicit return shorthand and a long form that has no implied return value. @@ -196,6 +223,23 @@ myAccount.balance = 100; console.log(myAccount.underBudget); // => true ``` ### Super Just like in CoffeeScript, ES6 classes can call on `super` to access the parent's method of the same name. Unlike CS, though, you need to use the method name outside of the constructor. Notice the use of `super.deposit` below: ```js class CachedAccount extends Account { constructor() { super(); this.cacheDirty = false; } deposit(amount) { this.cacheDirty = true; return super.deposit(amount); } } ``` ### Iterable Classes Another neat feature is the ability to make iterable classes, and the ability to use generators for the iterator. @@ -293,7 +337,30 @@ Some interesting caveats: console.log(mymodule.default.no()); ``` I currently don't have a great solution for this nonsense, but if you want to write libraries that are consumed by Node.js users who will likely be using `require`, this is something to keep in mind. As a possible workaround you **can** export default one thing and then assign all the things to it as properties. For example: ```js // myclass.js // ---------- function export1() { return true; } function export2() { return false; } export default class MyClass { hello() { console.log('Hello!'); } } MyClass.export1 = export1; MyClass.export2 = export2; // script-working.js // ----------------- const MyClass = require('./myclass'); console.log(MyClass.export1()); // => 'true' const instance = new MyClass(); instance.hello(); // => 'Hello!' ``` ## Conclusion Hopefully this article helps someone. It's been really fun learning ES6 and getting to play with all of the new toys like Babel and ESLint. -
danielgtaylor revised this gist
May 20, 2015 . 1 changed file with 54 additions and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -220,7 +220,7 @@ for (let item of test) { ``` ## Modules ES6 brings with it a [new module syntax](http://www.2ality.com/2014/09/es6-modules-final.html). It'll take some getting used to, because it includes both a default export and named exports. ```js import _ from 'lodash'; @@ -242,5 +242,58 @@ export default function def() { } ``` Some interesting caveats: 1. If no default is given then you don't just get an object of all the named exports. Instead use `import * as moduleName from 'moduleName';`. Feels weird, right? Particularly for Python users, where `import *` is a four letter word. ```js // mymodule.js // ----------- export function yes() { return true; } // script-broken.js // ---------------- import mymodule from './mymodule'; // This gives an error about `undefined`! console.log(mymodule.yes()); // script-working.js // ----------------- import * as mymodule from './mymodule'; console.log(mymodule.yes()); ``` 2. If there is only one export and it is a default export, then `module.exports` will be set to it. If however there are other exports, you get something that's very strange to consume from normal Node.js `require` statements. ```js // mymodule.js // ----------- export function yes() { return true; } function no() { return false; } export default {yes, no}; // script-working.js // ----------------- import mymodule, {yes} from './mymodule'; console.log(mymodule.no()); console.log(yes()); // script-broken.js // ---------------- const mymodule = require('./mymodule'); // Wat? This is an error. console.log(mymodule.no()); // This works instead. Turns out the module is an object with a 'default' // key that contains the default export. console.log(mymodule.default.no()); ``` I currently don't have a great solution for this nonsense, but if you want to write libraries that are consumed by Node.js users who will likely be using `require`, this is something to keep in mind. As a possible workaround you **can** export default one thing and then assign all the things to it as properties. ## Conclusion Hopefully this article helps someone. It's been really fun learning ES6 and getting to play with all of the new toys like Babel and ESLint. -
danielgtaylor revised this gist
May 20, 2015 . 1 changed file with 55 additions and 24 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -18,24 +18,28 @@ Since support for ES6 is still sketchy at best, you'll probably be transpiling j 2. Some of the more interesting ES6 features require a polyfill/runtime. Symbols, generators, and new types like `WeakMap` for example. Example `package.json` snippet: ```json { ... "scripts": { "lint": "eslint --ext .js,.es6 src", "precompile": "npm run lint", "compile": "babel --optional runtime src --out-dir lib", }, "dependencies": { "babel-runtime": "^5.3.3" }, "devDependencies": { "babel": "^5.3.3", ... } ... } ``` Don't make the mistake of depending on `babel` because that will download **many** more packages than you need. Depend on `babel-runtime`, devDepend on `babel` for the build. By the way, these polyfills may have [strange edge case behavior](https://github.com/zloirock/core-js#caveats-when-using-symbol-polyfill) in some environments. 3. Some features are disabled by default. Specifically, those in [TC39 Stage 0](http://babeljs.io/docs/usage/experimental/) like list comprehensions. This isn't Babel's fault - the specifications are not yet complete. Yes, we had these in CoffeeScript and now have to wait to use them again. @@ -74,12 +78,14 @@ const name = 'World'; console.log(`Hello, ${name}!`); ``` Notice the backticks instead of single quotes. It takes some time, so make sure your editor syntax highlights these properly or I guarantee you'll get stuff like `#{name}` printed out by accident. ## Functions Javascript has some new function types! With ES6 you can use both fat arrows and generators. Fat arrows behave like they do in CoffeeScript and keep `this` passed through to the function body. They support both an implicit return shorthand and a long form that has no implied return value. Splats are also supported, but the ellipsis is on the other side. Default arguments are also supported and work as you'd expect. Same with assignment destructuring. CoffeeScript: ```coffee @@ -88,8 +94,8 @@ square = (value) -> value * value someTask (err, result) => # Handle err and result myFunc = ({source, flag}, args...) -> otherFunc source, args... ``` Javascript: @@ -100,8 +106,8 @@ someTask((err, result) => { // Handle err and result }); function myFunc({source, flag}, ...args) { return otherFunc(source, ...args); } ``` @@ -123,8 +129,10 @@ for (let square of squares()) { } ``` The `function *` syntax tells Babel that this is a generator, unlike in CoffeeScript where the presence of a `yield` statement made it a generator. Note that generators can both yield *and* return values. ## Classes I'm happy to report that classes are actually very similar, and my main gripe with them is something that's being worked on in the spec and also has a usable workaround (only being able to create functions in a class). The following example will show just how similar the syntaxes are, including things like inheritance. CoffeeScript: ```coffee @@ -188,6 +196,29 @@ myAccount.balance = 100; console.log(myAccount.underBudget); // => true ``` ### Iterable Classes Another neat feature is the ability to make iterable classes, and the ability to use generators for the iterator. ```js class MyIterable { constructor(items) { this.items = items; } *[Symbol.iterator]() { for (let item of this.items) { yield `Hello, ${item}`; } } } const test = new MyIterable([1, 2, 3, 4, 5]); for (let item of test) { console.log(item); // => Hello, 1... } ``` ## Modules ES6 brings with it a [new module syntax](http://www.2ality.com/2014/09/es6-modules-final.html). It'll take some getting used to, because it includes both a default export and named exports. The biggest caveat: if not default is given then you don't just get an object of all the named exports. Instead use `import * as moduleName from 'moduleName';`. Feels weird, right? Particularly for Python users, where `import *` is a four letter word. -
danielgtaylor revised this gist
May 20, 2015 . No changes.There are no files selected for viewing
-
danielgtaylor created this gist
May 20, 2015 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,215 @@ # Moving to ES6 from CoffeeScript I fell in love with CoffeeScript a couple of years ago. Javascript has always seemed something of an interesting curiosity to me and I was happy to see the meteoric rise of Node.js, but coming from a background of Python I really preferred a cleaner syntax. In any fast moving community it is inevitable that things will change, and so today we see a big shift toward ES6, the new version of Javascript. It incorporates a handful of the nicer features from CoffeeScript and is usable today through tools like [Babel](https://babeljs.io/). Here are some of my thoughts and issues on moving away from CoffeeScript in favor of ES6. While reading I suggest keeping open a tab to [Babel's learning ES6](http://babeljs.io/docs/learn-es6/) page. The examples there are great. ## Punctuation Holy punctuation, Batman! Say goodbye to your whitespace and hello to parenthesis, curly braces, and semicolons again. Even with the advanced ES6 syntax you'll find yourself writing a lot more punctuation. Accept it now. You'll see it in the examples below. The good news is that with a good linter the code will still be pretty. ## Linting I used to use CoffeeLint. Now I use [ESLint](http://eslint.org/) through [babel-eslint](https://github.com/babel/babel-eslint). Along with the [Airbnb ES6 style guide](https://github.com/airbnb/javascript) it's quite strict. Your best bet is to set your editor up to lint as you type or on save. [Atom's eslint plugin](https://atom.io/packages/linter-eslint) seems to work well and looks for a locally-installed/configured eslint per directory, which should find the `.eslintrc` which you can copy from the Airbnb link above. Sublime has a [similar plugin](https://github.com/roadhump/SublimeLinter-eslint). ## Transpiling Since support for ES6 is still sketchy at best, you'll probably be transpiling just like you did with CoffeeScript. Unlike with CoffeeScript, there are some caveats, however. 1. Some ES6 features are just not available, like Proxies. Maybe some day we'll get the equivalent of Python's `__getattr__` and Ruby's `method_missing`, but that day is not today. Them's the breaks. 2. Some of the more interesting ES6 features require a polyfill/runtime. Symbols, generators, and new types like `WeakMap` for example. Example `package.json` snippet: ```json { ... "scripts": { "lint": "eslint --ext .js,.es6 src", "precompile": "npm run lint", "compile": "babel --optional runtime src --out-dir lib", }, "dependencies": { "babel-runtime": "^5.3.3" } ... } ``` Don't make the mistake of depending on `babel` because that will download **many** more packages than you need. Depend on `babel-runtime`, devDepend on `babel` for the build. By the way, these polyfills may have [strange edge case behavior](https://github.com/zloirock/core-js#caveats-when-using-symbol-polyfill) in some environments. 3. Some features are disabled by default. Specifically, those in [TC39 Stage 0](http://babeljs.io/docs/usage/experimental/) like list comprehensions. This isn't Babel's fault - the specifications are not yet complete. Yes, we had these in CoffeeScript and now have to wait to use them again. ## Let and Const Haven't you heard the news? `var` is out while her buddies `let` and `const` are in. With Javascript you should declare your variables. Default to `const` and if a value needs to change, then use `let`. The transpiler will enforce constants while the linter will complain if you use `var` or nothing. Keep in mind that `const` applies **only** to the value itself. This takes a little getting used to: ```js const name = 'Daniel'; // This is a compile error name = 'Kari'; // --------- const options = {}; const items = []; // This is *not* a compile error options.foo = 'bar'; options.baz = 5; items.push(1); items.push(2); // This *is* a compile error options = {}; items = null; ``` ## String Interpolation Luckly, string interpolation is one area where you just need to retrain your fingers: ```js const name = 'World'; console.log(`Hello, ${name}!`); ``` It takes some time, so make sure your editor syntax highlights these properly or I guarantee you'll get stuff like `#{name}` printed out by accident. ## Functions Javascript has some new function types! With ES6 you can use both fat arrows and generators. Fat arrows behave like they do in CoffeeScript and keep `this` passed through to the function body. They support both an implicit return shorthand and a long form that has no implied return value. Splats are also supported, but the ellipsis is on the other side. Default arguments are also supported and work as you'd expect. CoffeeScript: ```coffee square = (value) -> value * value someTask (err, result) => # Handle err and result myFunc = (args...) -> otherFunc args... ``` Javascript: ```js const square = value => value * value; someTask((err, result) => { // Handle err and result }); function myFunc(...args) { otherFunc(...args); } ``` ### Generators Generators provide a way to iterate through a large or possibly infinite list of items by only processing one at a time. [Read up on them](http://babeljs.io/docs/learn-es6/#generators) if you like. Here's a quick example: ```js // Instead of creating a 10000 item array, we yield each item as // needed. function *squares() { for (let n = 0; n < 10000; n++) { yield n * n; } } for (let square of squares()) { console.log(square); } ``` ## Classes I'm happy to report that classes are actually very similar, and my main gripe with them is something that's being worked on in the spec and also has a usable workaround. CoffeeScript: ```coffee class Account extends Foo @types = ['checking', 'savings'] constructor: (@balance) -> history: (done) -> someLongTask (err, data) -> # Do something with data done null, data deposit: (amount) -> @balance += amount ``` Javascript: ```js class Account extends Foo { constructor(balance) { this.balance = balance; } history(done) { someLongTask((err, data) => { // Do something with data done(null, data); }); } deposit(amount) { this.balance += amount; return this.balance; } } // Currently, you can only declare functions in a class Account.types = ['checking', 'savings']; ``` One cool feature is the ability to define `getters` and `setters`. Unfortunately they cannot be generator functions. ```js class Account { constructor() { this.balance = 0; } get underBudget() { return this.balance >= 0; } get overBudget() { return this.balance < 0; } } const myAccount = Account(); myAccount.balance = 100; console.log(myAccount.underBudget); // => true ``` ## Modules ES6 brings with it a [new module syntax](http://www.2ality.com/2014/09/es6-modules-final.html). It'll take some getting used to, because it includes both a default export and named exports. The biggest caveat: if not default is given then you don't just get an object of all the named exports. Instead use `import * as moduleName from 'moduleName';`. Feels weird, right? Particularly for Python users, where `import *` is a four letter word. ```js import _ from 'lodash'; import {item1, item2} from './mylib'; import * as library from 'library'; export const name = 'Daniel'; export function abc() { return 1; } export class Toaster { // ... } export default function def() { return new Toaster(); } ``` ## Conclusion Hopefully this article helps someone. It's been really fun learning ES6 and getting to play with all of the new toys like Babel and ESLint.