ECMAScript 简介

ECMAScript 是一种由 ECMA 国际的组织(European Computer Manufacturers Association,欧洲计算机制造商协会),通过 ECMA-262 标准化的脚本程序设计语言。而 JavaScript 是 ECMA-262 标准的实现和扩展。浏览器为 JavaScript 增加了 DOM 和 BOM 功能。

ECMAScript 和 JavaScript 的关系是,前者是后者的规格,后者是前者的一种实现(另外的 ECMAScript 方言还有 Jscript 和 ActionScript)。

ECMA-262 规定了 ECMAScript 语言的几个重要组成部分:

  1. 基本语法
  2. 数据类型
  3. 语句
  4. 关键字
  5. 操作符
  6. 内置对象
image
image

ECMAScript 版本

ECMAScript 3 是一个巨大的成功,在业界得到广泛支持,成为通行标准,奠定了 JavaScript 语言的基本语法,以后的版本完全继承。直到今天,初学者一开始学习 JavaScript,其实就是在学 3.0 版的语法。

ECMAScript 6 从开始制定到最后发布,整整用了 15 年。

版本号 发布时间 内容
ECMAScript 1 1997 年 06 月 首版
ECMAScript 2 1998 年 6 月 格式修正,以使得其形式与 ISO/IEC16262 国际标准一致
ECMAScript 3 1999 年 12 月 正则表达式,新的控制指令,异常处理,错误定义更加明确及其它改变
ECMAScript 4 2007 年 10 月 由于过于激进,各方对于是否通过这个标准,发生了严重分歧,最终没有发布通过。
ECMAScript 5 2009 年 12 月 和第 3 版标准不大,引进了一些夭折的 ECMAScript 4 的一些功能
ECMAscript 5.1 2011 年 6 月 成为 ISO 国际标准(ISO/IEC 16262:2011)。
ECMAScript 2015(ES2015、ES6) 2015 年 6 月 17 日 ECMAScript 的第六版修订,于 2015 年完成标准化。
ECMAScript 2016(ES2016) 2016 年 6 月 在 ES2015 基础上进行小幅修订
ECMAScript 2017(ES2017) 2017 年 6 月
ECMAScript 2018(ES2018) 2018 年 6 月
ECMAScript 2019(ES2019) 2019 年 6 月

ES6 与 ECMAScript 2015 的关系

ES6 泛指 ES 5.1 版以后的 JavaScript 的下一代标准,涵盖了 ES2015、ES2016、ES2017 等等。而 ES2015 则是正式名称,特指该年发布的正式版本的语言标准。

https://github.com/ruanyf/es6tutorial/blob/8240374c621ba0180b381c74c78198bf7107c433/docs/intro.md#es6-%E4%B8%8E-ecmascript-2015-%E7%9A%84%E5%85%B3%E7%B3%BB

TC39

ECMA 的第 39 号技术专家委员会(Technical Committee 39,简称 TC39)负责制订 ECMAScript 标准,成员包括 Microsoft、Mozilla、Google 等大公司。

TC39 是一个讨论 JavaScript 标准规范的技术委员会,已决定从 2019 年开始改变其运营结构。TC39 每年召开六次会议,规模已经发展到每次有 40 到 60 人参会的程度。

这个委员会以前是主席和副主席的运营结构,现在改为由三位联合主席(Aki Braun(PayPal)、Brian Terlson(微软)和 Yulia Startsev(Mozilla))共同负责的架构。他们还在 2019 年 3 月开设了官方网站:

每一项新特性最终要进入到 ECMAScript 规范里,需要经历 5 个阶段,这 5 个阶段如下:

  • Stage 0: Strawperson:只要是 TC39 成员或者贡献者,都可以提交想法
  • Stage 1: Proposal:这个阶段确定一个正式的提案
  • Stage 2: draft:规范的第一个版本,进入此阶段的提案大概率会成为标准
  • Stage 3: Candidate:进一步完善提案细则
  • Stage 4: Finished:表示已准备好将其添加到正式的 ECMAScript 标准

ECMAScript 6 简介

ES6 相关知识点主要以阮一峰文档为主,此篇笔记只是做一些常用功能提炼、补充说明和自己的理解。另外,有些属性早就被浏览器广泛支持,但是直到 ES6,才将其写入了标准。

