使用简单的 JavaScript,我们为什么应该抵制 ES6 的一些特性

2,870 阅读8分钟
原文链接: ourjs.com


作为一名专职的JavaScript开发者,我会密切关注有关JS最新动态,不过最近看过ECMAScript 6的一些新的语法后。我认为ES委员会已经偏离正确的轨道,正在将JavaScript引向错误的方向,很可能又在重复ES4的老路。

JavaScript的简单


长久以来,我一直认为当今JavaScript的广泛应用一部分原因是源于她的“简单”。
一定程度上也可以叫作“简陋”,因为她并不能让程序员很“舒服”的写代码。我曾经从事过很长时间的C#和Java的开发,一度认为JavaScript的讲法极其糟糕,跟很多人一样对这种语言非常不屑,甚至觉得她并不能称之为语言,到处都是坑:浏览器兼容,异步回调,继承机制,域,类型转换……
(注*参见  JavaScript就是一种垃圾语言 ;  Javascript诞生记-C和Self语言一夜情的产物 )
但自从Node.JS的横空出世,重新审视这门语言,你会发现这种简陋却可以让解释器很“舒服”地运行代码。在各种评测中,看到JavaScript虚拟机比Java虚拟机快个一两倍,甚至几倍已经不是什么新鲜事了。
这种性能的提升正是源自于她的简单,基于原型的超简面向对象实现方法;天生异步,没有复杂线程间的互锁和互斥;没有不可控的线程数量对CPU和内存的额外消耗,远小于各种主流编程语言的关键字的数量,以及灵活的闭包而形成的多样的代码组织形式,都决定了JavaScript是一种灵活高效的语言。
(注*参见 Web服务性能测试:Node完胜Java )

JavaScript的开放


目前JS被Node及其他平台采用的另一个原因是因为她的开放,目前JS引擎多达4,5种,这种局面也将长期持续下去,这些引擎相互之间在不断竞争,终究会不断提升JS的运行速度。
不光是Node,Java也早已内置了JavaScript的运行环境;最新的QT和Gnome也准备将JavaScript视为首选开发语言;微软在Win8也采用了WinJS技术;SAP最新推出的基于内存的数据库HANA,其中的XSEngine也正是由JS驱动的,与NodeJS的异步单线程不同,由于写法过于灵活,且程序流并不是很容易控制,XSEngine将其改造成同步多线程的形式,这一点其实已经有些违背JS的核心特性,但可以尽可能地保证ERP软件的正确性,降低ERP实施过程中的风险。
JS标准不是由一家公司制定的,也不存在专利问题。相对于使用Oracle的Java,微软的.NET,JavaScript的成本和风险似乎要低的多,这也是这些大公司选择基于JS技术,构建自己的JS平台的原因。

为什么要抵制ES6的部分标准


“简单”是JavaScript的立足之本。作为新的"C语言运行环境",“网页中的汇编语言”,理应追求性能第一,书写舒适第二。一些高级语言特性完全可以通过二次编绎到JavaScript来实现,其实CoffeeScript和TypeScript正是这么做的。这是一个很好的分工,语法语义应尽量保持接近底层,用最简单的方式实现,而不应添加过多的,复杂的,重复的特性添加入JavaScript核心,否则只会走Java越来越臃肿的老路。

我们都知道ECMAScript4,是一个著名的失败的标准。据说最初由Adobe撰写,后被ECMAScript委员会采纳,这其中有多少故事我们不得而知。我曾经也从事过一段时间的AS3的开发,一度被其优美,严谨的语法所迷住,其实当实并没有意识到,这样全新的语法体系对于浏览器来说可能过于复杂了,可能也正因为如此,其并没有在一款浏览器上真正实施过,同样这也可能是导致Flash Player越来越不稳定的原因。
现在翻开ES6的新特性,这些被遗弃的部分似乎又回来了,下面是我不太喜欢的几条(仅代表个人观点):
以下特性,部分截自此幻灯片:ECMAScript 6 需翻墙。
1. 看上去很美的类继承

class MetaLanguage extends Language {
  constructor(x, y, z, version) {
    super(x, y, z);
    this.version = version;
  }
  summary() {
    return version;
  }
}

