let 关键字用来声明变量,使用 let 声明的变量有几个特点:
1、不允许重复声明
2、块级作用域
if (true) {
let a = 10;
}
console.log(a) // a is not defined
3、不存在变量提升
console.log(a); //报错
let a = 20;
4、不影响作用域链
以后声明变量使用let 就对了
案例1:给多个 div 循环注册点击事件
// 错误示例,divs.length === 3
document.addEventListener('DOMContentLoaded', function () {
let divs = document.querySelectorAll('.box div');
for (var i = 0; i < divs.length; i++) {
divs[i].addEventListener('click', function () {
divs[i].style.backgroundColor = 'pink';
});
}
console.log(i); // 3
});
/*
i 为当前作用域下的共享变量。
当每次点击 div 的时候,各个点击事件共享 i 的值。
此时 i = 3,这将报错。
*/
正确实例:将以上代码中的 var 改为 let。
案例2:1s 后循环输出所有数字
错误示例:
for (var i = 1; i <= 5; i++) {
setTimeout(() => {
console.log(i);
}, 1000);
}
/*
输出:6 6 6 6 6
循环从1-5的时间很短暂,远不及 1s。
此时五个异步事件瞬间加入到异步事件队列中,等待 1s后依次执行。
而此时i为6,故瞬间输出 5 个 6。
异步事件队头
(1) console.log(i);
(2) console.log(i);
(3) console.log(i);
(4) console.log(i);
(5) console.log(i);
*/
正确示例:
for (let j = 1; j <= 5; j++) {
setTimeout(() => {
console.log(j);
}, 1000);
}
// 输出:1 2 3 4 5
// let 有块级作用域,每个 j 都会形成一个自己的块级作用域,与相应的异步事件共享:
// {j = 1;} {j = 2;} {j = 3;} {j = 4;} {j = 5;}
解决方法2:
// 给每一个 i 设置一个立即执行函数,会形成自己的块级作用域,不影响外部变量。
for (var i = 1; i <= 5; i++) {
(function (i) {
setTimeout(() => {
console.log(i);
}, 1000);
})(i);
}
const 关键字用来声明常量,const 声明有以下特点:
1、声明必须赋初始值
const test; // Missing initializer in const declaration
2、标识符一般为大写(潜规则,小写也行)
3、不允许重复声明
4、值不允许修改,但对数组和对象的元素修改,不算做对常量的修改,不会报错,原因是地址没有发生改变
5、块级作用域
应用场景:声明对象类型使用 const,非对象类型声明选择 let
ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构赋值。
1、数组的解构赋值
const arr = ['red', 'green', 'blue'];
let [r, g, b] = arr;
2、对象的解构赋值
const obj = {
uname: 'rick',
age: 30,
sayHi: function () {
console.log('hello');
},
sayBye() {
console.log('Bye~');
}
}
let {name, age, sayHi} = obj;
let {sayBye} = obj;
应用场景:频繁使用对象方法、数组元素,就可以使用解构赋值形式。
模板字符串(template string)是增强版的字符串,用反引号 ` 标识,特点:
1、字符串中可以出现换行符
let ul = `
- apple
- banana
- peach
`
2、可以使用 ${xxx} 形式输出变量(这个${}是固定写法)
let name = 'jack';
console.log(`hello, ${name}`);
应用场景:当遇到字符串与变量拼接的情况使用模板字符串。
ES6 允许在大括号里面,直接写入变量和函数,作为对象的属性和方法。这样的书写更加简洁。
let name = 'tianyang';
let slogon = '一点一滴都是进步';
let improve = function () { console.log('学习可以提高你的技能'); }
//属性和方法简写
let xuexi = {
name,//name:name的简化
slogon,
improve(){console.log('学习可以提高你的技能');}//improve:function () { console.log('学习可以提高你的技能'); }的简化
};
console.log(xuexi)
ES6 允许使用() => {} 定义函数。
function 写法
function fn(param1, param2, …, paramN) {
// 函数体
return expression;
}
箭头函数写法
let fn = (param1, param2, …, paramN) => {
// 函数体
return expression;
}
箭头函数的 注意点:
1、如果形参只有一个,则小括号可以省略
2、函数体如果只有一条语句,则花括号可以省略,函数的返回值为该条语句的执行结果
3、箭头函数 this 始终指向声明时所在作用域下 this 的值
4、箭头函数不能作为构造函数实例化
5、不能使用 arguments
// 省略小括号
let fn1 = n => {
return n * n;
}
// 省略花括号
let fn2 = (a + b) => a + b; //return也不能写
// 箭头函数 this 始终指向声明时所在作用域下 this 的值
const obj = {
a: 10,
getA () {
let fn3 = () => {
console.log(this); // obj {...}
console.log(this.a); // 10
}
fn3();
}
}
案例1:箭头函数 this 始终指向声明时所在作用域下 this 的值,call 等方法无法改变其指向。
let obj = {
uname: 'rick',
age: 30
};
let foo = () => {
console.log(this);
}
let bar = function () {
console.log(this);
}
// call 对箭头函数无效
foo.call(obj); // window
bar.call(obj); // obj {...}
案例2:筛选偶数
let arr = [2, 4, 5, 10, 12, 13, 20];
let res = arr.filter(v => v % 2 === 0);
console.log(res); // [2, 4 , 10, 12, 20]
案例3:点击 div两秒后变成粉色
方案1:使用 _this 保存 div 下的 this,从而设置 div 的 style 属性。
div.addEventListener('click', function () {
let _this = this; //不这样做this就指向window,现在指向div
setTimeout(function () {
console.log(_this); // ...
_this.style.backgroundColor = 'pink';
}, 2000)
});
- 1
- 2
- 3
- 4
- 5
- 6
- 7
方案2:使用 => 箭头函数
div.addEventListener('click', function () {
setTimeout(() => {
console.log(this); // ...
this.style.backgroundColor = 'pink';
}, 2000);
});
- 1
- 2
- 3
- 4
- 5
- 6
箭头函数适合与this无关的回调,定时器,数组的方法的回调
箭头函数不适合与this有关的回调,事件回调,对象的方法
7. 函数参数默认值设定
ES6 允许给函数参数设置默认值,当调用函数时不给实参,则使用参数默认值。
具有默认值的形参,一般要靠后。
let add = (x, y, z=3) => x + y + z;
console.log(add(1, 2)); // 6
- 1
- 2
可与解构赋值结合:
function connect({ host = '127.0.0.1', uesername, password, port }) {
console.log(host); // 127.0.0.1
console.log(uesername);
console.log(password);
console.log(port);
}
connect({
// host: 'docs.mphy.top',
uesername: 'root',
password: 'root',
port: 3306
})
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
8. rest 参数
ES6 引入 rest 参数,用于获取函数的实参,用来代替 arguments,作用与 arguments 类似。将接收的参数序列转换为一个数组对象。
用在函数形参中,语法格式:fn(a, b, ...args),写在参数列表最后面。
let fn = (a, b, ...args) => {
console.log(a);
console.log(b);
console.log(args);
};
fn(1,2,3,4,5);
/*
1
2
[3, 4, 5] 返回的是一个数组,而arguments返回的是对象
*/
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
案例1:求不定个数数字的和
let add = (...args) => {
//reduce() 方法接收一个函数作为累加器,reduce 为数组中的每一个元素依次执行回调函数
//接受四个参数:初始值(上一次回调的返回值),当前元素值,当前索引,原数组
let sum = args.reduce((pre, cur) => pre + cur, 0);
return sum;
}
console.log(add(1, 2, 3, 4, 5)); // 15
- 1
- 2
- 3
- 4
- 5
- 6
- 7
应用场景:rest 参数非常适合不定个数参数函数的场景
9. spread 扩展运算符
扩展运算符(spread)也是三个点(…)。它好比 rest 参数的逆运算,将一个数组转为用逗号分隔的参数序列,对数组进行解包。可用在调用函数时,传递的实参,将一个数组转换为参数序列。
扩展运算符也可以将对象解包。
let arr = [1, 2, 3];
/*...arr*/ // 1, 2, 3
console.log(...arr); // 1 2 3
//等同于
console.log(1, 2, 3)
- 1
- 2
- 3
- 4
- 5
案例1:数组合并
// 方法一
let A = [1, 2, 3];
let B = [4, 5, 6];
let C = [...A, ...B];
console.log(C); // [1, 2, 3, 4, 5, 6]
// 方法二
A.push(...B);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
案例2:数组克隆
这种数组克隆属于浅拷贝。
let arr1 = ['a', 'b', 'c'];
let arr2 = [...arr1];
console.log(arr2); // ['a', 'b', 'c']
- 1
- 2
- 3
案例3:将伪数组转换为真实数组
const divs = document.querySelectorAll('div'); //是一个对象
let divArr = [...divs]; //变成了数组
console.log(divArr);
- 1
- 2
- 3
案例4:对象合并
// 合并对象
let obj1 = {
a: 123
};
let obj2 = {
b: 456
};
let obj3 = {
c: 789
};
let obj = { ...obj1, ...obj2, ...obj3 };
console.log(obj);
// { a: 123, b: 456, c: 789 }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
10. Symbol数据类型
ES6 引入了一种新的原始数据类型 Symbol,表示独一无二的值。它是 JavaScript 语言的第七种数据类型,是一种类似于字符串的数据类型。
JavaScript 的七种基本数据类型:
1、值类型(基本类型):string、number、boolean、undefined、null、symbol
2、引用数据类型:object(包括了array、function)
Symbol 的特点:
1、Symbol 的值是唯一的,用来解决命名冲突的问题
2、Symbol 值不能与其他数据进行运算
3、Symbol 定义的对象属性不能使用 for...in 循环遍历,但是可以使用 Reflect.ownKeys 来获取对象的所有键名
1、Symbol的创建方式:
//创建一个 Symbol
let s1 = Symbol();
console.log(s1, typeof s1); // Symbol() symbol
//添加具有标识的 Symbol
let s2 = Symbol('1');
let s2_1 = Symbol('1');
console.log(s2 === s2_1); // false Symbol 都是独一无二的
//使用 Symbol.for() 方法创建,名字相同的 Symbol 具有相同的实体。
let s3 = Symbol.for('apple');
let s3_1 = Symbol.for('apple');
console.log(s3 === s3_1); // true
//输出 Symbol 变量的描述,使用 description 属性
let s4 = Symbol('测试');
console.log(s4.description); // 测试
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
11. 对象添加 Symbol 类型的属性
案例:安全的向对象中添加属性和方法。
分析:如果直接向对象中添加属性或方法,则原来对象中可能已经存在了同名属性或方法,会覆盖掉原来的。所以使用 Symbol 生成唯一的属性或方法名,可以更加安全的添加。
代码实现:
// 这是一个 game 对象,假设我们不知道里面有什么属性和方法
const game = {
uname: '俄罗斯方块',
up: function () { },
down: function () { }
}
// 通过 Symbol 生成唯一的属性名,然后给 game 添加方法
let [up, down] = [Symbol('up'), Symbol('down')];
game[up] = function () {
console.log('up');
}
game[down] = function () {
console.log('down');
}
// 调用刚刚创建的方法
game[up]();
game[down]();
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
12. Symbol 内置值
除了定义自己使用的 Symbol 值以外,ES6 还提供了11 个内置的 Symbol 值,指向语言内部使用的方法。可以称这些方法为魔术方法,因为它们会在特定的场景下自动执行。

案例1:Symbol.hasInstance 方法判断是否属于这个对象时被调用。
class A {
static [Symbol.hasInstance]() {
console.log('判断是否属于这个对象时被调用');
}
}
let obj = {};
console.log(obj instanceof A
// 判断是否属于这个对象时被调用
// false
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
案例2:数组使用 concat 方法时,是否可以展开。
let arr1 = [1, 2, 3];
let arr2 = [4, 5, 6];
let arr3 = [4, 5, 6];
arr2[Symbol.isConcatSpreadable] = false;
console.log(arr1.concat(arr2));
// [ 1, 2, 3, [ 4, 5, 6, [Symbol(Symbol.isConcatSpreadable)]: false ] ]
console.log(arr1.concat(arr3));
// [ 1, 2, 3, 4, 5, 6 ]
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
13. 迭代器
迭代器(Iterator)就是一种机制。它是一种接口,为各种不同的数据结构提 供统一的访问机制。任何数据结构只要部署 Iterator 接口,就可以完成遍历操作。
1、ES6 创造了一种新的遍历命令 for...of 循环,Iterator 接口主要供 for...of 消费。
2、原生具备 iterator 接口的数据(可用for of 遍历)
1.Array
2.Arguments
3.Set
4.Map
5.String
6.TypedArray
7.NodeList
- 1
- 2
- 3
- 4
- 5
- 6
- 7
工作原理
1、创建一个指针对象,指向当前数据结构的起始位置
2、第一次调用对象的 next 方法,指针自动指向数据结构的第一个成员
3、接下来不断调用 next 方法,指针一直往后移动,直到指向最后一个成员
4、每调用 next 方法返回一个包含 value 和 done 属性的对象
应用场景:需要自定义遍历数据的时候,要想到迭代器。
自定义遍历数据
我们可以通过给数据结构添加自定义 [Symbol.iterator]() 方法来使该数据结构能够直接被遍历,从而使 for...of 能够直接遍历指定数据,达到为 for...of 服务的功能。
// 需求:遍历对象中的数组
const xiaomi = {
uname: '小明',
course: [ '高数', '大物', '英语', '数据库' ],
// 通过自定义 [Symbol.iterator]() 方法
[Symbol.iterator]() {
// 初始指针对象指向数组第一个
let index = 0;
// 保存 xiaomi 的 this 值
let _this = this;
return {
next: function () {
// 不断调用 next 方法,直到指向最后一个成员
if (index < _this.course.length) {
return { value: _this.course[index++], done: false };
} else {
// 每调用next 方法返回一个包含value 和done 属性的对象
return { value: undefined, done: true };
}
}
}
}
}
// for...of直接遍历达到目的
for (let v of xiaomi) {
console.log(v);
}
- 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
- 26
- 27
14. Generator 生成器函数
生成器函数是 ES6 提供的一种 异步编程解决方案,语法行为与传统函数完全不同。
1、* 的位置没有限制
2、使用 function * gen() 和 yield 可以声明一个生成器函数。生成器函数返回的结果是迭代器对象,调用迭代器对象的 next 方法可以得到 yield 语句后的值。
3、每一个 yield 相当于函数的暂停标记,也可以认为是一个分隔符,每调用一次 next(),生成器函数就往下执行一段。
4、next 方法可以传递实参,作为 yield 语句的返回值
例如以下生成器函数中,3 个 yield 语句将函数内部分成了 4 段。
function* generator() {
console.log('before 111'); // 生成器第 1 段
yield 111;
console.log('before 222'); // 生成器第 1 段
yield 222;
console.log('before 333'); // 生成器第 1 段
yield 333;
console.log('after 333'); // 生成器第 1 段
}
let iter = generator();
console.log(iter.next());
console.log(iter.next());
console.log(iter.next());
console.log(iter.next());
/*
before 111
{ value: 111, done: false }
before 222
{ value: 222, done: false }
before 333
{ value: 333, done: false }
after 333
{ value: undefined, done: true }
*/
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
生成器函数的参数传递
function* generator(arg) {
console.log(arg); // 生成器第 1 段
let one = yield 111;
console.log(one); // 生成器第 2 段
let two = yield 222;
console.log(two); // 生成器第 3 段
let three = yield 333;
console.log(three); // 生成器第 4 段
}
let iter = generator('aaa'); // 传给生成器第 1 段
console.log(iter.next());
console.log(iter.next('bbb')); // 传给生成器第 2 段,作为这一段开始的 yield 语句返回值
console.log(iter.next('ccc')); // 传给生成器第 3 段,作为这一段开始的 yield 语句返回值
console.log(iter.next('ddd')); // 传给生成器第 4 段,作为这一段开始的 yield 语句返回值
/*
aaa
{ value: 111, done: false }
bbb
{ value: 222, done: false }
ccc
{ value: 333, done: false }
ddd
{ value: undefined, done: true }
*/
- 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
15. 生成器函数案例
案例1:1s后输出111,2s后输出222,3s后输出333
传统方式:嵌套太多,代码复杂,产生回调地狱。
setTimeout(() => {
console.log(111);
setTimeout(() => {
console.log(222);
setTimeout(() => {
console.log(333);
}, 3000);
}, 2000);
}, 1000);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
生成器实现:结构简洁明了
function one() {
setTimeout(() => {
console.log(111);
iter.next();
}, 1000);
}
function two() {
setTimeout(() => {
console.log(222);
iter.next();
}, 2000);
}
function three() {
setTimeout(() => {
console.log(333);
iter.next();
}, 3000);
}
function* generator() {
yield one();
yield two();
yield three();
}
let iter = generator();
iter.next();
- 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
- 26
- 27
- 28
- 29
案例2:生成器函数模拟每隔1s获取商品数据
function getUsers() {
setTimeout(() => {
let data = '用户数据';
iter.next(data); // 传参给生成器函数的第 2 段,后面类似
}, 1000);
}
function getOrders() {
setTimeout(() => {
let data = '订单数据';
iter.next(data);
}, 1000);
}
function getGoods() {
setTimeout(() => {
let data = '商品数据';
iter.next(data);
}, 1000);
}
function* generator() {
let users = yield getUsers();
console.log(users);
let orders = yield getOrders();
console.log(orders);
let goods = yield getGoods();
console.log(goods);
}
let iter = generator();
iter.next();
- 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
- 26
- 27
- 28
- 29
- 30
- 31
- 32
16. Promise
1、Promise 的定义和使用
Promise 是 ES6 引入的异步编程的新解决方案。语法上 Promise 是一个构造函数,用来封装异步操作并可以获取其成功或失败的结果。
一个 Promise 必然处于以下几种状态之一:
1、待定(pending):初始状态,既没有被兑现,也没有被拒绝。
2、已兑现(fulfilled):意味着操作成功完成。
3、已拒绝(rejected):意味着操作失败。
Promise 的使用:
1、Promise 构造函数:new Promise((resolve, reject)=>{})
2、Promise.prototype.then 方法
3、Promise.prototype.catch 方法
一个简单案例:
let p = new Promise(function (resolve, reject) {
// 使用 setTimeout 模拟请求数据库数据操作
setTimeout(function () {
// 这个异步请求数据库数据操作是否正确返回数据
let isRight = true;
if (isRight) {
let data = '数据库中的数据';
// 设置 Promise 对象的状态为操作成功
resolve(data);
} else {
let err = '数据读取失败!'
// 设置 Promise 对象的状态为操作失败
reject(err);
}
}, 1000);
});
p.then(function (value) {
console.log(value);
}, function (reason) {
console.error(reason);
})
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
2、Promise 封装读取文件
// 使用 nodejs 的 fs 读取文件模块
const fs = require('fs');
const p = new Promise(function (resolve, reject) {
fs.readFile('./resources/为学.txt', (err, data) => {
// err 是一个异常对象
if (err) reject(err);
// 如果成功
resolve(data);
})
})
p.then(function (value) {
// 转为字符串输出
console.log(value.toString());
}, function (reason) {
console.log('读取失败!!');
})
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
3、Promise 封装 Ajax 请求
const p = new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open('get', 'https://api.apiopen.top/getJoke');
xhr.send();
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status >= 200 && xhr.status < 300) {
// 成功
resolve(xhr.response);
} else {
// 失败
reject(xhr.status);
}
}
}
});
// 指定回调
p.then(function (value) {
console.log(value);
}, function (reason) {
console.error(reason);
})
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
4、Promise.prototype.then 方法
先复习一下一个 Promise 的三种状态:
1、待定(pending):初始状态,既没有被兑现,也没有被拒绝。
2、已兑现(fulfilled):意味着操作成功完成。
3、已拒绝(rejected):意味着操作失败。
Promise.prototype.then 方法返回的结果依然是 Promise 对象,对象状态由回调函数的执行结果决定。
具体情况如下:
1、若 then 方法没有返回值,则 then 方法返回的对象的状态值为成功 fulfilled,返回结果值为 undefined
const p = new Promise((resolve, reject) => {
setTimeout(() => {
// resolve('用户数据')
reject('出错了');
}, 1000);
})
// 未设定返回值
const res = p.then((value) => {
console.log(value);
}, (reason) => {
console.warn(reason);
})
// 打印 then 方法的返回值
console.log(res);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
2、如果回调函数中返回的结果是非 Promise 类型的属性,则 then 方法返回的对象,其状态为成功(fulfilled),返回结果值取决于 then 方法所执行的是哪个函数(resolve 或 reject)。
const p = new Promise((resolve, reject) => {
setTimeout(() => {
// resolve('用户数据')
reject('出错了');
}, 1000);
})
// 返回的非 Promise 对象
const res = p.then((value) => {
console.log(value);
return '成功了!!';
}, (reason) => {
console.warn(reason);
return '出错啦!!'
})
// 打印 then 方法的返回值
console.log(res);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
3、如果回调函数中返回的结果是 Promise 类型(return new Promise()),则 then 方法返回的 Promise 对象状态与该返回结果的状态相同,返回值也相同。
const p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('用户数据')
// reject('出错了');
}, 1000);
})
const res = p.then((value) => {
console.log(value);
// 返回 Promise 对象
return new Promise((resolve, reject) => {
resolve('(1)成功了!!!');
// reject('(1)出错了!!!')
})
}, (reason) => {
console.warn(reason);
return new Promise((resolve, reject) => {
// resolve('(2)成功了!!!');
reject('(2)出错了!!!')
})
})
// 打印 then 方法的返回值
console.log(res);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
4、如果回调函数中返回的结果是 throw 语句抛出异常,则 then 方法的对象的状态值为 rejected,返回结果值为 throw 抛出的字面量或者 Error 对象。
const p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('用户数据');
}, 1000);
});
const res = p.then((value) => {
console.log(value);
return new Promise((resolve, reject) => {
throw new Error('错误了!!');
})
});
// 打印结果
console.log(res);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
5、链式调用
Promise.prototype.then 方法返回的结果还是 Promise 对象,这意味着我们可以继续在该结果上使用 then 方法,也就是链式调用。
const p = new Promise(resolve=>{}, reject=>{});
p.then(value=>{}, reason=>{})
.then(value=>{}, reason=>{})
.then(value=>{}, reason=>{})
...
- 1
- 2
- 3
- 4
- 5
6、链式调用练习-多个文件读取
const fs = require('fs');
let p = new Promise((resolve, reject) => {
fs.readFile('./resources/users.md', (err, data) => {
// 传给下一轮文件读取操作
resolve(data);
})
});
p.then(value => {
return new Promise((resolve, reject) => {
// value 为第一次读取的文件数据,data 为第二次(当前)读取的数据
fs.readFile('./resources/orders.md', (err, data) => {
// 将上轮读取结果和本轮合并传到下一轮轮读取操作
resolve([value, data]);
});
});
}).then(value => {
return new Promise((resolve, reject) => {
fs.readFile('./resources/goods.md', (err, data) => {
// value 为上一轮传递过来的文件数据数组
value.push(data);
// 传给下一轮操作
resolve(value);
});
});
}).then(value => {
// 合并数组元素,输出
console.log(value.join('\n'));
});
- 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
- 26
- 27
- 28
- 29
- 30
7、Promise.prototype.catch
catch() 方法返回一个 Promise,并且处理拒绝的情况。它的行为与调用 Promise.prototype.then(undefined, onRejected) 相同。
即:
obj.catch(onRejected);
- 1
等同于:
obj.then(undefined, onRejected);
- 1
语法:
p.catch(onRejected);
p.catch(function(reason) {
// 拒绝
});
- 1
- 2
- 3
- 4
- 5
举例:
var p1 = new Promise(function (resolve, reject) {
resolve('Success');
});
p1.then(function (value) {
console.log(value); // "Success!"
throw 'oh, no!';
}).catch(function (e) {
console.log(e); // "oh, no!"
}).then(function () {
console.log('有 catch 捕获异常,所以这句输出');
}, function () {
console.log('没有 catch 捕获异常,这句将不会输出');
});
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
输出结果:
Success
oh, no!
有 catch 捕获异常,所以这句输出
- 1
- 2
- 3
-
相关阅读:
基于LLVM13 Enzyme 安装
采集EtherNET/IP转Profinet在西门子plc中的应用
MyBatis开发的详细步骤
postgresql简单sql
使用drawio的图层构建更强大的图表
前端表单滑块验证码开发
LeetCode 热题 HOT 100 第五十八天 226. 翻转二叉树 简单题 用python3求解
【VMware虚拟机使用记录】—— 虚拟机开启失败的问题分析及解决方法
#LLM入门|Prompt#1.9_总结_Summary
数据抓取使用爬虫ip常见问题解决方法
-
原文地址:https://blog.csdn.net/weixin_55608297/article/details/128087829