这个话题应该由设计开发JavaScript的人来写,因为话题范围比较大,不过我不是。但是作为熟悉多种程序语言的开发者,我仍然觉得有不少话要说,就挑选我想过的部分讲好了。

历史:

关于JavaScript的历史,想必大家早就清楚了,这个语言先天设计不良,但是又充满活力,运气又很好。参考:为什么是Javascript

进化:

JavaScript语言进化很快,这跟它的标准改进流程直接相关,后面会说到。估计它是进化改进最快的语言?社区的力量大。

对比:

JavaScript语言一直在吸收各个语言社区创造的良好实践。它自己也有一个非常庞大、有活力的社区。它将是未来很多应用开发语言选型的有力竞争者。如果再加上TypeScript的加强,更是不可小觑。TypeScript对于JavaScript语言来说,是一个非常不错的补充,它重点关注了脚本语言的变量类型问题。

JavaScript ES6:

我分享了一个脑图,在这里:JavaScript ES6

这个脑图,对JavaScript ES6做了系统的总结和介绍,可以当一个索引。 相对于过去的Js语言标准,ES6有着巨大优势。全面修复了过去JavaScript语言的各种设计缺陷,对已有的功能又做了进一步的加强。 总的来说,ES6吸收了其它语言的精华 – 特别是Python,以及社区的成功实践经验,对Js语言标准是一个重要升级,后续的升级反倒更像小修小补。

首先它试图让语言更为严谨,语言的组件“正交化”、”概念统一化“。我们可以看看Unix/Linux,设计里面有一个重要 的特色,就是:“一切皆文件”。通过操纵file接口,几乎可以操纵一切对象,包括内核/文件。这个思想为软件设计带来了重要的优点 – 统一的操作模式,带来灵活的设计方式。

而JavaScript ES6里面,大量引入迭代器概念,把迭代器接口的支持,扩展到了很多语言要素上。比如,字符串/map/set/数组等等。 这意味着,当语言运行的时候,操纵的数据对象可以是抽象的,是随时替换的,非常的灵活,它给出了操作对象数据的 一致模式,编写代码也更为便利,重用容易。

语言的缺陷修复

原始的JavaScript设计缺陷非常的多。这个连作者自己都承认 – “原创的并不优秀,优秀的并非原创。” 归根结底,JavaScript设计的时间太短,欠考虑的地方实在太多,缺乏社区的实践和打磨。《JavaScript语言精粹》就特别说明了 JavaScript语言的优点和糟粕。

所以ES6里面,几乎把原有的糟粕都修复了,当然为了向下兼容,原有的设计仍然保留了下来,这带来了隐患,也带来了更多的语言复杂性。因为为了修复,只好用新的语法,同时保留旧的关键字和设计,维持旧代码的兼容性。

比如var的变量定义,有作用域内的提升问题,但是let声明就没有。最让人头大、捉摸不定的this,这下彻底的钉死了 – 只要你使用了箭头函数。

全局变量的顶层关联问题,也做了修正。原来的js连模块系统都没有设计,导致社区出现各种所谓的”方案“,奇淫技巧用个遍。 这次定义了module的import/export操作规范,算是彻底解决了这个缺陷。

特色功能

生成器和async/await

这次ES6跟Python“抄袭”了很多设计,最重要的便是:“Generator” – 生成器。不过在Python里面,一般生成器只当迭代 接口用,作为”惰性“获取数据的一个模式,只是它同时具备”中途返回“的执行模式和代码调度的能力。但是恰恰因为它具备代码调度能力,在JavaScript中,这个功能得到了充分挖掘。

过去,Node技术栈开发,最容易出现错误的地方就是处理过程的同步操作,就是代码的执行顺序问题。因为Node的Api,几乎都是异步回调接口,想知道结果, 必须在回调函数里面等待。比如一个功能需要按1-2-3-4的步骤处理,这个在非异步的语言中就不是问题,是天然的动作,从上到下处理。 但是Node不同,要么写在回调中层层嵌套 – 弊端较大,要么使用Promise模式,改造成连缀式then调用 – 代码的侵入性较强,或者使用 第三方lib,强制处理过程注册到规定的数据结构,统一顺序处理。这些模式,都有各自的弊端。归根结底,异步不符合人类的思维习惯。

