# ECMAScript 6
# 简介
ECMAScript 6.0
(简称 ES6
)是 JavaScript
语言的下一代标准,于 2015 年 6 月发布。
# ECMAScript 和 JavaScript 的关系
ECMAScript
是 JavaScript
语言的标准规范。我们用 JavaScript
编写程序,然后遵循 ECMAScript
的规范。
ECMAScript
和 JavaScript
的关系是,前者是后者的规格,后者是前者的一种实现。
- 具体使用哪一版
ES
规范,主要看运行的浏览器是否支持。
ES6
是一个历史名词,也是一个泛指,含义是 5.1
版以后的 JavaScript
的下一代标准,涵盖了 ES2015
、 ES2016
、 ES2017
等等。除特殊说明外,本文所指的 ES6
主要指 ES2015
的标准。
# 部署进度
使用任一标准的 ECMAScript 都需要看所运行的环境是否支持。
使用浏览器,可访问 ES-Checker,得知当前所用浏览器对 ECMAScript 6 的支持程度及具体细则。
使用 NodeJS,一般支持程度都比浏览器高,想知道具体细则可通过安装 ES-Checker 模块检查。
$ npm install -g es-checker
$ es-checker
# let 和 const 命令
# let
ES 6
新增了 let
命令,用来声明变量。用法类似 var
,但是所声明的变量,只在 let
命令所在的代码块内有效。
ES5
时期, js
只有全局作用域和函数作用域。而 let
的出现,赋予了 js
块级作用域的概念。简单的说, let
声明的变量跟其它编程语言,如: C
、 C++
等类似,需要考虑作用域。
案例展示
let p = '第一层'; | |
if(true){ | |
let p = '第二层'; | |
console.log(p); | |
if(true){ | |
let p = '第三层'; | |
console.log(p); | |
} | |
} | |
console.log(p); | |
/* 输出结果 | |
第二层 | |
第三层 | |
第一层 | |
*/ | |
for(let i = 0; i < 3; i ++){ | |
console.log(i); | |
} | |
console.log(i); | |
/* 输出结果 | |
0 | |
1 | |
2 | |
ReferenceError: i is not defined | |
*/ | |
for(var i = 0; i < 3; i ++){ | |
console.log(i); | |
} | |
console.log(i); | |
/* 输出结果 | |
0 | |
1 | |
2 | |
3 | |
*/ |
# const
和 let
一样,同样拥有作用域,使用方法也与 var` 类似,不同的是,声明时必须初始化,且一旦声明,就不允许修改值。
案例展示
const p = '我是一个const'; | |
console.log(p); | |
p = '试图修改const'; | |
/* 输出结果 | |
我是一个 const | |
TypeError: Assignment to constant variable. | |
*/ |
# 解构赋值
# 基本用法
ES 6
允许按一定模式,同时对多个元素进行赋值。
简单的说,解构就是取右边位置的值赋给左边相应位置的变量。只要等号两边的模式相同,左边的变量就会被赋予对应的值。
案例展示
/* ES 6 之前的写法 */ | |
var a = 1; | |
var b = 2; | |
var c = 3; | |
/* ES 6 支持的写法 */ | |
var [a, b, c] = [1, 2, 3]; | |
// 以上两种方式结果一样,都是 a = 1, b = 2, c = 3 | |
/* 嵌套的解构 */ | |
let [a, [b, [c]]] = [1, [2, [3]]]; | |
// 结果依然是 a = 1, b = 2, c = 3 | |
/* 其它情况 */ | |
let [a, , c] = [1, 2, 3]; | |
// a = 1, c = 3 | |
let [a, b, c] = [1, , 3]; | |
// a = 1, b = undefined, c = 3 | |
let [a, b, c] = [1, [2, 3], 4] | |
// a = 1, b = 2, c = 4 | |
let [a, ...b] = [1, 2, 3, 4]; | |
//... 会把剩下的值都赋给其后面的变量 | |
// a = 1, b = [2, 3, 4] | |
let [a, b, ...c] = [1]; | |
// a = 1, b = undefined, c = [] |
注意:
...
修饰的变量必须为最后一个变量,否则将报错- 解析不成功的变量值为
undefined
# 默认值
解构赋值可以设置默认值。
当变量的值为 undefined
且其有设置默认值时,变量值为默认值。
ES 6
内部使用严格相等运算符 ===
进行判断,只有当变量值严格等于 undefined
时,默认值才会生效。
案例展示
let [isPrime = true] = []; | |
// isPrime = true | |
let [a, b = 7] = [1, 2]; | |
// a = 1, b = 2 | |
let [a, b = 7] = [1]; | |
// a = 1, b = 7 | |
let [a, b = 7] = [1, undefined]; | |
// a = 1, b = 7 | |
let [a, b = 7] = [1, null]; | |
// a = 1, b = null | |
/* 默认值也可以为其它变量,但这个变量必须为已经声明的变量 */ | |
let [a = 1, b = a] = []; | |
// a = 1, b = 1 | |
let [a = 1, b = a] = [3]; | |
// a = 3, b =3 | |
let [a, b = a] = [3]; | |
// a = 3, b = 3 | |
let [a = b, b = 2] = []; | |
// ReferenceError | |
let [a = b, b = 2] = [1]; | |
//a = 1, b = 2 没报错,因为不需要用到默认值 | |
/* 特殊用法 */ | |
let [a, b] = [1, 2]; | |
// a =1, b = 2 | |
[a, b] = [b, a]; | |
// a = 2, b = 1; | |
// 结构赋值可以用来方便地交换元素的值 |
# 对象的解构赋值
解构赋值可以运用到所有 iterable
结构的数据。
也就是说可以用于对象,用于对象的解构赋值是根据对象的属性的属性名进行匹配的。
案例展示
let {a, b} = {a: 1, b: 2}; | |
// a = 1, b = 2; | |
let {a, b} = {e: 1, r: 4, b: 2, a: 4}; | |
// a = 4, b = 2 | |
let {a, b} = {aa: 2, b: 3}; | |
// a = undefined, b = 3 | |
/* 如果变量名与属性名偏要不一致,则要写为以下形式 */ | |
let {one: a, two: b} = {one: 1, two: 2}; | |
// a = 1, b = 2 |
更多用法可查看 ES 6 文档
# 字符串的扩展
# 判断字符串
ES 6
提供了三种新方法,用来确定一个字符串是否包含在另一个字符串中,结果返回一个布尔值。
includes()
:表示是否找到了参数字符串。startsWith()
:表示参数字符串是否在源字符串的头部。endsWith()
:表示参数字符串是否在源字符串的尾部。
除了 子字符串
参数外,以上三个函数还支持第二个参数 n
,其中 includes()
、 startsWith()
带 n
表示从待操作字符串的第 n
个到字符串结束中寻找目标字符串;而 endsWith()
带 n
表示从待操作字符串的前 n
个 (不包含 n
) 中寻找目标字符串。
注意: n
表示字符串的索引,由 0
开始。
案例展示
let s = "Hello World!"; | |
console.log(s.includes('Wo')); | |
console.log(s.startsWith('Hell')); | |
console.log(s.startsWith('ello')); | |
console.log(s.endsWith('d!')); | |
console.log(s.endsWith('d')); | |
/* 输出结果 | |
true | |
true | |
false | |
true | |
false | |
*/ | |
console.log(s.includes('e', 2)); // 找子,在 'llo World!' 里找 'e' | |
console.log(s.startsWith('ello', 1)); // 找头,在 'ello World!' 里找 'ello' | |
console.log(s.endsWith('or', 9)); // 找尾,在 'Hello Wor' 里找 'or' | |
/* 输出结果 | |
false | |
true | |
true | |
*/ |
# 重复字符串
repeat(n)
:方法返回一个新字符串,表示将原字符串重复n
次。
注意: n
的取值范围为 (-1, +∞)
中的确切数字。如果 n
为小数,程序会进行向下取整;如果 n
为 (-1, 0)
的小数,程序会取 0
;如果 n
为 NaN
,效果等同于 0
;如果 n
为 Infinity
和其它负数,程序将报错。除了正常的数字外, n
还可以是字符串,程序会将其解析为数字,只有数字字符串能正确解析,其它都会解析为 0
。
案例展示
let s = 'ha'; | |
console.log(s.repeat(8)); | |
console.log(s.repeat(0)); | |
console.log(s.repeat(NaN)); | |
console.log(s.repeat(3.8)); | |
console.log(s.repeat(-0.5)); | |
/* 输出结果 | |
hahahahahahahaha | |
hahaha | |
*/ |
# 补全字符串
ES 2017
推出了字符串补全长度的功能。将不够指定长度的字符串用指定字符串补全到指定长度。
padStart()
:在头部进行补全。padEnd()
:在尾部进行补全。
案例展示
let s = "e"; | |
console.log(s.padStart(9, 'u')); | |
console.log(s.padEnd(9, 'm')); | |
console.log(s.padEnd(0, 'm')); | |
/* 输出结果 | |
uuuuuuuue | |
emmmmmmmm | |
e | |
*/ |
# 模板字符串
ES 6
引入了便捷的、功能强大模板字符串的写法,代替了传统繁杂的字符串。
模板字符串使用一对反引号 (``)
包围。用 ${}
的形式插入变量或函数。
基本形式为: 部分字符串${变量名或函数名}部分字符串
案例展示
let poet = '静夜诗'; | |
let name = '李白'; | |
function f(){ | |
return '举头望明月,低头思故乡。'; | |
} | |
let s = `\t\t${poet} | |
\t\t ${name} | |
床前明月光,疑是地上霜。 | |
${f()}`; | |
console.log(s); | |
/* 输出结果 | |
静夜诗 | |
李白 | |
床前明月光,疑是地上霜。 | |
举头望明月,低头思故乡。 | |
*/ |
# 数组的扩展
# Array.from()
Array.from()
:用于将类似数组的对象或可遍历的对象转换为真正的数组。
案例展示
let obj = { | |
0: 1, | |
1: 2, | |
2: 3, | |
length: 3 | |
} | |
let arr = Array.from(obj); | |
console.log(arr); | |
/* 输出结果 | |
[1, 2, 3] | |
*/ |
# Array.of()
Array.of()
:用于将一组值,转换为数组。
案例展示
let arr1 = Array.of(1, 2, 3); | |
let arr2 = Array.of(); | |
console.log(arr1); | |
console.log(arr2); | |
/* 输出结果 | |
[1, 2, 3] | |
[] | |
*/ |
# copyWithin()
copyWithin()
:在当前数组内部,将指定位置的成员复制到其他位置(会覆盖原有成员),然后返回当前数组。有三个参数:target
:从该位置开始替换数据。start
:从该位置开始读取数据,默认为 0。如果为负值,表示倒数。可省略。end
:到该位置前停止读取数据,默认等于数组长度。如果为负值,表示倒数。可省略。
案例展示
let arr = [1, 2, 3, 4, 5]; | |
console.log(arr.copyWithin(1, 3)); | |
/* 输出结果 | |
[1, 4, 5, 4, 5] | |
*/ |
# find () 和 findIndex ()
find()
:用于找出第一个符合条件的数组成员。它的参数是一个回调函数,所有数组成员依次执行该回调函数,直到找出第一个返回值为true
的成员,然后返回该成员。如果没有符合条件的成员,则返回undefined
。findIndex()
:用法与find()
类似,返回第一个符合条件的数组成员的索引,如果所有成员都不符合条件,则返回-1
。回调函数有三个参数可选:
value
:必需。当前元素。index
:可选。当前元素的索引值。arr
:可选。当前元素所属的数组对象。
案例展示
let arr = [1, 2, 3, 4, 5, 6, 7]; | |
let t = arr.find(function(value){ | |
return value > 4; | |
}); | |
// 另一种形式的写法 | |
// let u = obj_arr.find(value => value > 4); | |
console.log(t); | |
let i = arr.find(function(value){ | |
return value > 4; | |
}); | |
console.log(arr[i]); | |
/* 输出结果 | |
5 | |
5 | |
*/ | |
let obj_arr = [ | |
{ | |
id: 1, | |
book_name: 'C++ Primer' | |
}, | |
{ | |
id: 2, | |
book_name: 'Head First Java' | |
}, | |
{ | |
id: 3, | |
book_name: 'python编程从入门到实践' | |
}, | |
{ | |
id: 4, | |
book_name: 'labuladong的算法小抄' | |
} | |
]; | |
let u = obj_arr.find(function(item){ | |
return item.id === 2; | |
}); | |
// 另一种形式的写法 | |
// let u = obj_arr.find(item => item.id === 2); | |
console.log(u.book_name); | |
/* 输出结果 | |
Head First Java | |
*/ |
# fill()
fill()
:用于将指定值填充入一个数组。有三个参数:- value:必须。用于填充的值。
- start:可选。填充开始的位置,默认为
0
。从指定位置开始填充。负数表示倒数的位置。 - end:可选。填充结束的位置,默认为数组末尾。到指定位置前一个成员结束。负数表示倒数的位置。
案例展示
let arr1 = [1, 2, 3, 4, 5, 6, 7]; | |
let arr2 = [1, 2, 3, 4, 5, 6, 7]; | |
let arr3 = [1, 2, 3, 4, 5, 6, 7]; | |
arr1.fill(5); | |
arr2.fill(5, 3); | |
arr3.fill(5, 3, 6); | |
console.log(arr1); | |
console.log(arr2); | |
console.log(arr3); | |
/* 输出结果 | |
[5, 5, 5, 5, 5, 5, 5] | |
[ 1, 2, 3, 5, 5, 5, 5 ] | |
[ 1, 2, 3, 5, 5, 5, 7 ] | |
*/ |
# 数组遍历
ES 6
提供了三个新方法 keys()
、 values()
和 entries()
,用于遍历数组。它们会返回一个遍历器对象,结合 for...of
循环进行遍历。
keys()
:对键名的遍历。values()
:对键值的遍历。entries()
:对键值对的遍历。
案例展示
let arr = ['a', 'b', 'c']; | |
for(let i of arr.keys()){ | |
console.log(i); | |
} | |
/* 输出结果 | |
0 | |
1 | |
2 | |
*/ | |
for(let i of arr.values()){ | |
console.log(i); | |
} | |
/* 输出结果 | |
a | |
b | |
c | |
*/ | |
for(let [i, j] of arr.entries()){ | |
console.log(i, j); | |
} | |
/* 输出结果 | |
0 a | |
1 b | |
2 c | |
*/ |
# includes()
includes()
:返回一个布尔值,表示数组是否包含指的值。有三个参数可选:- value:必须。搜索的值。
- start:可选。搜索开始的位置,默认为
0
。从指定位置开始填充。负数表示倒数的位置。 - end:可选。搜索结束的位置,默认为数组末尾。到指定位置前一个成员结束。负数表示倒数的位置。
案例展示
let colors = ['red', 'green', 'blue']; | |
console.log(colors.includes('green')); | |
/* 输出结果 | |
true | |
*/ |
# 数组的空位
数组的空位指,数组的某一个位置没有任何值。空位与 undefined
不同,空位表示啥也没有, undefined
表示值为 undefined
。
案例展示
// 判断 1 号位置上是否有值 | |
let a = 1 in [undefined, undefined, undefined]; | |
let b = 1 in [ , , ,]; | |
console.log(a, b); | |
/* 输出结果 | |
true false | |
*/ |
# 函数的扩展
# 默认值
在 ES 6
之前,为函数的参数指定默认值,需要在函数体开头添加默认值赋值语句。如下:
function print(x, y){ | |
y = y || 'world'; | |
console.log(x + " " + y); | |
} | |
print('hello', 'ES 6'); // 输出:hello ES 6 | |
print('hello'); // 输出:hello world | |
print('hello', ''); // 输出:hello world |
这种方法存在缺陷,如上例第三次调用, y
位置传入一个空字符串,却依旧使用默认值代替。
而在 ES 6
中,允许我们直接在函数的参数表里提供默认值。如下:
function print(x, y = 'world'){ | |
console.log(x + " " + y); | |
} | |
print('hello', 'ES 6'); // 输出:hello ES 6 | |
print('hello'); // 输出:hello world | |
print('hello', ''); // 输出:hello |
建议: 最好将含有默认值的参数写在没含默认值的参数后面,防止参数值传递错误。
函数的参数默认值还可以和解构赋值结合使用,具体参考文档 。
# rest 参数
ES 6
提供了一种特殊的参数,叫 rest参数
。形式为 ...参数名
。
其中 ...
是一种语法,它会将传入参数未匹配的剩余所有值整合为一个数组,赋给 参数名
这个变量。
当我们不确定传入参数的个数时,可以发挥强大的作用。但是, rest参数
只能是最后一个参数。
function add1(...values) { | |
let sum = 0; | |
for (var num of values) { | |
sum += num; | |
} | |
console.log(sum); | |
} | |
function add2(a, ...values) { | |
let sum = 0; | |
for (var num of values) { | |
sum += num; | |
} | |
console.log(sum); | |
} | |
add1(1, 2, 3, 4, 5); // 输出:15 values = [1, 2, 3, 4, 5] | |
add2(1, 2, 3, 4, 5); // 输出:14 values = [2, 3, 4, 5],1 传给了 a |
# 扩展运算符
与 rest参数
类似的用法,还有一种扩展运算符 ...
,它相当于 rest参数
的逆运算,作用是将一个数组展开,将运算符后的数组的值依次取出,作用于调用的地方,常用于函数调用。
扩展运算符 ...
可以与正常的参数随意结合使用。
function add(x, y, z, t, p) { | |
console.log(x, y, z, t, p); | |
} | |
arr = [3, 4, 5]; | |
add(2, ...arr, 6); // 输出:2 3 4 5 6 | |
console.log(...arr); // 输出:3 4 5 |
除了用于函数调用,它还可以实现数组拼接。
let arr1 = [1, 2, 3]; | |
let arr2 = [4, 5, 6]; | |
let arr = [...arr1, ...arr2]; | |
console.log(arr); // 输出:[1, 2, 3, 4, 5, 6] |
更多相关应用可查看文档。
# 箭头函数
ES 6
允许使用 =>
定义函数。
基本形式如下:
单个参数 => 单条语句
或单个参数 => { 多条语句 }
(参数表) => 单条语句
或(参数表) => { 多条语句 }
需要注意的点:
- 函数体是单条语句,返回值则可以省略
return
,多条语句则不可省略。 - 参数如果有多个,需要用
()
括起来。 - 函数体内的
this
对象,就是定义时所在的对象,而不是使用时所在的对象。 - 不可以当作构造函数,即不可以使用
new
命令,否则会抛出一个错误。 - 不可以使用
arguments
对象,该对象在函数体内不存在。如果要用,可以用 Rest 参数代替。
案例展示
let f = x => x > 5; | |
console.log(f(6)); // 输出:true | |
let add = (x, y) => x + y; | |
console.log(add(2, 5)); // 输出:7 | |
let print = (...arges) => { | |
let sum = 0; | |
for(let num of arges){ | |
sum += num; | |
} | |
console.log(sum); | |
} | |
print(2, 3, 5); // 输出:10 | |
function IDCode(name = 'Mark', age = 18){ | |
this.name = name; | |
this.age = age; | |
console.log(`My name is ${this.name}, I'm ${this.age} years old.`) | |
} | |
// 错误写法,=> 函数的方式不能用于构造函数 | |
// let IDCode = (name = 'Mark', age = 18) => { | |
// this.name = name; | |
// this.age = age; | |
// console.log(`My name is ${this.name}, I'm ${this.age} years old.`); | |
// } | |
let people = new IDCode(); // 输出:My name is Mark, I'm 18 years old. |
建议: 将 =>
函数用于使用匿名函数的地方,如数组的一些方法等。而其它函数依旧使用 function()
的形式。
# 对象的扩展
# 简洁表示
ES 6
支持一种较为简洁的对象表示方法,通过把变量包装进对象里,变量名就表示属性名,变量值就表示属性值。
var name = 'Mark'; | |
var age = 18; | |
var person = {name, age}; | |
console.log(person); // 输出:{name: 'Mark', age: 18} | |
// 除了正常属性可以这么写外,方法也可以这样写 | |
var name = 'Mark'; | |
var age = 18; | |
var say = function(){ | |
console.log(`My name is ${name}, I'm ${age} years old.`); | |
} | |
var person = {name, age, say}; | |
console.log(person); // 输出:{name: 'Mark', age: 18, say: [Function: say] } |
更多用法详见文档。
# 属性名表达式
ES 6
对象的属性名支持表达式的写法。
let a = 'name'; | |
let obj = { | |
[a]: 'Mark', | |
['a' + 'g' + 'e']: 18 | |
} | |
console.log(obj); // 输出:{name: 'Mark', age: 18} | |
console.log(obj[a]); // 输出:Mark | |
console.log(obj['name']); // 输出:Mark |
属性名为表达式,表达式需用方括号 []
括起来。
注意: 属性名表达式与简洁表示法,不能同时使用,会报错。
# 对象的遍历
ES 6
提供了 5 种方法用于遍历对象的属性。
for...in
循环,遍历对象自身的和继承的可枚举属性(不含 Symbol 属性)。Object.keys(obj)
,返回一个数组,包含对象所有键名(不含 Symbol 属性)。Object.getOwnPropertyNames(obj)
,返回一个数组,包含对象的所有属性(不含 Symbol 属性,但是包含不可枚举属性)。Object.getOwnPropertySymbols(obj)
,返回一个数组,只包含对象的所有 Symbol 属性。Reflect.ownKeys(obj)
,返回一个数组,包含对象自身的所有属性(任何类型都包含)。
案例展示
let a = 'name'; | |
let isAdult = Symbol(); | |
let obj = { | |
[a]: 'Mark', | |
['a' + 'g' + 'e']: 18, | |
[isAdult]: true, | |
say: function(){ | |
console.log(`My name is ${name}, I'm ${age} years old.`); | |
} | |
} | |
console.log(Object.keys(obj)); // 输出:['name', 'age', 'say'] | |
console.log(Object.getOwnPropertyNames(obj)); // 输出:['name', 'age', 'say'] | |
console.log(Object.getOwnPropertySymbols(obj)); // 输出:[Symbol () ] | |
console.log(Reflect.ownKeys(obj)); // 输出:['name', 'age', 'say', Symbol () ] | |
for(let i in obj){ // 输出:name | |
console.log(i); // age | |
} // say |
# Symbol
Symbol
是 ES 6
引入了一种新的原始数据类型,类似字符串,表示独一无二的值。
定义一个 Symbol
变量的基本形式为: let x = Symbol()
。圆括号内可添加描述性字符串。
可作为对象的属性名,用于消除属性名的冲突。作为属性名,需要放在方括号 []
内。
即使描述性字符串完全一样,两个 Symbol
也是不一样的值。
Symbol
有点类似其它语言的 私有属性
,寻常的方法无法探知遍历到。
// 写法一 | |
let a = 'name'; | |
let b = 'name'; | |
let obj = { | |
[a]: 'Mark', | |
[b]: 'Jack' // 两条语句键名相同,均为 'name',最终结果被覆盖为 'Jack' | |
} | |
console.log(obj[a]); // 输出:Jack | |
console.log(obj[b]); // 输出:Jack | |
// 写法二 | |
let a = Symbol('name'); | |
let b = Symbol('name'); | |
let obj = { | |
[a]: 'Mark', | |
[b]: 'Jack' // 虽然看起来都是有相同的语句定义,但均为不同的 Symbol 值,因此不冲突 | |
} | |
console.log(obj[a]); // 输出:Mark | |
console.log(obj[b]); // 输出:Jack |
更多知识相关知识可查看 ES6 文档。