ES6新增方法

数组新增方法:

方法描述原数组
.forEach()按顺序为数组中的每个元素调用一次指定的函数(遍历数组)不改变

Symbol 类型

Symbol 是 ES6 新增的一种基本数据类型,它表示独一无二的值( Symbol 值 ),Symbol 类型的值可以作为对象的属性名,它保证每个属性的名字都是独一无二的,不会出现因属性重名而冲突的问题,这也是该数据类型仅有的目的

Symbol 值通过Symbol()函数得到,一个Symbol()函数返回一个 Symbol 值

例:

1
2
3
let s = Symbol();
console.log(s); // Symbol()
console.log(typeof s); // 'symbol'

Symbol() 函数不能通过new Symbol()来调用,这是因为 Symbol 类型是一个基本数据类型,而非对象,因此无需使用 new 来调用,不过 Js 引擎会将 Symbol 值包装成 Symbol 类型对象( 临时对象 )来使用

Symbol()函数可以接受一个字符串作为参数,表示对 Symbol 值的描述,这样在输出 Symbol 值时,比较容易区分

例:

1
2
3
4
5
6
let s1 = Symbol('foo');
let s2 = Symbol('bar');

console.log(s1, s1.toString()); // Symbol(foo) 'Symbol(foo)'
console.log(s2, s2.toString()); // Symbol(bar) 'Symbol(bar)'
console.log(s1 === s2); // false

属性:

  • description

    描述:获取 Symbol 值的描述

    例:

    1
    2
    const sym = Symbol('foo');
    console.log(sym.description); // "foo"

Symbol 值可以作为对象的属性名,这样就能保证不会出现同名的属性

例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
let symbol = Symbol();

// 写法一
let a = {};
a[symbol] = 'Hello!';

// 写法二
let a = {
[symbol]: 'Hello!'
};

// 写法三
let a = {};
Object.defineProperty(a, symbol, { value: 'Hello!' });

// 以上写法都得到同样结果
console.log(a[symbol]); // "Hello!"
/*
注意:使用 Symbol 值定义属性时,Symbol 值必须放在方括号之中使用
*/

Symbol值作为属性名时,需要注意两点:

  1. 使用 Symbol 值定义属性时,Symbol 值必须放在方括号( [] )之中使用
  2. 遍历对象的时候,Symbol 属性名不会出现在for...infor...of循环中,也不会被Object.keys()Object.getOwnPropertyNames()JSON.stringify()返回

如果要想从对象中获取 Symbol 属性名,可以使用Object.getOwnPropertySymbols()方法,该方法会返回对象中所有 Symbol 属性名

例:

1
2
3
4
5
6
7
const obj = {};
let a = Symbol('a');
let b = Symbol('b');
obj[a] = 'Hello';
obj[b] = 'World';
const symbols = Object.getOwnPropertySymbols(obj); // 返回一个数组
console.log(symbols); // [Symbol(a), Symbol(b)]

Iterator 迭代器

迭代器是一个符合迭代器协议的对象,它提供了顺序遍历的机制。允许你依次访问一个容器( 例如:数组、字符串、Set、Map 等 )中的每一个元素,而无需了解容器的内部结构

迭代器对象需要具有一个 next() 方法,该方法返回一个对象,需包含两个属性:

  • value:当前迭代的值
  • done:一个布尔值,表示迭代是否结束。donetrue 表示迭代结束

Iterrable 可迭代对象

可迭代对象是实现了 @@iterator 方法的对象。一个对象如果具有 Symbol.iterator 属性(即具有一个默认的迭代器方法),它就是可迭代的。常见的可迭代对象包括:数组、字符串、SetMaparguments 对象等

例:

1
2
3
4
5
6
7
8
9
10
11
let array = [1, 2, 3];
let iterator = array[Symbol.iterator]();

console.log(iterator.next()); // { value: 1, done: false }
console.log(iterator.next()); // { value: 2, done: false }
console.log(iterator.next()); // { value: 3, done: false }
console.log(iterator.next()); // { value: undefined, done: true }

/*
在这个例子中,数组 array 是可迭代的对象。通过调用 array[Symbol.iterator](),我们得到一个迭代器对象。每次调用 iterator.next(),它返回集合中的下一个值和 done 状态
*/

for…of 循环

for...of 循环是一种简化的方式来遍历可迭代对象,内部使用了 Iterator。它自动处理 next() 的调用,直到 donetrue 为止

例:

1
2
3
4
5
let array = [1, 2, 3];

for (let value of array) {
console.log(value); // 依次输出 1, 2, 3
}

Symbol.iterator

Symbol.iterator 是一个特殊的符号,定义了对象的默认迭代器方法。实现了 Symbol.iterator 的对象被认为是可迭代的,可以用 for...of 来遍历,JavaScript 中的内置类型,如数组、字符串、MapSet 等,默认具有迭代器方法,如果没有我们可以手动创建迭代器方法( Symbol.iterator )

手动创建对象的迭代器方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
let customIterable = {
values: [1, 2, 3],
[Symbol.iterator]: function() {
let index = 0;
let values = this.values;
return {
next: function() {
if (index < values.length) {
return { value: values[index++], done: false };
} else {
return { value: undefined, done: true };
}
}
};
}
};

for (let value of customIterable) {
console.log(value); // 依次输出 1, 2, 3
}

Generator 生成器

Set 集合

Set 是 ES6 新增的一种数据结构,它是一种无重复的集合,类似于数组

Set 类型中的值是唯一的,即使向 Set 类型重复添加相同的值,也只会保留一个

常用方法和属性:

  • add(value):向 Set 类型中添加一个值
  • delete(value):从 Set 类型中删除一个值
  • has(value):判断 Set 类型中是否包含某个值
  • clear():清空 Set 类型
  • size:返回 Set 类型中的值的个数

例:

1
2
3
4
5
6
7
8
9
10
11
12
/* 例1 */
// Set 函数可以接受一个数组(或者具有 iterable 接口的其他数据结构)作为参数,用来初始化
let arr = [1, 2, 3, 4, 5, 5, 5, 5]
let s = new Set(arr)
console.log(s, s.size) // {1, 2, 3, 4, 5} 5
// 数组去重
arr = [...s]

/* 例2 */
// 字符串去重
let str = [...new Set('ababbc')].join('')
console.log(str) // abc

遍历方法:

  • keys():返回键名的遍历器
  • values():返回值的遍历器
  • entries():返回键值对的遍历器
  • forEach():使用回调函数遍历每个成员

keys 方法、values 方法、entries 方法返回的都是遍历器对象(详见 Iterator 遍历器)。由于 Set 结构没有键名,只有键值(或者说键名和键值是同一个值),所以 keys 方法和 values 方法的行为完全一致

例:

Map 类型

ES6 模块化( Module )

在 ES6 中引入的标准化模块系统,它允许开发者将代码分割成独立的模块,并通过导入和导出机制来组织和重用代码。在这之前,JavaScript 一直没有模块(module)系统,社区制定了一些模块加载方案,最主要的有 CommonJS 和 AMD 两种。前者用于 Node.js 环境,后者用于浏览器环境

导出与导入( export & import )

在浏览器环境中,模块就是一个被 ES6 模块化系统所管理的 JavaScript 文件或代码,你可以使用 export 关键字从一个模块中导出变量、函数或类,如果要在别的模块中使用该模块导出的内容,可以使用 import 关键字从其他模块中导入

具名导出与导入:

可以在一个模块中导出多个变量、函数、类等。每个导出的名称都是明确指定的,在导入时,可以选择性地导入所需的内容

1
2
3
4
5
6
7
8
9
10
/* module1.js */

// 分别导出(可导出 var、let、const、function、class)
export let n = Math.sqrt(2)
export function fun() {}

// 统一导出, 导出时还可以通过 as 关键字来重命名导出
const PI = 3.14
class P {}
export { PI, P as Person }
1
2
3
4
5
/* module2.js */

// 导入时还可以通过 as 关键字来重命名导入
import { n, fun as fn, PI, Person } from './module1.js'
console.log(n, fn, PI, Person) // 1.4142135623730951 fun() {} 3.14 class P {}
1
2
<!-- index.html -->
<script src="./module2.js" type="module"></script>

默认导出与导入:

一个模块只能有一个默认导出。它通常用来导出模块的主要功能或值,在导入时,不需要使用花括号,并且可以自定义导入的名称( 名称在导入时决定 )

1
2
3
4
5
6
/* utils.js */

// 一个 js 文件中只能有一个默认导出
export default function () {
console.log('Goodbye!');
}
1
2
3
4
/* main.js */
import farewell from './utils.js'

farewell() // Goodbye!
1
2
<!-- index.html -->
<script src="./main.js" type="module"></script>

组合导出与导入:

1
2
3
4
5
6
7
8
9
10
/* shapes.js */
export const pi = 3.14159;
export default class Circle {
constructor(radius) {
this.radius = radius;
}
area() {
return pi * this.radius * this.radius;
}
}
1
2
3
4
5
/* main.js */
import Circle, { pi } from './shapes.js';
const myCircle = new Circle(5);
console.log(myCircle.area()); // 78.53975
console.log(pi); // 3.14159
1
2
<!-- index.html -->
<script src="./main.js" type="module"></script>

导入所有:

1
2
// 将 module.js 导出的所有内容作为对象属性保存在变量 m 中
import * as m from './module.js'
  • 模块的使用方式

    在 HTML 中使用 <script type="module"> 标签来告诉浏览器这是一个模块文件。浏览器会自动识别并处理模块中的依赖关系和导入导出。在 Web浏览器中你需要通过 Web 服务器去运行,这是因为浏览器需要通过 HTTP 协议并且会按照 CORS 规则来访问

  • 模块是独立作用域的

    每个模块都有自己的作用域,不会污染全局命名空间。模块内的变量、函数、类等,默认是模块私有的,只有通过 export 导出才能被其他模块访问

  • 模块自动采用严格模式

    无需手动指定 "use strict",模块文件会自动在严格模式下运行

  • 异步加载

    浏览器会异步加载模块文件,这意味着不会阻塞页面的渲染。即使使用 import 来加载多个模块,浏览器也会并行下载这些模块,优化性能

  • 模块的静态的

    importexport 是静态的,也就是说,它们必须在模块的顶层使用,不能在条件语句或函数中。

    这样编译器可以提前分析模块的依赖关系,进行优化,如 tree-shaking

Proxy

ES6 模块的设计思想是尽量的静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量。CommonJS 和 AMD 模块,都只能在运行时确定这些东西。比如,CommonJS 模块就是对象,输入时必须查找对象属性

Reflect

globalThis