如果处理逻辑牵扯比较多的顺序控制,比如处理链条是:1-2-3-4-5-6-7-8等等,这个就会带来很大的开发困难,代码易错,难以调试和维护。 难怪以前有人认为,Node不适合这种需要复杂逻辑处理的应用场景。参考:Node

但是,ES6版本的JavaScript支持了生成器,以及包装生成器的语法糖:async和await,近乎完美(至少目前看是)的解决了这个代码的执行顺序问题。因为,通过生成器的代码调度行为,可以让代码异步执行,却按同步书写,这符合人的思维习惯。

不过把async/await全归到“生成器”,也不准确,它更像是一个组合模式,同时组合了生成器的执行调度和Promise的异步执行,因为await返回的 都是Promies对象。想下Ajax,Ajax便是多种要素的组合,形成了一套操作模式。

比如:

const asyncProcessFile = async function () {
const file1 = await readFile(‘/demo/file1’);
const file2 = await readFile(‘/demo/file2’);
const file3 = await readFile(‘/demo/file3’);
processFiles(file1, file2, file3);
}

以上代码,await保证了执行的顺序,告诉使用者,要等后面的结果出来,最后一起处理文件的内容。 所以,选用Node技术栈的框架,也应该优先使用ES6版本Js的产品。

解构赋值

解构赋值,其实是根据目标对象的结构,通过适当的简化语法,一次性的解析/读取对象的属性值或内容,这样就不必像其它语言那样,层层递进的 解析结构,再逐个获得结果,赋予变量。

  • 最简单的是数组: let [a, b, c] = [1, 2, 3];

a, b, c 自动获得对应1, 2, 3的数值

  • 对象也可以支持:

let { foo, bar } = { foo: ‘a’, bar: ‘b’ };
foo // ‘a’ bar // ‘b’

深入支持

ES6版本的Js,增加了一些新的概念和组件,增强了语言的内部功能。比如Proxy,Reflect,Symbol等,不过一些功能应该主要是为工具开发者/类库 开发者提供的,普通用户使用的几率不高。

未来扩展

需要注意的是:ES6虽然在2015年就作为标准推出,但是实现总是需要一个过程的。目前实现主要有两个地方:浏览器和Node。Node对ES6的支持比浏览器 要更好。所以开发的时候,尽量选更好的执行环境。

在ES6之后,还有一些提案推出,会进入后续的JavaScript标准中。JavaScript的提案门槛不高,社区内的任何人都可以提交提案 – 很民主,可以请求修改语言标准。不过, 正因为如此,JavaScript的语言特性增加的比较快,相比之下,Python要克制的多,它的原则是,如果类库可以很好的支持,就不要修改语言,增加特性。

目前增加的有关语言特性的提案,目前基本都是对语言已有功能的增强,大幅修改或者全新功能比较少。目前可能是倾向于先吸收其它语言较好的、适合JavaScript的 特性,这在目前还好。但是,不排除因为语言功能流行风潮的出现,来一波新功能大幅增加的操作。但是流行的未必是靠谱的,未必是真正合适、合理的。

因为增加特性支持功能有隐患,就是语言的特性会产生交互作用,结果没准出乎意料,造成意想不到的后果。

而且,语言的特性越多,学习使用就越复杂,给用户带来更多的压力和困扰 – 想想C++。更何况,JavaScript为了兼容性,老的错误设计一样也没废除,照旧支持, 这个相当于多出来一部分“外挂”,麻烦也不小。如此发展下去,JavaScript开发可能变成一项具有相当难度的工作,没准普通水平的JavaScript开发者,前端工作都难以胜任。

你们看看现在JavaScript有多少种循环模式?

所以,一个建议就是,尽量使用语言的优良特性,使用它的优良子集,使用设计出来的优秀部分编写简单可靠的程序,而不是基于坏的设计书写“巧妙”的代码。

专业前端-使用vue框架构建SPA应用
科技要讲人文么?