JavaScript ES6

ES6, 全称 ECMAScript 6.0 ,是 JavaScript 的下一个版本标准。

一、ES6

1. 什么是 ES6 ?

ES6 是 JavaScript 的下一代标准。

2. 支持情况

ECMAScript 6 compatibility table

3. Babel

一般通过 Babel 进行转码,它可以将 ES6 代码转换为更具兼容性的语法,使其能够在旧版本的浏览器下运行。

二、let 和 const

1. let

let 用于声明一个仅在代码块内有效的变量。

作用域仅在块中:

  • 使用 var 声明的变量没有块作用域

    1
    2
    3
    4
    { 
    var x = 10;
    }
    // 此处可以访问到x
  • 使用 let 声明的变量仅能在代码块中访问

    1
    2
    3
    4
    { 
    let x = 10;
    }
    // 此处无法访问到x

变量覆盖问题:

  • var 可以重复声明,而 let 不行

    1
    2
    3
    4
    5
    6
    7
    // var可以重复声明
    var a = 1;
    var a = 2;

    // let不能重复声明
    let b = 3;
    let b = 4;
  • 若代码块外部原有变量,在代码块中用 var 定义同名变量,将会覆盖代码块外的变量

    1
    2
    3
    4
    5
    6
    7
    var x = 10;
    // 此处 x 为 10
    {
    var x = 6;
    // 此处 x 为 6
    }
    // 此处 x 为 6

    使用 let 就可以解决这一问题

    1
    2
    3
    4
    5
    6
    7
    var x = 10;
    // 此处 x 为 10
    {
    let x = 6;
    // 此处 x 为 6
    }
    // 此处 x 为 10

变量提升问题:

  • 对于 var 来说,变量的声明将会被提升到作用域的顶端

    1
    2
    3
    4
    5
    6
    // 在声明前使用,将不会报错
    // 但由于未定义,因此将会输出undefined
    console.log(a)

    // 在使用后才声明并定义
    var a = 5;
  • 对于 let 来说,不存在变量提升,因此在声明前使用将会报错

    1
    2
    3
    4
    5
    // 报错
    console.log(a)

    // 在使用后才声明并定义
    let a = 5;

迭代变量复用问题:

  • 对于 var 来说,迭代变量会重用,从而可能导致执行结果不符合预期

    1
    2
    3
    for (var i = 0; i < 5; i++ ) {
    setTimeout(() => console.log(i), 0); // 5 5 5 5 5
    }
  • 对于 let 来说,每次迭代循环都会声明一个新的迭代变量

    1
    2
    3
    for (let i = 0; i < 5; i++ ) {
    setTimeout(() => console.log(i), 0); // 0 1 2 3 4
    }

2. const

const 用于声明常变量。

  • 声明后不允许改变
  • 因为声明后不允许改变,因此应该在声明时初始化
  • const 保证变量值不变,因此,如果变量为引用类型,变量所指向的内存地址不允许改变,但内存地址上的值可以改变

三、解构赋值

1. 说明

对象的解构赋值针对 对象/数组 进行模式匹配,对其中的变量进行赋值。

2. 数组的解构赋值

希望从数组中提取值,对变量进行赋值。

不使用解构赋值语法:

1
2
3
4
let list = [1, 2, 3]
let a = list[0]
let b = list[3]
let c = list[2]

使用结构赋值语法:

1
2
let list = [1, 2, 3]
let [a, b, c] = list

3. 对象的解构赋值

对象的结构赋值与数组的不同在于:

数组元素是按次序排列的,变量只需要按顺序排列即可取到值;

而对象的属性没有次序,变量必须和属性同名才能取到正确的值。

不使用解构赋值语法:

1
2
3
let user = { name: '小张', age: 20 }
let name = user.name
let age = user.age

使用结构赋值语法:

1
2
let user = { name: '小张', age: 20 }
let { name, age } = user

使用解构赋值语法,且变量名不对应:

1
2
3
let user = { name: '小张', age: 20 }
let { name: uname, age: uage } = user
// uname = '小张' uage = 20

4. 默认值

解构赋值允许指定默认值,当且仅当对应 数组元素/对象成员 严格等于 undefined 时,默认值才会生效。

1
2
3
4
5
let [a = 默认值] = []
// a的值为默认值

let [b = 默认值] = [null]
// b的值为null

5. 应用

(1) 获取对象的方法

通过对象的解构赋值,可以获取对象的方法并赋给变量。

获取 console.log:

1
const { log } = console

获取对数、正弦、余弦方法:

1
const { log, sin, cos } = Math

(2) 交换变量值

1
[x, y] = [y, x]

四、Symbol

1. 说明

Symbol 是 ES6 引入的新数据类型,用于表示独一无二的值。

2. 基本使用

1
let 变量名 = Symbol("描述")

