本文翻译自 Nicolas Bevacqua 的书籍 《Practical Modern JavaScript》,这是该书的第一章。翻译时我收获很大,但愿阅读时你也能有所收获。javascript
本章主要讲述了如下内容:css
JS语言的发展简史;html
规范的
stage0
,stage1
,stage2
,stage3
,stage4
各阶段的意义;java
ployfill
是局限性的新规范实现;node
babel
,eslint
的基本使用方法;gitES6的划分;github
如下为正文web
谁也想不到,1995年被当作营销策略推出的JavaScript语言,在2017年成为了最受欢迎平台(web)上的核心语言。JavaScript如今不只能够在浏览器中运行,它还被用做开发桌面和手机应用,用于嵌入式设备,甚至NASA还拿它来设计太空组件。正则表达式
这些年来,JavaScript语言取得的成绩有目共睹,其变化也是日异月殊,咱们不由好奇,JS是如何取得这些成绩的,将来的JavaScript又该向何处发展?算法
1995年,那时候的浏览器还只支持html和简单的css,当时的浏览器巨头--网景公司,不甘于浏览器中只能实现用HTML的静态网站,Brendan Eich 也所以被招进网景,着手开发一种供浏览器使用的相似Scheme
的函数式语言。不过他加入后发现,网景公司高层但愿这门语言看起来像Java(存在暗中进行的交易)。
只花了10天时间 Brendan 就开发完成了当时被称做Mocha
的第一版 JavaScript 原型,这个新语言相似 Scheme
,它把函数当作一等公民,并以原型链为其核心。那时候的JS比较简陋,没有数组和字面量的对象的概念,全部的报错都只能经过丑陋的alert
展现,缺少异常处理机制,出错时不少运算的结果会是NaN
或undefined
。不过 Brendan 对 DOM 0 的描述及第一版的JavaScript仍是成为了最初的标准。
1995年9月,网景公司发布了Netscape Navigator 2.0 beta版,JavaScript也被包装为LiveScript
一同面世。1995年12月,Netscape Navigator 2.0 beta3发布,LiveScript
在这时被更名为JavaScript
(当时这个商标为Sun公司全部,如今属甲骨文公司)。以后不久,网景推出了LiveWire
,一种在其服务器(Netscape Enterprise Server
)上的JavaScript
实现1。
1996年,微软推出了JScript
,同ie3捆绑发行,JScript
在微软的IIS服务器上一样可用。
1996年开始,JS语言开始走上规范之路,它已ECMAScript
的名字被标准化到ECMA-262规范,规范指定者是ECMA组织下的一个名为TC39的技术委员会。因为当时Sun公司不肯意转让JavaScript
这一商标,虽然微软愿意转让JScript
这一商标,但却遭到其它公司成员的反对,所以这一语言的名字就成了咱们熟悉的ECMAScript
。
尽管JavaScript
和JScript
是竞争关系,网易和微软在当时的TC39标准委员会占主导地位,委员会仍是取得了大量的成果,向后兼容被设定为以后标准制定的黄金法则,好比说虽然有了新的严格相等运算符===
,可是JS同时兼容了非严格相等运算符==
。
1997年6月ECMA-262的初版发布,以后一年中,规范依据ISO / IEC 16262国际标准进行了改进,并由ISO认证机构大量审查,1997年6月ECMAScript
规范正式发布第二版。
1999年12月,第三版也发布了,这一版的规范带来了正则表达式,switch
,do/while
,try/catch
,Object#hasOwnProperty
以及其它的一些改变,同时新增的大部分规范在网景的新版浏览器SpiderMonkey中也得以实现。
ES4标准的草案在以后不久就被TC39
提出,这一草案直接影响了2000年中期的JScript
,.NET
等的开发,2006年Flash推出了ActionScript 3
也深受其影响。
可是关于JavaScript
语言该如何发展,当时的意见很是矛盾,这使规范制定的工做停滞不前。这在Web标准指定史上是一个很是尴尬且奇妙的时刻,当时微软掌握着主动权,可是它对规范的改进却没太大的兴趣。
两年后,随着火狐浏览器市场占有率不断增高,就任于 Mozilla 的 Brendan 迫使微软回到标准指定的议程中。2005年中期开始,TC39委员会又开始了例会。从新开始讨论起ES4,他们计划在ES4
中引入模块系统,类,迭代器,生成器,解构,类型注释,适当的尾调用,新的数据类型和各类其余功能,因为这个工程的野心太大,ES4的制定于是被一而再的延期。
2007年,TC39委员会被迫分为两部分,一部分负责ES3的渐进增强版ES3.1
标准的制定,另外一部分则负责从新设计改动巨大的ES4标准。2008年8月,ES3.1被认为是正确的选择,随后其改名为ES5,ES4也随之被废弃,不过值得庆幸的是 ES4 提出的不少新功能被融入到了 ES6 ,也有一些功能仍然在考虑之中,另外一些则已被放弃,拒绝或撤回。兼容ES3.1 成为 ES4 标准提出的功能可能被采纳的前提。
2009年12月,ES3发布10周年后,第五版ECMAScript
才得以发布。这个版本把十年来各浏览器中已有的广泛实践标准化了,新增了get
`set`,改进了数组原型的函数式特征,原生支持了JSON的解析,提出了严格模式。
2011年6月,ES5标准再次修改并改进为 ISO/IEC 16262:2011标准 的第三版,并以ES5.1的名义正式发布。
2015年6月,也就是ES5.1发布的四年后,TC39公布了JS语言有史以来最大的更新 ES6,其中包含了不少ES4中提出草案。本书,咱们将深刻探索ES6。
ES6的发布是JS标准制定历史上的一个重要里程碑。除了数十种引入注目的新功能,ES6 的发布也标志着 ECMAScript 标准将持续更新。
ES3发布了十年都没有重大变化,然后的ES6花了四年才得以标准化,TC39的效率确实须要改进了。以前标准的修订自己是有期限的,可是只要未达成共识,修订时间就会被延长,延长的时间里可能有些功能又有了新的变化,这又致使更多的延迟。
不过随着ES6标准的指定,TC39的工做流程也流线化了。为了知足频繁的需求能持续更新,他们优化了规则的制定过程,为此,新流程的指定从基于Word转移到使用Ecmarkup
(用于格式化ECMAScript规范的HTML超集)和Github request
,这使得提案数量以及非会员参与度都大大提高,新的流程指定过程也更透明了,以往你须要从网上下载某个规范的word或pdf说明文稿,如今你随时能够经过这个网站看见最新的草案。
如今Firefox,Chrome,Edge,Safari和Node.js的最新版都原生实现了 ES6 规范中超过95%的标准了,可是咱们并不须要等到规范百分百的被支持再使用新语法。在描述如何使用以前,咱们先看看规范指定的几个阶段。
Stage0 :任何还没有提交为正式提案的讨论,想法,改变或对已有规范的补充建议都被认为是一个稻草人草案(“strawman” proposal),但只有TC39成员能够提出此阶段的草案。
Stage1 :此阶段,稻草人草案升级为正式化的提案,并将逐步解决多部门关切的问题,如与其余提案的相互之间会有什么影响,这一草案具体该如何实施等问题。人们须要对这些问题提供具体的解决方案。stage1的提案一般还须要包括API描述,拥有说明性使用示例,并对语义和算法进行讨论,通常来讲草案在这一阶段会经历巨大的变化。
Stage2 :此阶段,草案就有了初始的规范。经过polyfill
,开发者能够开始使用这一阶段的草案了,一些浏览器引擎也会逐步对这一阶段的规范的提供原生支持,此外经过使用构建工具也能够编译源代码为现有引擎能够执行的代码,这些方法都使得这一阶段的草案能够开始被使用了。
State3 :此阶段的规范就属于候选推荐规范了,这一阶段以后变化就不会那么大了,要达到这一阶段须要知足如下条件:
规范的编辑和指定的审阅者必须在最终规范上签字;
用户也应该对该提议感兴趣;
提案必须至少被一个浏览器原生支持;
拥有高效的ployfill
,或者被Babel支持;
Stage4 :此阶段的提案必须有两个独立的经过验收测试的实现,进入第4阶段的提案将包含在 ECMAScript 的下一个修订版中。
ES6开始,TC39每一年都会发布新的规范,新的规范将包含年号,如ES6 别名 ES2015,2016年经过的规范将叫 真名叫 ES2016 而非ES7,以此类推。不过对ES6,社区仍是习惯喊其为 ES6,由于其在新命名规则公布以前就以被你们熟悉,ES7有时候也会面临相似的问题,不过以后就会好转了,咱们以后会说ES6,ES2016,ES2017,ES2018等等。
精简提案流程和每一年发布的新版本使得新版标准的发布得以持续化,不过这也意味着版本号再也不那么重要了,这也使得如今你们都把精力放在提案上,对参考ECMAScript标准的期待反而变少了。
上文已经说了,只要JavaScript引擎提供了两个独立的实现,第3阶段的候选推荐方案就很是有可能在下一步中就被归入规范。实际上,Stage3的提案经过能够经过实验阶段的浏览器解析,ployfill
或者转译器解析后,通常被认为是能够在生产环节中安全的使用。Stage2的提案以及更早的提案也被有些开发者运用到生产中,这使得社区对规范有了更多的反馈。
相似Babel这样,以代码做为输入,以浏览器可识别的HTML,CSS,JS的做为输出的工具被称为转译器,或者转译器(转译器的子集),当咱们想在咱们的代码中写还不是正式标准的语法时,Babel 这类转译器能够帮助咱们,它们能够把尚不被支持的语法转换为目前被浏览器普遍支持的语法。
编译通常在开发阶段进行。这使得浏览器更早的‘支持了’新的规范,让JS开发者更早享受到了新规范带来的好处。同时这对规范的做者和实践者也大有裨益,他们所以收集到了更多关于规范可行性、可取性,可能的错误或边缘案例的反馈。
使用转译器转译,是现今在生产环境使用ES6语法的最值得信赖的方案,只须要一个build
步骤,咱们的新代码就能够被老的浏览器成功解析了。
一样咱们也能够经过转译提早使用ES7甚至更新的语法,新的标准每一年的推出,转译器也将会立刻支持ES2017,ES2018等等,与此同时,随着浏览器对新标准的支持度愈来愈高,转译器也会逐步减小对ES6代码的转译,ES7的转译等等。从这个角度看来,转译器起到了链接新旧的做用。
Babel如此重要下面咱们谈谈如何在工做中使用Babel。
Babel能够转译ES6代码为ES5代码,经转译的代码是易读的,这有助于咱们加深咱们对新语法的理解。
在线的Babel REPL (Read-Evaluate-Print Loop) 为咱们学习ES6提供了很好的途径,无须安装Node.js,无须使用 Babel
CLI ,在网页上就能够实现源代码的转译。
在Babel REPL中咱们只须要在左侧输入代码,在右侧就能够看到编译后的代码,转译会自动进行。
在REPL中,咱们在左侧输入如下代码看看会发生什么:
var double = value => value * 2 console.log(double(3)) // <- 6
在右侧咱们能够看到编译为ES5的结果,以下图所示,当你更新左侧代码时,右侧也会实时更新
学习新语法免不了实践,REPL就是咱们的练武场,不过须要注意的是,Babel 不能直接转译新增的Symbol
,Proxy
,WeakMap
等语法,并不是Babel忽略了这些新特性,转译时,若是运行了Babel提供的相关插件,这些语法也能被转译,这意味着咱们须要在代码中引入babel-ployfill
。
为何要这样呢?
须要额外引入ployfill是由于,老版本的JS中,想要正确执行这些新语法是很难的或者几乎是不可完成的。polyfills 虽然能够貌似解决这种问题,可是一般并不能百分百覆盖全部使用场景状况,也就是说 ployfill 实际上只是一种折中处理,所以若是咱们在生成环境中使用这类新的语法,就算经过ployfill转译了,咱们也应该在正式上线前仔细进行测试。
也所以,对于这些没法转译的新内置语法,咱们最好仍是等浏览器彻底支持之后再使用。不过就算暂时不能使用,咱们也仍是应该学习这些新特性,使得本身对JS语言有更深入的理解。
虽然新的Chrome,Firefox,Edge等现代浏览器如今已经支持大部分的 ES2015 甚至更新的语法,当咱们使用某个新语法时,浏览器的开发者工具很是有用。可是若是生产阶段想要使用新语法,咱们仍是推荐你对代码进行转译,这能让你的app的适用性更广。
除了REPL,经过Babel提供的一个node.js包,咱们还能够在命令行中实现转译,下面以一个简单的例子做为示范:
新建目录,打开目录所在位置的终端,并经过npm init
命令实现这个目录的初始化;
mkdir babel-setup cd babel-setup npm init --yes # yes参数使得初始化时采用默认设置
建立一个名为example.js
的文件,在babel-setup目录下新建子目录 src,并把这个文件保存其中,在example.js中添加下述内容:
var double = value => value * 2 console.log(double(3)) // <- 6
打开终端工具,输入如下代码完成Babel的安装:
npm install babel-cli@6 --save-dev npm install babel-preset-env@6 --save-dev
注:经过
npm
指令初始化的node项目,会自动在根目录新建一个名为node_modules的文件夹,以后咱们经过npm scripts
或经过require
语句就能够在项目中引用这些包
--save-dev
参数会将新安装的包添加到咱们的package.json
配置文件中做为开发依赖项,当咱们把咱们的项目复制到新的环境中时,咱们只须要经过npm install
指令完成依赖环境的安装。
@
标记用以指明安装某特定版本的包,使用@6
,npm
将安装babel-cli
,6.x
版中的最新版。这种偏好设置能够很是有效的在将来保护咱们的应用,永远不会安装7.0.0
或者更新的版本,这些新版本可能包含咱们在此时开发时没法预料的重大改变。
接下来,咱们按照下面的方法替换 package.json
文件中的 scripts
,babel-cli
提供的 babel
命令会检测整个src
目录,把他们转换为咱们想要的输出格式,并存储在目录dist
中。
{ "scripts": { "build": "babel src --out-dir dist" } }
如今咱们的 package.json
应该是下面这个样子了
{ "scripts": { "build": "babel src --out-dir dist" }, "devDependencies": { "babel-cli": "^6.24.0", "babel-preset-env": "^1.2.1" } }
任何写做
scripts
对象中的命令均可以经过npm run <name>
来执行,它会暂时修改$PATH
的值,这使得虽然咱们的babel-cli
并未全局安装,可是也能够直接运行
咱们在命令行中输入npm run build
命令,系统会自动新建dist/example.js
文件,此时的输出文件会和咱们的源代码一致,这是由于babel
并不会自动给咱们添加配置文件,咱们须要在根目录建立文件.babelrc
输入下述代码进行配置
{ "presets": ["env"] }
咱们安装的env
预设 , 会在构建时添加一系列的Babel插件,这些插件能够转换不一样的ES6代码为ES5代码,具体到咱们的 example.js
咱们会看到咱们使用的箭头函数被转换为了 ES5中的普通函数,env
预设依照协议,依据浏览器的支持状况转换JS代码,这个预设是能够配置的,咱们能够控制须要兼容到哪一版本的浏览器。默认状况下全部的新语法都会被转译,以让咱们的应用有尽量广的兼容性。
example.js
转译为ES5后,代码以下:
» npm run build » cat dist/example.js "use strict" var double = function double(value) { return value * 2 } console.log(double(3)) // <- 6
下面咱们来讲明如何使用另一个很是有用的工具,eslint
,它能够依据某个基线标准,帮助咱们改善代码质量。
随着应用逐步复杂,咱们的代码量会愈来愈多,这同时会带来不少问题,一些代码多是多余的或再也不那么有用,咱们须要写新的代码,删除一些再也不相关或必须的特性;运用新架构也可能也会须要调整代码位置;可是随着项目的扩大,开发人员也随之增多。
lint工具能够用于识别语法错误,现代的lint工具也经常是可自定义的,经过抽象团队成员的意见为编码样式规则,以合适的配置使用linter工具,能帮助团队构建一种对每一个人适用的一致的编码风格。
除了确保程序能够正常运行,咱们可能还但愿防止throw
语句抛出异常,在生产代码中不容许出现 console.log
和 debugger
等语句。这些均可以经过lint工具来实现。
尽管linter在定义和实行编码风格方面是有效的,可是咱们也应该当心定义规则,若是定义的规则过于严格,开发人员可能会沮丧到影响开发效率,若是过于宽松,则又难以维持代码的一致性。
ESlink是一个具备不少插件的现代linter工具,它支持众多规则,并容许咱们挑选须要的规则去遵照。经过配置能够实如今没有准守这些规则时会在输出中打印出警告语句或者错误。
使用npm
能够完成eslint
的安装:
npm install eslint@3 --save-dev
接下来,咱们对ESlint进行基本配置 , 因为咱们的eslint
并不是全局安装,咱们能够在node_modules/.bin
文件夹下找到对应的命令行工具。执行下面的命令行命令将引导咱们完成对项目的首次配置。在这里,咱们选用standard
以选用流行风格,并配置配置文件为JSON
格式:
./node_modules/.bin/eslint --init ? How would you like to configure ESLint? Use a popular style guide ? Which style guide do you want to follow? Standard ? What format do you want your config file to be in? JSON
除了我的风格,eslint
容许咱们拓展预约义的风格,这些拓展都以node.js的包呈现。在选择standard
风格后,咱们会发现ESlint
添加了一些依赖项到package.json
中,它们是预约义标准风格的所依赖的包,eslint
还在根目录下,建立了一个名为.eslintrc.json
的配置文件,以下所示:
// .eslintrc.json { "extends": "standard", "plugins": [ "standard", "promise" ] }
在命令行中使用命令时还要带上node_modules/bin
这样的路径感受很很差,为了解决这个问题,咱们把lint
命令也添加到package.json
中:
{ "scripts": { "lint": "eslint ." } }
也许你还记得,npm run
会临时把node_modules
目录添加到$PATH
中。如今当我想lint咱们的代码时,输入npm run lint
就能够了,npm会自动找到对应的包。
仍是以example.js
文件为例,这个文件目前存在一些样式问题,咱们看看ESLint
的效果如何:
// 样式不对的expamle.js var goodbye='Goodbye!' function hello(){ return goodbye} if(false){}
当咱们执行lint
脚本,ESlint会列出这个文件中的全部问题,以下:
若是咱们在执行上述命令的时候 添加参数 --fix
,eslint会为咱们修复大部分的问题,咱们修改package.json
以下
{ "scripts": { "lint-fix": "eslint . --fix" } }
当咱们执行 lint --fix
的时候,咱们会发现如今的错误只是说 hello
从未使用过,false
是一个不变的状态,其它的问题都被修复了,修复后的代码以下。
// 修改后的example.js var goodbye = 'Goodbye!' function hello() { return goodbye } if (false) {}
--fix
的参数是咱们解决代码风格问题时的有效帮手,可是也不用担忧它会搅乱咱们的程序,ESlint只会修复该修复的部分。
prettier
是另外一个相似的工具,它会自动规范化咱们的代码,当咱们给定固定的规则,如空格数,使用单引号仍是双引号,尾逗号,以及一行最大长度时,prettier
会实现咱们的代码的自动重写。
读到这里,你已经知道了如何转译现代的JS为浏览器广泛支持的JS,也知道了如何合适的使用lint来规范代码,下面咱们了解一下本书的结构。
ES6改动很是大,这从规范的页数就能够看出,ES5.1 258页,ES6 566页。总的来讲新增的规范能够划分为如下不一样类别:
语法糖
新机制
更好的语义化
更多内置的方法
现存局限方法的非破坏性解决方案
语法糖是ES6全部改变中最重要的一块,class
语法可简洁的构建对象实例,支持使用箭头函数,简写的对象属性方法,解构,剩余值,和拓展,等提供语义良好的编写程序的方法。第二章 [ES6 Essentials and Classes, Symbols, Objects, and Decorators]()将讨论 ES6 的这些新特性。
ES6为咱们提供了几种控制异步代码流的机制:包括可靠的promises
,表征一系列值的iterators
,特殊的iterators
--> generators
。基于这些新概念,ES2017还有了了async/await
语法,让咱们以同步的方式来书写异步代码。在[Iteration and Flow Control]()一节中,咱们将详细叙述各类新机制。
ES6提供了一些新的内置类型来管理set和map,这些新类型不具备仅使用字符串键的限制,这一部分,咱们会在[Practical Considerations]()这一节中进行系统的描述。
Proxy
对象从新定义了咱们经过JavaScript reflection
能够作什么,Proxy对象相似于其它上下文中的代理。能够用以修改与JavaScript对象的任何交互,如定义、删除或访问属性。考虑到代理对象的工做机制,他们不能完全经过ployfill
实现,事实上相关的ployfill
也是存在的,可是因为存在局限性使得他们在某些方面与规范有所不一致。咱们将在[ Managing Property Access with Proxies]()这一节完全理解代理对象。
除此以外,ES6对Number
,Math
,Array
和string
等都进行了更新,提供了新的api。在[Build-in Improvement in ES6]()这一节,咱们将经过大量的示例来实践这些api。
ES6还在语言层面上为咱们提供了模块系统,[JavaScript Modules]()一节里,咱们将讲述JS的模块系统。
考虑到ES6的改动之大,咱们很难用咱们现有的JS知识理解全部的新特性,[ Practical Considerations ]()一节咱们会分析各ES6特性的优势和重要性,以便你对ES6有一个更加系统的理解。
谁也想不到出生如此卑微的JS语言,今天的应用会如此广,它改变巨大,ES6就是这变化过程当中的一大步,固然这确定不是终点,考虑到标准每一年都会更新,学会如何和最新标准保持一致很是重要。
前面咱们已经说明了,ES是一个持续更新的标准,最好的学习新标准的方法是周期性的浏览TC39的建议库,尤为要注意浏览处于stage3阶段的提议,这些极可能就是以后的标准。
另外一些跟上进度的方法是,订阅每周电子邮件,和阅读JavaScript的博客。
在写本书的时,等待已久的Async Functions
提议已经定于在ES2017中发布。同时还有一些处于候选阶段的提议,好比动态import()
,这容许异步的加载本地JS模块,还有一个建议用ES6中的rest
和 spread
语法 对对象属性进行枚举。
尽管本书主要将讨论ES6,咱们也会学习一些重要的候选方案,如以前已经说起的async functions
,dynamic import()
,calls
以及对象的 rest/spread
和一些其它的属性。
A booklet from 1998 explains the intricacies of server-side JavaScript with LiveWire.
You can read the original announcement at the Microsoft website (July, 2000).
Listen to Brendan Eich in the JavaScript Jabber podcast, talking about the origin of JavaScript.
You can read a news report from The Mac Observer, July 2003.
Brendan Eich sent an email to the es-discuss mailing list in 2008 where he summarized the situation, almost 10 years after ES3 had been released.
For the full set of changes made when merging the Web ECMAScript specification upstream, see the WHATWG blog.
Check out the presentation “Post-ES6 Spec Process” from September 2013 that led to the streamlined proposal revisioning process here.
Check out all of the proposals being considered by TC39.
Check out this detailed table reporting ES6 compatibility across browsers.
Take a look at the TC39 proposal process documentation.
You can track strawman proposals.
Note that Standard is just a self-proclamation, and not actually standardized in any official capacity. It doesn’t really matter which style guide you follow as long as you follow it consistently. Consistency helps reduce confusion while reading a project’s codebase. The Airbnb style guide is also fairly popular and it doesn’t omit semicolons by default, unlike Standard.
Check out all of the proposals being considered by TC39.
There are many newsletters, including Pony Foo Weekly and JavaScript Weekly.
Many of the articles on Pony Foo and by Axel Rauschmayer focus on ECMAScript development.
有任何想法(建议,对ES6的见解等等),欢迎在Github 上提出,本翻译Github地址:PracticeModernJavaScript