从coffeeScript迁移到ES6

Posted Node全栈

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了从coffeeScript迁移到ES6相关的知识,希望对你有一定的参考价值。

作为一名重度coffeeScript患者,面对coffeeScript未来可能终将会被ES6所取代这个高兴不又悲伤的事实,总怀着复杂的心情。过与其缅怀过去不如着眼未来,今日正好看见 @i5ting 哥所发的 帖子,着实是一篇很不错的coffeeScript迁移到ES6的指南,所以在此翻译( 原文)一下,与众热爱coffeeScript的朋友分享。


从coffeeScript迁移到ES6


我在多年前爱上了coffeScript。对于javascript,我一直保持着深沉的爱,也十分高兴得看到node.js的快速发展,但是作为一个有python背景的程序员,我更喜欢coffeeScript的简练语法。 在任何一个活跃的社区中,事物的迭代更新都是必然的,现在,我们看见了javaScript向着ES6标准的巨大进步。ES6包含了相比于CoffeeScript更多好的特性,并且通过如Babel这样的工具,我们已经可以开始着手使用他们。以下是从coffeeScript迁移到ES6的一些注意点和我的想法。


符号


放弃空格(whitespace),重新转而使用圆括号,花括号和分号。请接受在使用ES6时,你会敲上更多的符号。不过在规范的代码格式下,它们看上去还是挺整洁的。


语法校验


过去我使用的是CoffeeLint,现在我通过babel-eslint来使用ESLint。遵循Airbnb ES6 style guide这个语法风格指南。最好将你的编辑器设置成在输入或保存时进行语法检查。Atom’s eslint plugin这个语法校验插件非常不错,你可以将上面的Airbnb ES6 style guide链接中的内容,放入你的.eslintrc配置文件中。SublimeText也有类似的插件。


代码转换(Transpiling)


由于现在离对ES6的完美支持还很远,所以最好还是使用代码转换器(Transpiling),如Babel,就像在用CoffeeScript时一样。不过和CoffeeScript不同的是,这里有一些值得说明的:


1,并不是所有的ES6特性全部都可用,诸如Proxies。

2,另有一些ES6特性需要使用polyfill/runtime才可用,如Symbols,generator函数,WeakMap。一段package.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", ... } ...}


请不要将babel放入dependencies中,这样会下载许多你并不需要的package,请将babel-runtime写在dependencies中,将babel写在devDependencies中。


3,一些CoffeeScript即有的特性如数组推导(list comprehensions)在ES6中也是不可用的,因为这些特性的规范还未被完全完善。


Let和Const


var已经过时了,现在流行的是它的好基友let和const。今后在javaScript中,如果要声明不可变的变量,请使用const,反之,请使用let。 语法校验会给出警告当你仍在使用var或不通过任何关键字声明变量时。



const name = 'Daniel';// This is a compile errorname = 'Kari';// ---------const options = {};const items = [];// This is *not* a compile erroroptions.foo = 'bar';options.baz = 5;items.push(1);items.push(2);// This *is* a compile erroroptions = {};items = null;


字符串替换


幸运的是,CoffeScript和ES6关于字符串替换方面区别很小,你要做的只是改变一下你的手指肌肉记忆:


const name = 'World';console.log(`Hello, ${name}!`);


注意反引号代替了双引号。


请确保你的编辑器能正确的高亮这些新语法,我敢保证在一开始你任然会时不时敲出#{name}。。


函数


ES6中,有了一些新的函数类型,如generator函数和胖箭头(=>)函数。胖箭头函数在ES6和CoffeeScript中表现一致,如绑定函数中的上下文(this)为它被定义时的上下文。


函数的变参也同样被支持,但是与coffeeScript语法不同的是,ES6中省略号在另外一侧。参数默认值和解构赋值也同样被支持。


coffeeScript:


square = (value) -> value * value someTask (err, result) => # Handle err and resultmyFunc = ({source, flag}, args...) -> otherFunc source, args...


javaScript:


const square = value => value * value;someTask((err, result) => { // Handle err and result});function myFunc({source, flag}, ...args) { return otherFunc(source, ...args);}


generator函数:


generator函数提供了一种迭代一系列超长任务的便捷方式,例子:


// 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);}


通过function*语法来声明一个generator函数。这与CoffeScript中只要函数体内包含yield关键字,本函数就是generator函数不同。


generator函数同样也可以yield和返回值。


类(Classes)


两者关于类的语法非常的相似,不过在ES6中,只可以在class中声明函数。下面的例子说明了两者语法的接近,包括继承:

coffeeScript:


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:


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 classAccount.types = ['checking', 'savings'];


一个不错的特性是类有了定义getter和setter的能力,不过它们不能是generator函数:


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


可遍历类(Iterable Classes)


另一个灵活的特性就是可以创建可遍历类,并且可以将generator函数用于遍历器。


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...}


模块


ES6提供了一个新的模块语法,这也需要花一定时间适应,因为它同时提供了匿名导出和普通导出:


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() { return new Toaster();}


几个值得注意的点: 1,如果不使用匿名导出,你不能直接通过import moduleName from 'moduleName';来获取所有的导出对象,而是要使用import * as moduleName from 'moduleName';:


// 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,如果脚本中仅仅只有一个匿名导出,那么在使用Node.js的require命令引入时,这个匿名导出的对象表现得像被传递给了module.exports一样。但是如果脚本中还有其他的普通导出,就会得到非常奇怪的结果:


// 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());


这个坑爹的情况目前还没有任何好的解决方案。所以如果你正在写一个库并且准备让Node.js使用者使用require命令对其进行导入,最好只使用一次匿名导出,并且把一切的一切都绑定在这个匿名导出的对象的属性上。


结语


希望本文可以帮助到一些准备从coffeeScript迁移到ES6的人,我本人也在学习ES6的过程中感受到十足的乐趣,并且我对我的新玩具ESLint和Babel实在是爱不释手


https://iojs.org/en/es6.html

http://kangax.github.io/compat-table/es6/



全文完



以上是关于从coffeeScript迁移到ES6的主要内容,如果未能解决你的问题,请参考以下文章

在编译字符串插值实例时,Coffeescript是否始终使用ES6模板文字?

在浏览器和测试运行器中将目标从 es5 迁移到 es6

通过 CoffeeScript 和 Browserify 使用 ES6 导入

用ES6代替CoffeeScript

使用 ES6 模块和 CoffeeScript 的首选方式

将 Node.js 项目从普通 ES6 迁移到 TypeScript