ES6的困境





5.00/5 (2投票s)
ES6的困境
ECMAScript 6 或 ES6 是 JavaScript 的演进,也是它的未来。它是我们一直以来都在等待的 Web 技术创新。它充满了我们一直渴望的强大功能,并最终使我们能够以可扩展且易于维护的方式构建大型 Web 应用程序。它使成千上万来自 Java、C# 或其他“更高级”语言的开发人员能够最终编写 JavaScript,并将他们经验中的所有优点带到一个急需组织和方向的市场。
这是营销宣传,公平地说,其中很多都是真实的。然而,它也对 Web 需要修复什么做出了许多假设。并且,在无法控制其执行环境的情况下,演进像 JavaScript 这样无处不在的语言确实存在一个真正的问题。Web 是一个分布式平台。改变一辆正在行驶中的车辆的轮子比改变一辆可以开回车库、锁好、升级然后开出去的车辆要难得多。
JavaScript 似乎不再够用了
可以说,JavaScript 宽松的架构一直是其他语言程序员感到困惑的根源。特别是类的缺乏和原型继承对许多人来说是不可接受的。它感觉违反直觉,并且与学校中关于计算的内容不符。更糟糕的是,我们有美丽但令人困惑的闭包结构,以及缺乏常量和类型安全。所有这些都意味着 JavaScript 获得了架构糟糕、不值得信任大型代码库的声誉。尽管如此,它还是发展成为现在软件开发中最常用的语言之一——这在很大程度上归功于 Web 的兴起。
ES6 及后续版本的语言旨在消除这些烦恼,使 JavaScript 成为一种更好、更可靠、更高效的语言。
公平地说,这并不新鲜。过去我们有过几种基于 JavaScript 的语言,试图修复这些问题。TypeScript、Dart、CoffeeScript 甚至 Flash 的ActionScript 都试图解决同样的问题。区别在于它们都需要某种形式的转换或容器才能在浏览器中显示。ES6,现在已经定稿,旨在原生支持浏览器,并像 JavaScript 一样运行,通过指向它的脚本元素。ES6 有许多令人印象深刻的功能
- 箭头函数作为匿名函数的简写形式。
- 块级作用域使用 let 而不是 var 使变量作用于块(if、for、while 等)。
- 类用于封装和扩展代码。
- 常量使用 const 关键字。
- 默认参数用于函数,例如 foo(bar = 3, baz = 2)。
- 解构用于将数组或对象的值分配给变量。
- 生成器使用 function* 和 yield 关键字创建迭代器。
- Map,一种字典类型对象,可用于存储键/值对。以及Set作为用于存储数据值列表的集合对象。
- 模块作为组织和加载代码的方式。
- Promise用于异步操作,避免回调地狱。
- 剩余参数代替使用 arguments 来访问函数参数。
- 模板字符串用于构建字符串值,包括多行字符串。
考虑到我们现在对 JavaScript 的使用,这似乎是一个真实的需求。游戏、应用程序开发甚至服务器端开发都可以使用 JavaScript。在每种环境中,我们都有习惯于不同工具和方法的开发人员。过去的 Web 开发的“狂野西部”似乎不利于当今的性能和维护需求。这让我想起了我们从 DHTML 迁移到 DOM 脚本编写的时代。我们需要秩序和可靠性。
问题在于,目前 ES6 离在 Web 上部署还远未成熟。这并非语言的错,而是 Web 本身的性质。我们不能也不应该 dictake人们使用什么来浏览 Web。但是不同浏览器对 ES6 的支持并不令人鼓舞。
更大的问题是,ES6 在 JavaScript 的历史中首次打破了 Web 的设备和浏览器无关的概念。
用新语法破坏 Web
ES6 的问题不在于它对语言做了什么——JavaScript 一直在用新的方法和 API 进行扩展。我们只需要测试当前浏览器或环境是否了解此功能即可安全使用。这被称为渐进增强,意味着我们永远不会提供损坏的体验。
在调用某个方法之前测试其是否存在,您就是安全的。就像在跳入河流之前检查它是否足够深是一个好主意一样。ES6 的问题在于它打破了向后兼容性,因为它引入了许多 JavaScript 的语法更改,而不仅仅是新的方法和对象。
这不应该成为问题,但与用于构建 Web 上“事物”的所有其他语言(有意识地回避网站与应用程序的争论)不同,JavaScript 不容错。
以下 HTML 对浏览器来说不是问题
<p><span>Nesting</p></span>
浏览器内部会修复这些问题,并继续愉快地渲染页面其余部分。
这种容错性是 HTML5 的主要理念之一。HTML5 解析器非常宽容,因为我们知道开发人员会犯错误,而我们的用户不应该为此受苦。一个 XHTML 文档只有一个错误就会导致渲染失败。这还不够好——我们需要一个更健壮的 Web,因为最终用户体验至关重要。这甚至被定义为 HTML 的设计原则,即利益相关者优先级。
在发生冲突时,用户优先于作者,作者优先于实现者,实现者优先于规定者,规定者优先于理论上的纯粹性。换句话说,用户承担的成本或困难应该比作者承担的成本更重要;然后作者承担的成本应比实现者承担的成本更重要;然后实现者承担的成本应比规范作者承担的成本更重要;最后,规范作者承担的成本应比仅仅出于理论原因提出更改的人的成本更重要。当然,最好是一次性改进多个利益相关者的利益。
CSS 解析器对代码也有同样的态度。例如,应用了此 CSS 的文档中的段落将显示为浅橙色。
p {color: microsoftblue;} p {color: peachpuff;} p {colour: powderblue;}
"peachpuff" 是一个有效的 CSS 颜色名称,而 "microsoftblue" 不是。虽然 "powderblue" 也是一种有效的颜色,但语法上正确拼写的 "colour" 在 CSS 中需要是 "color",这就是为什么它没有被应用。本质上,CSS 解析器无法处理的任何代码行都会被跳过。
这种容错性在 JavaScript 中不起作用,这就是为什么它是 Web 堆栈中最脆弱的部分。任何导致 JavaScript 错误的问题都会导致整个脚本无法执行——浏览器中没有容错能力。
当人们讨论 Web 上渐进增强的必要性时,这一点经常被忽略。这并非针对禁用 JavaScript 的最终用户——他们是极少数。它指的是在 JavaScript 执行之前和浏览器最终尝试运行它时可能发生的所有事情。Stuart Langridge 维护着一个有趣的决策树,它告诉你从请求脚本到执行过程中可能发生的各种事情。
ES6 为 JavaScript 的语法引入了许多更改。虽然支持 ES6 的浏览器或预处理器没有问题,但对于不支持 ES6 的浏览器来说,这无异于语法错误。
function showperson(ismember = 0, hasphoto = 0, ...moar) { /* … */ }
这给我们带来了大问题。除非我们将自己限制在少数已支持它的浏览器中,否则无法使用 ES6。Microsoft Edge、Firefox、Chrome 和 iOS Safari 都实现了 ES6 的良好子集。但是,并非所有这些浏览器都是我们的用户所拥有的,而且我们不能假设人们会一直升级。不幸的是,有很多硬件配备了无法升级的操作系统,其中内置了过时的浏览器。
可以对语法支持进行特性检测吗?
解决此问题的一个非常有趣的方法是 Featuretests.io,由 Kyle Simpson 开发。它是一个非常小的 JavaScript 库,允许您测试 ES6 特性,从而仅在浏览器支持时才加载 ES6 脚本。也就是说,在语法层面进行渐进增强。
使用这个库,您可以测试您拥有的浏览器并查看它们支持什么。我现在快速查看我的机器,显示如下:
Firefox 39 (OSX) | Microsoft Edge (Windows 10 VM) | Chrome 43 (OSX) | Safari 8.07 (OSX) |
|
|
|
|
我无意比较浏览器——那是浪费时间,因为它们变化太快了。我想说明的是,在浏览器对 ES6 的支持方面,它们之间存在相当大的差异。这使得特性测试变得尴尬,因为只有测试所有您想使用的功能才是安全的。假设支持其他功能,仅仅测试一个功能是很危险的。
如果您全力以赴,并测试您想使用的每一个特性,您的代码就不会出错。但是,这很容易变成过犹不及。对许多开发人员来说,ES6 的要点不是零散地使用这些功能,而是从一开始就用 ES6 编写整个应用程序。
这没什么新东西。当 HTML5 和 CSS3 是一个热门词汇,我们迫不及待地想使用它时,我们遇到了很多“这个酷东西只在 Safari 中有效”或“您需要使用 Chrome 才能看到这个网站”,因为其中许多功能仍在变化中。这些产品仍然存在于 Web 上,基本未得到维护,浏览器需要包含大量陈旧的代码才能不破坏 Web。我们想要得太多,太快,事后也没有清理。
为了让开发人员能够使用 Modernizr 自动检测所有功能,付出了很多努力。对于旧浏览器的支持,这仍然是一个很好的主意,但在更定义明确的环境中,开发人员发现它是不必要的开销。相反,我们开始设定一个支持基线,只为那些符合要求的浏览器提供脚本功能和高级样式。BBC 的开发人员三年前称之为“cutting the mustard”(挑选中坚力量),他们的基线是这些几项检查。
if ('querySelector' in document && 'localStorage' in window && 'addEventListener' in window) { // bootstrap the javascript application }
也许我们可以为我们的 ES6 工作也考虑一下这一点?定义几个守门员特性进行检查,然后继续进行?
如果您不想依赖浏览器支持,而只想完整地使用 ES6,那么您需要使用某种转换工具。这可能是一种像 TypeScript 这样的语言,或者所谓的转译器(因为它会将 ES6 转换为 JavaScript)。
转译器来救援?
如今 JavaScript 的好处在于它摆脱了浏览器的限制,也可以在服务器端使用。node.js 使用 Chrome 的V8 引擎作为独立的可执行文件,并且现在还可以使用Microsoft 的 Chakra JS 引擎。这使我们能够使用任何 JavaScript 转换工具来获取 ES6 代码并将其转换为可以在任何浏览器中运行的旧版 JavaScript。如果您已经在使用 Grunt 或 Gulp 等任务管理工具,这可能只是部署前的另一个任务。
市面上有几种选择。最著名的转译器是源自 Google 的Traceur 和 Babel,它最初被称为 6to5,但随着 ECMAScript 的演进,即使在我们尝试实现它的过程中,也需要一个更通用的名称。
目前,转译似乎是在实际项目中安全使用 ES6 的方式,而无需担心跨环境的支持差异。它还与习惯于更严格和基于类的语言的开发人员的工作流程很好地结合。但是,仔细检查后,存在一些奇怪的缺点:
- 首先,在大多数情况下,转译根本不进行任何特性检测——ES6 代码被完全转换为 ES5(或在某些情况下,如果您愿意,甚至向下转换为 ES3)。这意味着支持 ES6 的浏览器永远不会获得代码。这使得浏览器中的实现有点多余,而且更糟糕的是,它不允许测试 ES6 在浏览器中实现的性能和有效性。
- 传统上,Web 是“查看源代码”。过去,我们很多人都是通过查看源代码来学习如何为其编码的。我们查看源代码,找出别人使用了什么,然后我们的调试过程也是一样,在看到错误控制台中有东西出错了之后。如今,我们有了开发者工具,功能更强大。但是,如果我们转译代码,我们就永远不会编写执行的代码。我们在浏览器中调试的代码是转译器创建的代码,它针对性能进行了优化,而不是可读性。为了调试我们的代码,我们需要找到一种方法来连接生成的代码和编写的代码。为此,我们使用源映射。这同样适用于由 Sass 或 Less 生成的 CSS。
- 从其他代码生成代码可能会导致生成巨大的文件。我们编写的原始代码紧凑、干净且结构良好,这不一定重要,当最终结果意味着用户必须下载数兆字节的 JavaScript 来实现一个可以更轻量级的界面时。
性能如何?
每当我们向浏览器引入新功能时,都会出现一个问题:我们是让事情变得更快,还是拖慢速度?在收集足够的数据来找出优化点之前,我们无法知道。在 ES6 的情况下,如果我们转译代码,则无法真正收集这些数据。目前,ES6 在浏览器中的原生性能看起来并不太令人鼓舞,正如 Kevin Decker 的速度报告所示。
此报告显示了 ES6 功能相对于 ES5 基线每秒操作的性能。非深绿色部分比 JavaScript 慢。非深绿色部分要么比 ES5 性能慢,要么与 ES5 性能相同。这意味着只有展开运算符、Promises、Maps 和Sets 带来了性能优势。
ES6 很重要,需要我们的关注
目前,ES6 在 Web 开发方面处于一个奇怪的境地。它是一个标准,得到了很好的支持(比 Web Components 等要好),但它也破坏了向后兼容性。我不想歌颂它所做的所有伟大事迹并展示可能对您有效也可能无效的示例,而是想邀请您参与有关 ES6 的讨论。因此,我想在这里提出几个问题供您思考,并且我们非常希望在评论中听到您的意见。
如果我们的主要目的是通过编码来创建 Web 内容,我们需要问自己几个问题:
- 我们是否让开发人员的便利性压倒了最终用户体验?是提供更多、更快地交付比为特定环境交付最佳性能的解决方案更重要吗?
- 当我们更容易、更快、更易于维护时,构建需要更多用户环境支持的大型应用程序是否可以接受?
- 更容易更改和以更定义明确的方式扩展应用程序是否可以弥补排除用户?
- 现在是时候划清界限,让 JavaScript 更安全、更易于扩展了吗?
- Web 持续向后兼容的概念是否存在缺陷?我们是否因为迎合最低公分母而阻碍了自己?
- ES6 的新功能真的是一种好处,还是我们只是在追赶并重复其他环境所做的事情?鉴于整个软件可能都出了问题,这真的是我们所能做的最好的吗?
- 这些变更的好处是否值得付出努力来使用它们?新进入 Web 的开发人员是否必须使用转译器、预处理器和任务运行器才能入门?
- 当转译器创建 ES5 代码时,我们如何帮助浏览器更好地支持 ES6?
- 使用 TypeScript 这样的 JavaScript 子集是更好的选择吗?
- 将我们编写的代码与浏览器获取的代码进行抽象化是正确的方向吗?查看源代码是否已经过时了?
- ES6 只是另一种在 Web 上工作的方式——就像使用 emscripten 将字节码转换为 JavaScript 或 Flash 一样?预编译步骤可能对 Web 开发人员来说很奇怪,但对于来自 Java 的人来说完全没问题。换句话说,ES6 并非适合所有人,但我们试图让它成为这样吗?
接下来的几个月将是 ES6 激动人心的时期,它的本质意味着将会有持续的讨论和演讲。它非常吸引那些不喜欢 JavaScript 的灵活性和随机性的人。它让那些坚持使用 JavaScript 的人感到困惑,并觉得这增加了许多开销,而没有立即的好处。进化总是伴随着成长的烦恼。是时候发声并尝试适合您的方法了。
更多关于 JavaScript 的实践
本文是 Microsoft 技术传道者关于实用 JavaScript 学习、开源项目和互操作性最佳实践的 Web 开发系列文章的一部分,包括Microsoft Edge 浏览器和新的EdgeHTML 渲染引擎。
我们鼓励您在包括 Microsoft Edge(Windows 10 的默认浏览器)在内的各种浏览器和设备上进行测试,并使用 dev.modern.IE 上的免费工具。
- 扫描您的网站是否存在过时库、布局问题和可访问性问题
- 将虚拟机用于 Mac、Linux 和 Windows
- 在您自己的设备上远程测试 Microsoft Edge
- GitHub 上的编程实验室:跨浏览器测试和最佳实践
来自我们工程师和布道者的 Microsoft Edge 和 Web 平台深度技术学习
- Microsoft Edge Web Summit 2015(关于新浏览器、新支持的 Web 平台标准以及来自 JavaScript 社区的特邀演讲嘉宾的预期)
- 哇,我可以在 Mac 和 Linux 上测试 Edge 和 IE!(来自 Rey Bango)
- 在不破坏 Web 的情况下推进 JavaScript(来自 Christian Heilmann)
- 让 Web 正常工作的 Edge 渲染引擎(来自 Jacob Rossi)
- 利用 WebGL 和 Microsoft Edge 释放 3D 渲染(来自 David Catuhe,包括vorlon.JS 和babylonJS 项目)
- 托管 Web 应用和 Web 平台创新(来自 Kevin Hill 和 Kiril Seksenov,包括manifold.JS 项目)
更多免费的跨平台工具和网络平台资源