http://es6.ruanyifeng.com/

ECMAScript 6.0(以下简称 ES6)是 JavaScript 语言的下一代标准,泛指 5.1 版以后的 JavaScript,涵盖了 ES2015、ES2016、ES2017 等等。它的目标,是使得 JavaScript 语言可以用来编写复杂的大型应用程序,成为企业级开发语言。

  • 2015 年 6 月正式发布 ECMAScript 2015(简称 ES2015)
  • 2016 年 6 月,在 ES2015 基础上进行小幅修订的《ECMAScript 2016 标准》(简称 ES2016)如期发布,这个版本可以看作是 ES6.1 版,因为两者的差异非常小(只新增了数组实例的 includes 方法和指数运算符),基本上是同一个标准。
image
image

常用的 ES6 新特性

  • let、const
  • 解构赋值
  • 扩展运算符(展开)
  • rest 参数(收缩)
  • 模版字符串
  • 函数参数的默认值
  • 箭头函数
  • 对象属性的简洁表示法
  • Promise
  • Class
  • ES Module
image
image

ES6 部署

Babel

为了环境的兼容性,你可以使用 babel 来将 ES6 代码转为 ES5 代码。

npm WARN deprecated babel-preset-es2015@6.24.1: � Thanks for using Babel: we recommend using babel-preset-env now: please read babeljs.io/env to update!(已弃用 babel-preset-es2015,现在建议使用 babel-preset-env)

1
2
// npm install babel-preset-es2015 --save-dev
npm install babel-preset-env --save-dev

.babelrc:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{
"presets": [
["env", {
"targets": {
"browsers": ["Android >= 4.0", "ios >= 6"]
},
"include": [],
"useBuiltIns": true
}]
],
"plugins": [
["lodash"],
["transform-object-rest-spread"],
["component", { "libraryName": "mint-ui", "style": true }]
]
}

let 和 const 命令

ES6 提出了两个新的声明变量的命令:letconst,主要是为了规避使用var定义变量时的副作用(变量提升问题、作用域问题等),减少运行时错误,防止在变量声明前就使用这个变量,从而导致意料之外的行为。这样的设计是为了让大家养成良好的编程习惯 —— 变量一定要在声明之后使用,否则就报错。

块级作用域

JavaScript 一直是函数作用域,这就是为什么将整个 JavaScript 文件包装在一个空的立即调用函数表达式(IIFE)中变得很普遍的原因。这样做是为了隔离文件中的所有变量,因此没有可变冲突。

现在,我们有块作用域和两个绑定到块的新变量声明。

ES6 带来了块级作用域,通过letconst定义的变量属于块级作用域。

1
2
3
4
5
6
{
var a = 1;
let b = 2;
}
console.log(a); // 1
console.log(b); // Uncaught ReferenceError: b is not defined

注意:letconst不允许重复声明

1
2
let v = 1;
let v = 2; // Uncaught SyntaxError: Identifier 'v' has already been declared

注意:由letconst命令声明的变量不是顶层对象(window、global)的属性

1
2
let a = 1;
window.a === undefined; // true

let

let 完全可以取代 var,因为两者语义相同,而且 let 没有副作用(不存在变量提升问题,并且属于块级作用域)。==建议不再使用 var 命令,而是使用 let 命令取代。==

1
2
3
4
5
6
7
8
9
'use strict';

if (true) {
let x = 'hello';
}

for (let i = 0; i < 10; i++) {
console.log(i);
}

上面代码如果用 var 替代 let,实际上就声明了两个全局变量,这显然不是本意。变量应该只在其声明的代码块内有效,var 命令做不到这一点。

const

const 用来声明只读的常量。一旦声明,常量的值就不能改变,否则报错。

注意: const 定义的数组和对象可以被修改(不变的是指向的内存地址)

1
2
3
4
const k = { a: 1 };
k.a = 2;
k.b = 3;
console.log(k); // Object {a: 2, b: 3}

最佳实践:

  • 在 let 和 const 之间,建议优先使用 const,尤其是在全局环境,不应该设置变量,只应设置常量。
  • 所有的函数都应该设置为常量。

暂时性死区

1
2
3
4
5
let a = 1;
{
console.log(a); // Uncaught ReferenceError: a is not defined
let a = 2;
}

ES6 声明变量的六种方法

ES5 只有两种声明变量的方法:var 命令和 function 命令。

ES6 添加了 let 和 const 命令、import 命令和 class 命令。所以,ES6 一共有 6 种声明变量的方法。

  1. var
  2. function
  3. let
  4. const
  5. import
  6. class

变量的解构赋值

ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring)。

变量的解构赋值简化了数组和对象中信息的提取。ES6 前,我们一个一个获取对象信息;ES6 后,解构能让我们从对象或者数组里取出数据存为变量

数组的解构赋值

1
2
3
4
5
6
7
8
9

var data = { name: 'dys', age: 1 };

// ES5
var name = data.name;
var age = data.age;

// ES6
const { name, age } = data;

其实,只要某种数据结构具有 Iterator 接口,都可以采用数组形式的解构赋值。

对象的解构赋值

对象的解构与数组有一个重要的不同。数组的元素是按次序排列的,变量的取值由它的位置决定;而对象的属性没有次序,变量必须与属性同名,才能取到正确的值。

对象的解构赋值是下面形式的简写(参见《对象的扩展》一章)。也就是说,对象的解构赋值的内部机制,是先找到同名属性,然后再赋给对应的变量。真正被赋值的是后者,而不是前者。

1
2
let { foo, bar } = { foo: "aaa", bar: "bbb" };
let { foo: foo, bar: bar } = { foo: "aaa", bar: "bbb" };
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// ES5
var people1 = {
name: 'bai',
age: 20,
color: ['red', 'blue']
};
var myName = people1.name;
var myAge = people1.age;
var myColor = people1.color[0];

// ES6
let people2 = {
name: 'ming',
age: 20,
color: ['red', 'blue']
}

let { name, age } = people2;
let [first, second] = people2.color;

解构赋值允许指定默认值

==解构赋值允许指定默认值==。注意,ES6 内部使用严格相等运算符(===),判断一个位置是否有值。所以,只有当一个数组成员严格等于 undefined,默认值才会生效。

1
2
let [x = 1] = [undefined]; x // 1
let [x = 1] = [null]; x // null

==如果解构不成功,变量的值就等于 undefined==。

1
2
3
4
5
6
7
let [x, y = 'b'] = ['a', undefined]; // x='a', y='b'
let [head, ...tail] = [1, 2, 3, 4]; // head=1, tail=[2, 3, 4]

let { foo, bar } = { foo: "aaa", bar: "bbb" }; // foo="aaa", bar="bbb"
let { log, sin, cos } = Math;

let [a, b, c, d, e] = 'hello';

解构赋值用途

  • 交换变量的值
1
2
3
4
let a = 1;
let b = 2;
[b, a] = [a, b];
console.log(a, b); // 2 1
  • 从函数返回多个值,以前需要通过数组索引取值
1
2
3
4
5
6
7
function foo() {
return [1, 2, 3, 4, 5, 6];
}

let a, b, rest;
[a, , , b, ...rest] = foo();
console.log(a, b, rest); // 1 4 [5, 6]
  • 提取 JSON 数据
  • 函数参数的定义
  • 函数参数的默认值
  • 输入模块的指定方法

最佳实践

使用数组成员对变量赋值时,优先使用解构赋值。

1
2
3
4
5
6
7
8
const arr = [1, 2, 3, 4];

// bad
const first = arr[0];
const second = arr[1];

// good
const [first, second] = arr;

函数的参数如果是对象的成员,优先使用解构赋值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// bad
function getFullName(user) {
const firstName = user.firstName;
const lastName = user.lastName;
}

// good
function getFullName(obj) {
const { firstName, lastName } = obj;
}

// best
function getFullName({ firstName, lastName }) {
}

如果函数返回多个值,优先使用对象的解构赋值,而不是数组的解构赋值。这样便于以后添加返回值,以及更改返回值的顺序。

1
2
3
4
5
6
7
8
9
10
11
// bad
function processInput(input) {
return [left, right, top, bottom];
}

// good
function processInput(input) {
return { left, right, top, bottom };
}

const { left, right } = processInput(input);