ES6 简介
ES 的全称是 ECMAScript,它是由 ECMA 国际标准化组织制定的一项脚本语言的标准化规范,ES6 实际上是一个泛指,泛指 ES2015 及后续的版本。

var、let、const
let
1 2 3 4 5 6
| if (true) { let num = 100; var abc = 200; } console.log(abc); console.log(num);
|
1 2
| console.log(a); let a = 100;
|
1 2 3 4 5
| var num = 10; if (true) { console.log(num); let num = 20; }
|
经典面试题
使用 let 时,每次循环都会产生一个块级作用域,每个块级作用域中的变量都是不同的
函数执行时输出的是自己循环产生的块级作用域下的值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| var arr = []; for (var i = 0; i < 2; i++) { arr[i] = function () { console.log(i); }; } arr[0](); arr[1]();
console.log('-------------'); let arr = []; for (let i = 0; i < 2; i++) { arr[i] = function () { console.log(i); }; } arr[0](); arr[1]();
|
const
1 2 3 4 5 6 7 8 9
| if (true) { const a = 10; if (true) { const a = 20; console.log(a); } console.log(a); } console.log(a);
|
1 2 3 4 5 6
| const PI = 3.14; PI = 100; const ary = [100, 200]; ary[0] = 123; ary = [1, 2]; console.log(ary);
|
let、const、var 的区别
var | let | const |
---|
全局作用域 | 块级作用域 | 块级作用域 |
变量提升 | 不存在变量提升 | 不存在变量提升 |
值可更改 | 值可更改 | 值不可更改 |
解构赋值
按照一定的模式,从数组或对象中提取值,将提取出来的值赋值给另外的变量
数组解构
1 2 3 4 5 6
| let ary = [1, 2, 3]; let [a, b, c, d] = ary; console.log(a); console.log(b); console.log(c); console.log(d);
|
对象解构
1 2 3 4 5 6 7 8
| let person = { name: 'lisi', age: 30, sex: '男' }; let { name, age, sex } = person; console.log(name); console.log(age); console.log(sex);
let { name: myName } = person; console.log(myName);
|
箭头函数
1 2 3 4
| const fn = () => { console.log(123); }; fn();
|
- 如果函数体中只有一句代码 并且代码的执行结果就是函数的返回值 函数体大括号可以省略
1 2 3
| const sum = (n1, n2) => n1 + n2; const result = sum(10, 20); console.log(result);
|
1 2
| const fn = (v) => console.log(v); fn(20);
|
- 箭头函数不绑定 this 箭头函数没有自己的 this 关键字
1 2 3 4 5 6 7 8 9 10
| function fn() { console.log(this); return () => { console.log(this); }; } const obj = { name: 'zhangsan' }; const resFn = fn.call(obj); resFn();
|
经典面试题
1 2 3 4 5 6 7 8
| var age = 100; var obj = { age: 20, say: () => { console.log(this.age); }, }; obj.say();
|
剩余参数
1 2 3 4 5 6 7
| const sum = (...args) => { let total = 0; args.forEach((item) => (total += item)); return total; }; console.log(sum(10, 20)); console.log(sum(10, 20, 30));
|
剩余参数和解构搭配
1 2 3 4
| let ary1 = ['张三', '李四', '王五']; let [s1, ...s2] = ary1; console.log(s1); console.log(s2);
|
数组的扩展
1 2 3 4
| let ary = ['a', 'b', 'c'];
console.log(...ary); console.log('a', 'b', 'c');
|
1 2 3 4 5 6 7 8 9 10 11 12
| let ary1 = [1, 2, 3]; let ary2 = [4, 5, 6];
let ary3 = [...ary1, ...ary2]; console.log(ary3);
let ary1 = [1, 2, 3]; let ary2 = [4, 5, 6]; ary1.push(...ary2); console.log(ary1);
|
1 2 3 4 5 6 7 8 9 10
| <div>1</div> <div>2</div> <div>3</div> <script> var oDivs = document.getElementsByTagName('div'); console.log(oDivs); var ary = [...oDivs]; ary.push('a'); console.log(ary); </script>
|
Array.from()
1 2 3 4 5 6 7 8
| let arrayLike = { 0: '张三', 1: '李四', 2: '王五', length: 3, }; let ary = Array.from(arrayLike); console.log(ary);
|
- 还可以接收第二个参数,类似于数组的 map()方法,将处理后的值返放入返回的数组
1 2 3 4 5 6 7
| let arrayLike = { 0: '1', 1: '2', length: 2, }; let ary = Array.from(arrayLike, (item) => item * 2); console.log(ary);
|
find()
- 用于找出第一个符合条件的数组成员,如果没有找到返回 undefined
1 2 3 4 5 6 7 8 9 10 11 12
| var ary = [ { id: 1, name: '张三', }, { id: 2, name: '李四', }, ]; let target = ary.find((item) => item.id == 3); console.log(target);
|
findIndex()
- 用于找出第一个符合条件的数组成员的位置,如果没有找到返回-1
1 2 3
| let ary = [10, 20, 50]; let index = ary.findIndex((item) => item > 15); console.log(index);
|
includes()
1 2 3 4 5
| let ary = ['a', 'b', 'c']; let result = ary.includes('a'); console.log(result); result = ary.includes('e'); console.log(result);
|
字符串的扩展
模板字符串
- ES6 新增的创建字符串方式,使用反引号定义,可以解析变量
1 2 3
| let name = `张三`; let sayHello = `Hello, 我的名字叫${name}`; console.log(sayHello);
|
1 2 3 4 5 6 7 8 9 10 11
| let result = { name: 'zhangsan', age: 20, }; let html = ` <div> <span>${result.name}</span> <span>${result.age}</span> </div> `; console.log(html);
|
1 2 3 4 5
| const fn = () => { return '我是fn函数'; }; let html = `我是模板字符串 ${fn()}`; console.log(html);
|
startsWith() 和 endsWith()
- startsWith():表示参数是否在原字符串的头部,返回布尔值
- endsWith():表示参数是否在原字符串的尾部,返回布尔值
1 2 3 4 5
| let str = 'Hello ECMAScript 2015'; let r1 = str.startsWith('Hello'); console.log(r1); let r2 = str.endsWith('2016'); console.log(r2);
|
repeat()
- repeat() 方法表示将原字符串重复 n 次,返回一个新字符串
1
| console.log('y'.repeat(5));
|
Set 数据结构
1 2 3 4 5 6 7 8 9 10
| const s1 = new Set(); console.log(s1.size);
const s2 = new Set(['a', 'b']); console.log(s2.size);
const s3 = new Set(['a', 'a', 'b', 'b']); console.log(s3.size); const ary = [...s3]; console.log(ary);
|
实例方法
- add(value):添加某个值,返回 Set 结构本身
- delete(value):删除某个值,返回一个布尔值,表示是否删除成功
- has(value):返回一个布尔值,表示该值是否为 Set 的成员
- clear():清除所有成员,没有返回值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| const s4 = new Set();
s4.add('a').add('b'); console.log(s4.size);
const r1 = s4.delete('c'); console.log(s4.size); console.log(r1);
const r2 = s4.has('d'); console.log(r2);
s4.clear(); console.log(s4.size);
|
遍历
1 2 3 4 5
| const s5 = new Set(['a', 'b', 'c']); s5.forEach((value) => { console.log(value); });
|
模块化
ES6 的模块化主要包含如下 3 种用法:
- 默认导出与默认导入
- 按需导出与按需导入
- 直接导入并执行模块中的代码
默认导出
1 2 3 4 5 6 7 8
| let n1 = 10; let n2 = 20; function show() {}
export default { n1, show, };
|
默认导入
1 2 3
| import m1 from './01.默认导出.js';
console.log(m1);
|
默认导出与默认导入的注意事项
- 每个模块中,只允许使用唯一的一次 export default(默认导出),否则会报错!
1 2 3 4 5 6 7 8 9 10 11 12 13
| let n1 = 10 let n2 = 20 function show() {}
export default { n1, show }
export default { n2 }
|
- 默认导入时的接收可以任意名称,只要是合法的名称即可
1 2 3 4
| import m1 from './m1.js'
import 123m from './m1.js'
|
按需导出
1 2 3 4 5 6 7 8
| export let s1 = 'aaa'; export let s2 = 'ccc'; export function say() {}
export default { a: 20, };
|
按需导入
1 2 3 4 5 6 7
| import { s1, s2 as str2, say } from './03.按需导出.js'; import info from './03.按需导出.js';
console.log(s1); console.log(str2); console.log(say); console.log(info);
|
按需导出与按需导入的注意事项
- 每个模块可以使用多次按需导出
- 按需导入的成员名称必须和按需导出的名称保持一致
- 按需导入时,可以使用 as 关键字进行重命名
- 按需导入可以和默认导入一起使用
直接导入并执行模块中的代码
如果只想单纯地执行某个模块中的代码,并不需要得到模块中向外共享的成员。此时,可以直接导入并执行模块代码
1 2 3 4 5 6 7 8
| for (let i = 0; i < 3; i++) { console.log(i); }
import './05.直接运行模块中的代码.js';
|
Promise
- Promise 是一个构造函数
- 我们可以创建 Promise 的实例 const p = new Promise()
- new 出来的 Promise 实例对象,代表一个异步操作
- Promise.prototype 上包含一个 .then() 方法
- 每一次 new Promise() 构造函数得到的实例对象
- 都可以通过原型链的方式访问到 .then() 方法,例如 p.then()
- .then() 方法用来预先指定成功和失败的回调函数
- p.then(成功的函数, 失败的函数)
- p.then(result => {}, error => {})
- 调用 .then() 方法时,成功的回调函数是必选的,失败的回调函数是可选的
.then() 方法的特性
如果上一个 .then() 方法中返回了一个新的 Promise 实例对象,则可以通过下一个 .then() 继续进行处理。通过 .then() 方法的链式调用,就解决了回调地狱的问题。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| import thenFs from 'then-fs';
thenFs .readFile('./files/1.txt', 'utf8') .then((r1) => { console.log(r1); return thenFs.readFile('.files/2.txt', 'utf8'); }) .then((r2) => { console.log(r2); return thenFs.readFile('.files/3.txt', 'utf8'); }) .then((r3) => { console.log(r3); });
|
通过 .catch() 捕获错误
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| import thenFs from 'then-fs';
thenFs .readFile('./files/11.txt', 'utf8') .then((r1) => { console.log(r1); return thenFs.readFile('./files/2.txt', 'utf8'); }) .then((r2) => { console.log(r2); return thenFs.readFile('./files/3.txt', 'utf8'); }) .then((r3) => { console.log(r3); }) .catch((err) => { console.log(err.message); });
|
Promise.all() 方法
Promise.all() 方法会发起并行的 Promise 异步操作,等所有的异步操作全部结束后才会执行下一步的 .then 操作(等待机制)。
1 2 3 4 5 6 7
| import thenFs from 'then-fs';
const promiseArr = [thenFs.readFile('./files/3.txt', 'utf8'), thenFs.readFile('./files/2.txt', 'utf8'), thenFs.readFile('./files/1.txt', 'utf8')];
Promise.all(promiseArr).then((result) => { console.log(result); });
|
Promise.race() 方法
Promise.race() 方法会发起并行的 Promise 异步操作,只要任何一个异步操作完成,就立即执行下一步的 .then 操作(赛跑机制)。
1 2 3 4 5 6 7
| import thenFs from 'then-fs';
const promiseArr = [thenFs.readFile('./files/3.txt', 'utf8'), thenFs.readFile('./files/2.txt', 'utf8'), thenFs.readFile('./files/1.txt', 'utf8')];
Promise.race(promiseArr).then((result) => { console.log(result); });
|
基于 Promise 封装方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| import fs from 'fs'
function getFile(fpath) { return new Promise((resolve, reject) => { fs.readFile(fpath, 'utf8', (err, data) { if(err) return reject(err) return resolve(data) }) }) }
getFile('./files/1.txt') .then(res => console.log(r1)) .catch(err => console.log(err.message))
|
async/await
async/await 是 ES8(ECMAScript 2017)引入的新语法,用来简化 Promise 异步操作。在 async/await 出现之前,开发者只能通过链式 .then() 的方式处理 Promise 异步操作。
- .then 链式调用的优点:解决了回调地狱的问题
- .then 链式调用的缺点:代码冗余、阅读性差、不易理解
基本使用
1 2 3 4 5 6 7 8 9 10 11 12
| import thenFs from 'then-fs';
async function getFile() { const r1 = await thenFs.readFile('./files/1.txt', 'utf8'); console.log(r1); const r2 = await thenFs.readFile('./files/2.txt', 'utf8'); console.log(r2); const r3 = await thenFs.readFile('./files/3.txt', 'utf8'); console.log(r3); }
getFile();
|
注意事项
- 如果在 function 中使用了 await,则 function 必须被 async 修饰
- 在 async 方法中,第一个 await 之前的代码会同步执行,await 之后的代码会异步执行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| import thenFs from 'then-fs';
console.log('A'); async function getAllFile() { console.log('B'); const r1 = await thenFs.readFile('./files/1.txt', 'utf8'); const r2 = await thenFs.readFile('./files/2.txt', 'utf8'); const r3 = await thenFs.readFile('./files/3.txt', 'utf8'); console.log(r1, r2, r3); console.log('D'); }
getAllFile(); console.log('C');
|
Event Loop
JavaScript 是一门单线程执行的编程语言。也就是说,同一时间只能做一件事情。单线程执行任务队列的问题:如果前一个任务非常耗时,则后续的任务就不得不一直等待,从而导致程序假死的问题。
同步任务和异步任务
为了防止某个耗时任务导致程序假死的问题,JavaScript 把待执行的任务分为了两类:
- 同步任务(synchronous)
- 又叫做非耗时任务,指的是在主线程上排队执行的那些任务
- 只有前一个任务执行完毕,才能执行后一个任务
- 异步任务(asynchronous)
- 又叫做耗时任务,异步任务由 JavaScript 委托给宿主环境进行执行
- 当异步任务执行完成后,会通知 JavaScript 主线程执行异步任务的回调函数
执行过程
JavaScript 主线程从“任务队列”中读取异步任务的回调函数,放到执行栈中依次执行。这个过程是循环不断的,所以整个的这种运行机制又称为 EventLoop(事件循环)。
- 同步任务由 JavaScript 主线程次序执行
- 异步任务委托给宿主环境执行
- 已完成的异步任务对应的回调函数,会被加入到任务队列中等待执行
- JavaScript 主线程的执行栈被清空后,会读取任务队列中的回调函数,次序执行
- JavaScript 主线程不断重复上面的第 4 步

1 2 3 4 5 6 7 8 9 10 11 12 13 14
| import thenFs from 'then-fs';
console.log('A');
thenFs.readFile('./files/1.txt', 'utf8').then((data) => console.log('B'));
setTimeout(() => { console.log('C'); }, 0);
console.log('D');
|
宏任务和微任务
JavaScript 把异步任务又做了进一步的划分,异步任务又分为两类,分别是:
- 宏任务(macrotask)
- 异步 Ajax 请求、
- setTimeout、setInterval、
- 文件操作
- 其它宏任务
- 微任务(microtask)
- Promise.then、.catch 和 .finally
- process.nextTick
- 其它微任务
执行顺序
每个宏任务执行完后,都会检查是否存在待执行的微任务,如果有,则执行完所有微任务后,再执行下一个宏任务。

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| setTimeout(() => { console.log(1); }, 0);
new Promise((resolve) => { console.log(2); resolve(); }).then(() => { console.log(3); });
console.log(4);
|
经典面试题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| console.log(1);
setTimeout(() => { console.log(2); new Promise((resolve) => { console.log(3); resolve(); }).then(() => console.log(4)); }, 0);
new Promise((resolve) => { console.log(5); resolve(); }).then(() => console.log(6));
setTimeout(() => { console.log(7); new Promise((resolve) => { console.log(8); resolve(); }).then(() => console.log(9)); }, 0);
|