作者都是各自领域经过审查的专家,并撰写他们有经验的主题. 我们所有的内容都经过同行评审,并由同一领域的Toptal专家验证.
威廉·科茨
验证专家 在工程
16 的经验

拥有15年以上经验的全栈开发人员和企业家, 威廉(理学硕士)喜欢跟上最新的网络技术.

分享

我一直是 CoffeeScript 到现在已经有两年多了. 我发现写CoffeeScript更有效率, 少犯愚蠢的错误, 并且由于支持源映射,调试CoffeeScript代码完全是无痛的.

ES6有什么新功能? CoffeeScript转换的透视图

最近我一直在玩ES6使用 巴别塔总的来说,我是他的粉丝. 在本文中, 我将从CoffeeScript转换的角度编译我在ES6上的发现, 看看我喜欢的东西,看看我还错过了什么.

有句法意义的缩进:有利还是不利?

我对CoffeeScript的语法上重要的缩进总是有点爱恨交织. 当一切顺利时,你会得到“看,妈妈,没有手。!使用这种极简语法的吹嘘权利. 但是当事情出错时,不正确的缩进会导致真正的bug(相信我), 它会发生), 我觉得这些好处不值得.

 如果iWriteCoffeeScript
   如果iAmNotCareful
      badThings = '发生'

通常,这些bug是在代码块中包含一些嵌套语句时产生的, 你不小心缩进了一条语句. 是的,这通常是由眼睛疲劳引起的, 但在我看来,它更有可能发生在CoffeeScript中.

当我开始编写ES6时,我不太确定自己的本能反应会是什么. 事实证明它确实有感觉 真的很不错 再次开始使用花括号. 使用现代编辑器也有帮助, 一般来说,您只需要打开大括号,编辑器就会好心地为您关闭它. 这感觉有点像在经历了CoffeeScript的巫毒魔法后,回到了一个平静、清晰的世界.

if (iWriteES6) {
  if (iWriteNestedStatements) {
     让坏事不太可能发生
  }
}

当然,我坚持要去掉分号. 如果我们不需要它们,我建议扔掉它们. 我觉得它们很丑,而且需要额外的输入.

类的支持

ES6中的类支持非常棒, 如果你不再使用CoffeeScript,你会感到宾至如归. 让我们用一个简单的例子来比较两者的语法:

ES6类

类动物{
  构造函数(numberOfLegs) {
    这.numberOfLegs = numberOfLegs
  }
  toString () {
    返回“我是一个有${这的动物”.numberOfLegs}腿”
  }
}

类猴扩展动物{
  构造函数(香蕉){
    超级(2)
    这.香蕉=香蕉
  }
  toString () {
    让超级String = 超级.toString ()
      .替换(/动物/,'猴子')
    返回${超级String}和${这.香蕉香蕉}”
  }
}

CoffeeScript类

类动物
  constructor: (@numberOfLegs) ->

  toString: ->
    “我是一只长着#{@numberOfLegs}腿的动物”

类猴子扩展动物
   constructor: (@numberOfBananas) ->
     超级(2)

   toString: ->
     超级String = 超级.toString ()
       .替换(/动物/,'猴子')
     “#{超级String}和#{@numberOfLegs}香蕉”

第一印象

你可能会注意到的第一件事是,ES6仍然比CoffeeScript冗长得多. CoffeeScript中一个非常方便的快捷方式是在构造函数中支持实例变量的自动赋值:

constructor: (@numberOfLegs) ->

这相当于ES6中的以下内容:

 构造函数(numberOfLegs) {
    这.numberOfLegs = numberOfLegs
 }

当然,这并不是世界末日, 而且这种更显式的语法更容易阅读. 另一件小事是我们没有 @ 的快捷方式 ,对于Ruby背景的人来说,这总是让人感觉很好,但也不是什么大问题. 总的来说,我们在这里感觉很自在, 我更喜欢用ES6语法来定义方法.

如何超!

这个方法有一个小问题 超级 在ES6中处理. CoffeeScript处理 超级 以Ruby的方式(“请向同名超类方法发送消息”), 但在ES6中,你只能在构造函数中使用“裸”超级. ES6遵循更传统的类java方法 超级 是对超类实例的引用.

我会回来的

在CoffeeScript中,我们可以使用隐式返回来构建像下面这样漂亮的代码:

foo = ->
  如果数学.random() > 0.5
    如果数学.random() > 0.5
      如果数学.random() > 0.5
        “foo”

在这里,当你打电话时,你得到“foo”的机会很小 foo (). ES6没有这些功能,迫使我们返回使用 返回 关键字:

const foo = () => {
  如果(数学.random() > 0.5 ) {
    如果(数学.random() > 0.5 ) {
      如果(数学.random() > 0.5 ) {
        返回“foo”
      }
    }
  }
}  

正如我们在上面的例子中看到的, 这通常是一件好事, 但我仍然发现自己忘记添加它,并且到处都是意想不到的未定义返回! 我遇到过一个例外, 关于箭头函数, 你会得到这样的一个隐式返回:

数组.map( x => x * 10 )

这是一种方便,但可能会有点混乱,因为你需要 返回 如果加上花括号:

数组.map( x => {
  返回x * 10
})

然而,这仍然是有道理的. 原因是,如果您添加了花括号,那是因为您想使用多行, 如果你有多条线,那么清楚你返回的是什么是有意义的.

ES6类语法奖励

我们已经看到,在定义类时,CoffeeScript有一些语法技巧, 但是ES6也有它自己的一些技巧.

getter和setter

ES6通过getter和setter对封装提供了强大的支持, 如下例所示:

