[译] 从 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的例子:

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已过期了,如今盛行的是它的好基友letconst。以后在javaScript中,假如要声明不可变的变量,请运用const,反之,请运用let
语法校验会给出正告当你仍在运用var或不经由过程任何关键字声明变量时。

有一个值得注重的点是,const仅仅是指向变量地点的地点,这能够须要花一点时候去顺应:

jsconst 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;

字符串替代

荣幸的是,CoffeScript和ES6关于字符串替代方面区分很小,你要做的只是转变一下你的手指肌肉影象:

jsconst name = 'World';

console.log(`Hello, ${name}!`);

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

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

函数

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

函数的变参也一样被支撑,然则与coffeeScript语法差别的是,ES6中省略号在别的一侧。参数默认值和解构赋值也一样被支撑。

coffeeScript:

Coffeesquare = (value) -> value * value

someTask (err, result) =>
  # Handle err and result

myFunc = ({source, flag}, args...) ->
  otherFunc source, args...

javaScript:

jsconst square = value => value * value;

someTask((err, result) => {
  // Handle err and result
});

function myFunc({source, flag}, ...args) {
  return otherFunc(source, ...args);
}

generator函数:

generator函数供应了一种迭代一系列超长使命的便利体式格局,例子:

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

经由过程function*语法来声明一个generator函数。这与CoffeScript中只需函数体内包含yield关键字,本函数就是generator函数差别。generator函数一样也能够yield和返回值。

类(Classes)

二者关于类的语法异常的类似,不过在ES6中,只能够在class中声明函数。下面的例子申清楚明了二者语法的靠近,包含继续:

coffeeScript:

Coffeeclass 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:

jsclass 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'];

一个不错的特征是类有了定义gettersetter的才能,不过它们不能是generator函数:

jsclass 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函数用于遍历器。

jsclass 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供应了一个新的模块语法,这也须要花肯定时候顺应,由于它同时供应了匿名导出和一般导出:

jsimport _ 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';:

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,假如剧本中仅仅只要一个匿名导出,那末在运用Node.js的require敕令引入时,这个匿名导出的对象表现得像被传递给了module.exports一样。然则假如剧本中另有其他的一般导出,就会获得异常新鲜的效果:

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

这个坑爹的状况现在还没有任何好的解决方案。所以假如你正在写一个库而且预备让Node.js运用者运用require敕令对其举行导入,最好只运用一次匿名导出,而且把一切的一切都绑定在这个匿名导出的对象的属性上。

结语

愿望本文能够协助到一些预备从coffeeScript迁移到ES6的人,我本人也在进修ES6的过程当中感受到实足的兴趣,而且我对我的新玩具ESLint和Babel实在是爱不释手。。

原文链接

    原文作者:菜菜蔡伟
    原文地址: https://segmentfault.com/a/1190000002790283
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