3. 注意点

  • Symbol 变量的类型为 symbol

    1
    2
    let a = Symbol("描述")
    // typeof(a)的值为symbol
  • 若将 Symbol 变量 log,将会展示描述

    1
    2
    let a = Symbol("描述")
    console.log(a) // Symbol("描述")
  • 每个 Symbol 的值都是唯一的,即使他们的描述相同

    1
    2
    3
    let a = Symbol("描述")
    let b = Symbol("描述")
    console.log(a === b) // false
  • Symbol 作为属性名时,无法被 for in/ofObject.keys()Object.getOwnPropertyNames()JSON.stringify() 访问

  • 如果希望获取对象的 Symbol 属性,可以通过 Object.getOwnPropertySymbols()Reflect.ownKeys()

4. Symbol.for()

首先全局搜索被登记的 Symbol 中是否有以指定字符串作为描述的 Symbol,如果有则返回,如果没有则新建并登记。

1
2
3
let yellow1 = Symbol.for("Yellow")
let yellow2 = Symbol.for("Yellow")
yellow1 === yellow2 // true

5. Symbol.keyFor()

该方法可以接收一个已被登记的 Symbol,返回其描述。

1
2
let 变量名 = Symbol.for("描述")
Symbol.keyFor(变量名) // 描述

4. 应用 - 作为属性名

因为 Symbol 的值都是不相等的,因此可以作为对象的属性名,从而保证对象的属性不重名。

1
2
3
4
5
6
7
8
9
10
let sy = Symbol("描述1")

// 写法1
let object = {}
object[sy] = "属性值"

// 写法2
let object = {
[sy]: "属性值"
}

需要注意的是:

Symbol 作为对象属性名时,不能使用 . 运算符,而应该使用 []

因为 . 运算符之后总是字符串,因此会读取到 Symbol 的 toString 值,而不会读取到 Symbol 的值。

五、Map 和 Set

1. Map

(1) 说明

Map 用于保存键值对。

(2) 特点

  • Map 的 key 可以为任何值
  • Map 的键值对是有序的
  • Map 的键值对个数可以通过 size 属性获取

2. Set

Set 对象用于存储一堆值,这些值应该是唯一的。

六、Reflect 和 Proxy

待更

七、字符串

1. 字符串的识别

在 ES6 之前,如果希望判断字符串中是否包含字串,需要借助 indexOf() 方法。

ES6 新增了以下实例方法:

  • includes():判断字符串中是否包含子串,返回布尔值
  • startsWith():判断字符串是否以子串开头,返回布尔值
  • endsWith():判断字符串是否以字串结尾,返回布尔值

2. 字符串重复

repeat() 方法用于将字符串重复指定次数后返回。

1
2
let str = "hello".repeat(2)
// str的值为 "hellohello"
  • 如果参数是小数,则向下取整
  • 如果参数在 -1 ~ 0 之间,则取整至 0
  • 如果参数为负数,报错
  • 如果参数为 Infinity,报错
  • 如果参数为 NaN,取整至 0
  • 如果参数为字符串,会尝试将字符串转换为数字

3. 字符串补全

  • padStart():接受一个字符串参数,将其从头部补全原字符串,返回新字符串
  • padEnd():接受一个字符串参数,将其从尾部补全原字符串,返回新字符串

4. 模板字符串

(1) 多行字符串

可以通过 `` ` 符号来定义多行字符串。

1
2
3
let str =  `Hello,
World`
console.log(str);

(2) 插入变量、表达式、函数

可以在被 `` 符号包裹的字符串中,使用${}` 插入变量、表达式、函数。

1
2
3
4
5
6
7
8
9
10
let name = "小张"
let birthYear = 2000
let sayHi = function(name) {
return "你好呀!" + name
}
let str =
`姓名:${name}
年龄:${2021 - birthYear}
欢迎词:${sayHi(name)}`
console.log(str)

(3) 作为函数参数

  • 模板字符串可以作为函数参数,并且可以简化函数的调用

  • 当模板字符串为纯字符串时,

    1
    fun`hello`

    等价于:

    1
    fun("hello")
  • 当模板字符串带有变量、表达式、函数时,JavaScript 会将模板字符串处理为多个参数,具体处理方法为:

    • 将模板字符串挨个拆分
    • 拆分后的字符串部分组成字符串数组,作为函数的第 1 个参数
    • 拆分后的变量、表达式、函数部分依次排开,作为函数的第 2、… 、n 个参数
    1
    f`My Name is ${name},I am ${age+1} years old next year.`;

    等价于:

    1
    f(['My Name is',',I am ',' years old next year.'],'Mike',28);

八、数值

待更

九、对象

1. 属性的简洁表示

ES6 允许用变量简洁表示对象的属性,无需显式给出对象属性的属性名和属性值,而是直接填入变量即可,此时变量名将作为属性名,而变量值将作为属性值。

非简洁表示:

1
2
3
4
let user1 = {
name: "小张",
age: 20
}

简洁表示:

1
2
3
4
5
6
let name = "小张"
let age = 20
let user1 = {
name,
age
}

2. 方法的简洁表示

对于对象中的方法,允许进行简写。

非简洁表示:

1
2
3
4
5
let user1 = {
sayHi: function(){
console.log("Hi");
}
}

简洁表示:

1
2
3
4
5
let user1 = {
sayHi(){
console.log("Hi");
}
}

3. 表达式做属性/方法名

ES6 允许使用表达式作为属性/方法名,只需要将表达式用 [] 包裹即可。

1
2
3
4
let user = {
["na" + "me"]: "小张",
[isWorker ? "wid" : "uid"]: "1001"
}

4. 扩展运算符

(1) 作用

扩展运算符 ... 的作用是:取出目标对象中的所有可遍历属性,拷贝至当前对象之中。

(2) 基本用法

1
let 对象2 = {...对象1}

此段代码将对象 1 **中的可遍历属性拷贝至对象 2 **之中,从而实现了对象的 “不完全深拷贝”。

假设对象的成员为对象/数组,拷贝时将拷贝”指针”,而非完整拷贝。

(3) 合并对象

1
let 对象3 = {...对象1, ...对象2}

5. Object.assign()

1
Object.assign(目标对象, 对象1, 对象2, ...)

Object.assign() 方法用于将对象中的可遍历属性拷贝至目标对象之中,从而实现对象的 “不完全拷贝”。

6. Object.is()

1
Object.is(对象1, 对象2)

用于判断两个对象是否严格相等,与 === 类似。

十、数组

待更

十一、函数

1. 参数默认值

可以为函数的参数指定默认值。

1
2
3
fun(参数名 = 默认值) {

}

当且仅当未传递参数或参数为 undefined 时,参数的默认值才会生效

2. 不确定个数的参数

ES6 允许不确定个数的参数,该参数只能有一个,并且只能放在参数的最后。

所有不确定个数的 “多余参数” 将会组成一个数组,传递给函数。

1
2
3
4
5
6
7
8
9
10
fun(参数1, 参数2, ...参数数组) {

}

// 调用函数并传入参数
fun(1, 2, 3, 4, 5, 6)
// 此时函数接收到的参数情况如下:
// 参数1 -- 1
// 参数2 -- 2
// 参数数组 -- [3, 4, 5, 6]

3. 箭头函数

(1) 简化书写

箭头函数允许用较为简短的语句编写函数表达式。

1
2
3
4
5
6
7
8
function(参数) {
代码;
}

// 可以改写为
(参数) => {
代码;
}

箭头函数的进一步简化:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 普通版
var sum = function(x, y) {
return x + y;
}

// 箭头函数-普通版
const sum = (x, y) => {
return x + y;
}

// 箭头函数-只有一行代码且返回结果时
const sum = (x, y) => x + y;

// 箭头函数-只有一行代码且返回结果且只有一个参数时
const sum = x => x + 1;

(2) this 问题

对于箭头函数而言,函数中的 this 指的是定义函数时的 this,而非使用函数时的 this 。

十二、Class 类

1. 说明

在 ES6 中引入了 Class 类这一概念,它可以看作一个语法糖,本质上是对象构造函数。

2. 基本语法

(1) 类的定义

使用 class 定义类;

类中可以书写成员属性和成员方法;

使用 constructor 定义构造函数,在其中初始化对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class 类名 {
属性名1;
属性名2;

···

constructor(···) {
···
}

···

方法名() {
···
}
}

(2) 类的实例化

1
new 类名()

3. 类的继承

通过 extends 实现继承,通过 super 调用父类方法。

1
2
3
4
5
6
7
8
class Cat extends Animal {

constructor(···) {
···
super(···);
}

}

4. 寄存器

可以通过 get 和 set 定义对原有属性的封装,控制对原有属性的访问及修改。

1
2
3
4
5
6
7
8
9
10
11
12
13
class 类名 {

属性名

get 访问器名() {
return this.属性名
}

set 访问器名(参数) {
this.属性名 = 参数
}

}

需要注意的是:

  • 属性名与访问器名不能相同,否则会陷入死循环
  • get 和 set 必须成对出现

十三、模块化

具体请看:

前端 模块化开发

十四、Promise

具体请看:

JavaScript Promise

十五、Generator

待更

十六、async

1. async function

1
2
3
async function 函数名() {

}

async 关键字放置于 function 之前,它将会创建一个异步函数,函数将异步执行并返回 Promise 对象。

2. await

1
2
3
4
5
async function 函数名() {
···
[let res = ]await 表达式
···
}
  • await 操作符后跟 Promise 对象

    也可以后跟其它表达式,表达式将会立刻执行并返回结果

  • await 操作符只能在异步函数中使用

  • await 表达式 将会等待 Promise 执行结束,并返回执行结果

参考