看这段是不是觉得有点眼熟?为嘛这段跟微软的TypeScript的长得这么像?我们暂不去揣测标准制定者跟微软有何关系,但一下子用了这么多关键字,浏览器知道吗?

这一点已经其实已经颠覆了JavaScript的基础,采用基于类的对象模型(代码风格),代替原型模型实现面向对象;原型系统不预先设定基类,通过克隆/共享原型实现继承,过程简单,而且性能很好,可以看一看同是基于原型的语言Lisaac:

举例来说 Lisaac 产生的代码速度几乎跟C一样快。测试是由 MPEG-2 编码器的 Lisaac 版本得出的,它由一个C语言版本复制而来。测试显示,Lisaac版本比 C 版本慢1.9%,但代码行数少了37%。然而C语言并非面向对象语言,而是一个过程式语言。Lisaac 跟 C++ 版本相比可能更说明问题。
——  摘自 WiKi百科

本人还是比较喜欢原型链式写法,宁愿自己累点也不愿让JS引擎多一丁点负担,而且这样写起来非常自由,通过Function.prototype或Object.create可以组织出来多种继承形式,甚至我可以需要的时侯指定,动态改变对象的基类(prototype),信手捻来,不受拘束;

其次模拟的继承必竟不如extends来得方便,这样可以在一定程度上防止过度面向对象,避免过度设计。本人坚持反对在框架级别滥用面向对象,滥用设计模式,崇尚简约自然的代码组织形式。(注:之前另一篇反模式的文章  他们为什么说面向对象有问题,探讨面向对象的一些缺陷

2. 新的function表达形式

let empty = ->;
let square = (x) -> x * x;

$("#shopping-chart").on('click', (event)=>
  this.customer.purchase(this.chart);
);

[1, 2, 3].map{|x| x * x}; //[1, 4, 9]
照抄Ruby/Python的表达式,似乎还有Lambda的影子。由于JavaScript是非编绎性语言,这样的改动势必将JS引擎复杂化;目前与JS相似的但更简单的脚本语言nginx_lua在性能上已经超越了node.js,照这样下去不断复杂化与膨胀的JS语法体系终将成为性能的更大拖累。作为C和"汇编”级别的JavaScript应该保持本色,保持底层语法简单,这样才能不断优化。这种高级语言的特性完全可以通过TypeScript和CoffeeScript类似方式来实现。

3.再来点Java和Node的模块管理

module DBLayer {
  export function query(s) { ... }
  export function connection(..args) { ... }
}
import DBLayer.*;

module CanvasLib = require('http://../canvas.js');
import CanvasLib.{Triangle, rotate};

我不太清楚在语言核心中设置如此“高级”的模块管理器是否合适,如果前端全部使用这种单个文件同步加载方式,就不知道会给性能带来多大的损失了。

目前的前端的模块加载方式非常成熟,且各种方案均可以通过BUILD工具自动合并,压缩分散的JS文件,尽可能地提高前端性能。

模块管理是非常顶层的层次需求,应该第三方来完成,这样才能满意不同人的需要,一旦采用这种模块管理,你可能就真的没得选了,

结论

我坚信大道至简,JavaScript应该有自己的风格,应该更加自信,应该追求实现最简,而非书写最简。语言的核心特性应该是稳定的,灵活的,最大程度减少语法限制,这样才能组合出自由多样的用法,这样才能有效地控制语言的复杂度,从而确保运行时能获得最佳性能。

我一度以为我只是为数不多反ES6这些特性的程序员,其实有些国外程度员很早就已经提出异议了,参见Thoughts on ECMAScript 6 and new syntax 观其评论,反对这些“Bad new syntax”特性的人者居多。
总而言之,ES6也提出了一些好的部分。但很多地方似乎“借鉴”了一点Java,一点.Net,一点TypeScript,一点CoffeeScript,一点Ruby/Python…… 我不是很明白他们要制定出一个什么东西,我们应该欢迎那些简单,实用的新特性,但标准并不应该让语言变得更复杂,尤其是一个四不像的东西。但幸亏JavaScript是一个公开,开放的,由大家共同维护的平台,这些过于颠覆的新特性可能会再次重蹈ES4的覆辙,再次被遗弃。
原文地址: ourjs.com

本文对你有帮助?欢迎扫码加入前端学习小组微信群: