# ECMAScript 6

# 简介

ECMAScript 6.0 (简称 ES6 )是 JavaScript 语言的下一代标准,于 2015 年 6 月发布。

# ECMAScript 和 JavaScript 的关系

ECMAScriptJavaScript 语言的标准规范。我们用 JavaScript 编写程序,然后遵循 ECMAScript 的规范。

ECMAScriptJavaScript 的关系是,前者是后者的规格,后者是前者的一种实现。

  • 具体使用哪一版 ES 规范,主要看运行的浏览器是否支持。

ES6 是一个历史名词,也是一个泛指,含义是 5.1 版以后的 JavaScript 的下一代标准,涵盖了 ES2015ES2016ES2017 等等。除特殊说明外,本文所指的 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 声明的变量跟其它编程语言,如: CC++ 等类似,需要考虑作用域。

案例展示

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 ;如果 nNaN ,效果等同于 0 ;如果 nInfinity 和其它负数,程序将报错。除了正常的数字外, 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

SymbolES 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 文档

更新于 阅读次数

请我喝[茶]~( ̄▽ ̄)~*

深坑妙脆角 微信支付

微信支付

深坑妙脆角 支付宝

支付宝

深坑妙脆角 贝宝

贝宝