类BananaStore {
  构造函数(){
    这._香蕉 = []
  }
  填充(){
    //从后台获取香蕉
  }
  得到香蕉(){
    返回这._香蕉.filter( banana => banana.isRipe)
  }
  集香蕉(香蕉){
    如果香蕉.length > 100) {
      扔哇${香蕉.长度}是很多香蕉!`
    }
    这.香蕉=香蕉
  }
}

多亏了getter,如果我们访问 bananaStore.香蕉 它只会返回成熟的香蕉. 这真的很棒,因为在CoffeeScript中,我们需要通过getter方法来实现 bananaStore.getBananas (). 当我们在JavaScript中习惯直接访问属性时,这感觉一点也不自然. 这也使开发变得混乱,因为我们需要考虑我们应该如何访问每个属性——它是什么 .香蕉 or getBananas ()?

setter同样有用,因为我们可以清理数据集,甚至在设置无效数据时抛出异常, 如上面的玩具示例所示. 这对于任何有Ruby背景的人来说都是非常熟悉的.

我个人不认为这意味着你应该疯狂地使用getter和setter. 如果您可以通过getter和setter封装实例变量(就像上面的例子)来获得一些东西,那么就继续这样做吧. 但是假设你只有一个布尔标志,比如 isEditable. 如果你不会因为直接暴露而失去任何东西 isEditable 属性,我认为这是最好的方法,因为它是最简单的.

静态方法

ES6支持静态方法,这让我们可以做这个顽皮的把戏:

类喷火 {  
  静态新() {
    返回新的这
  }
}

如果你讨厌写作 新的喷火 () 你现在可以写 喷火.新(). 从那以后,纯粹主义者会抱怨 是一个保留词,但经过非常快速的测试,它似乎在节点.Js和现代浏览器都可以. 但是您可能不希望在生产中使用它!

不幸的是,ES6中不支持静态属性, 如果要定义类常量并在静态方法中访问它们,就必须这样做, 这有点俗气:

类喷火 {
  静态imgPath() {
    返回“$ {.ROOT_PATH} / img '
  }
}
喷火.ROOT_PATH = '/foo'

有一个 ES7提议 实现声明性实例和类属性, 这样我们就可以这样做了, 这很好:

类喷火 {
  static ROOT_PATH = '/foo'
  静态imgPath() {
    返回“$ {.ROOT_PATH} / img '
  }
}

这已经可以在CoffeeScript中很优雅地完成:

类喷火
   @ROOT_PATH:“/ foo”

   @imgPath: ->
     @ROOT_PATH

字符串插值

我们终于可以在Javascript中做字符串插值了!

我很高兴在新的一年里${新 Date().getFullYear()}我们可以插入字符串

来自CoffeeScript/Ruby的这种语法感觉有点恶心. 也许是因为我的Ruby背景,反勾号是用来执行系统命令的, 一开始感觉很不对劲. 另外,使用美元符号感觉有点过时——但也许只有我这么想.

我想由于向后兼容性的考虑,它只是不可能实现CoffeeScript风格的字符串插值. “真是#{咒骂}耻辱!”.

箭头功能

箭头函数在CoffeeScripter中被认为是理所当然的. ES6基本上实现了CoffeeScript的粗箭头语法. 所以我们得到了一个简洁的语法,我们得到了词法作用域 ,所以当我们使用ES5时,我们不必像这样跳圈:

Var 那 = 这
doSomethingAsync ().然后(function(res) {
  那.foo (res)
})

在ES6中等效的是:

doSomethingAsync ().then( res => {
  这.foo (res)
})

注意,我省略了一元回调函数周围的圆括号,因为我可以!

类固醇上的对象字面量

ES6中的对象字面量有一些重要的性能增强. 现在他们可以做各种各样的事情!

动态属性名称

直到写这篇文章,我才真正意识到,自从CoffeeScript 1.9.我们现在可以这样做:

dynamicProperty = 'foo'
obj = {"#{dynamicProperty}": 'bar'}

这比以前需要做的事情少了很多痛苦:

dynamicProperty = 'foo'
Obj = {}
obj[dynamicProperty] = 'bar'

ES6有一个替代的语法,我认为它很好,但不是一个巨大的胜利:

让dynamicProperty = 'foo'
让obj = {[dynamicProperty]: 'bar'}

时髦的快捷键

这实际上是从CoffeeScript中提取的,但直到现在我才知道. 无论如何,这是非常有用的:

让foo = 'foo'
Let bar = 'bar'
让obj = {foo, bar}

现在的内容是 obj is {foo: 'foo', bar: 'bar'}. 这非常有用,值得记住.

班级能做的事,我也能做!

对象字面量现在可以做类能做的几乎所有事情,甚至像这样:

让obj = {
  _foo:“foo”,
  获取foo () {
    返回这._foo
  },
  设置foo(str) {
     这._foo = STR
  },
  is喷火 () {
     返回这.喷火 === ' 喷火 '
  }
}

不太确定你为什么想要开始这样做,但是嘿,现在你可以了! 当然,你不能定义静态方法,因为那样做毫无意义.

让你困惑的for循环

ES6 for循环语法将为有经验的CoffeeScripters引入一些肌肉记忆错误, 因为ES6的for-of转换成了CoffeeSCript的for-in, 反之亦然.

ES6

For (let I ([1,2,3]) {
  控制台.日志(我)
}
// 1
// 2
// 3

CoffeeScript

对于I ([1,2,3])
  控制台.日志(我)
# 0
# 1
# 2

我应该切换到ES6吗?

我最近在做一个相当大的 节点.js 项目使用100% CoffeeScript,我仍然非常高兴和超级高效的使用它. 我要说的是,ES6中唯一让我嫉妒的是getter和setter.

而且,在今天的实践中使用ES6仍然有点痛苦. 如果您能够使用最新的节点.Js版本,那么你几乎得到了所有 ES6特性,但对于旧版本和浏览器来说,情况仍然不那么乐观. 是的,您可以使用巴别塔,但这当然意味着将巴别塔集成到您的堆栈中.

话虽如此,我认为ES6在接下来的一两年里会取得很大的进展, 我希望在ES7中看到更伟大的东西.

我对ES6真正感到高兴的是,“普通的老式JavaScript”几乎和CoffeeScript一样友好和强大. 这对我这个 自由职业者 在过去,我对JavaScript项目有点反感,但是在ES6中,一切似乎都变得更闪亮了.

聘请Toptal这方面的专家.
现在雇佣
威廉·科茨

威廉·科茨

验证专家 在工程
16 的经验

葡萄牙里斯本

2015年10月23日成为会员

作者简介

拥有15年以上经验的全栈开发人员和企业家, 威廉(理学硕士)喜欢跟上最新的网络技术.

作者都是各自领域经过审查的专家,并撰写他们有经验的主题. 我们所有的内容都经过同行评审,并由同一领域的Toptal专家验证.

世界级的文章,每周发一次.

输入您的电子邮件,即表示您同意我们的 隐私政策.

世界级的文章,每周发一次.

输入您的电子邮件,即表示您同意我们的 隐私政策.

Toptal开发者

加入总冠军® 社区.