2.14 Promise
2.14.1 什么是Promise
Promise是ES6引入的异步编程的新解决方案。语法上Promise是一个构造函数,用来封装异步操作并可以获取其成功或失败的结果。
- Promise 构造函数: Promise (excutor) {}
- Promise.prototype.then 方法
- Promise.prototype.catch 方法
2.14.2 promise的好处
1.可以避免多层异步调用嵌套问题(即回调地狱)
2.Promise 对象提供了简洁的API,使得控制异步操作更加容易(js执行机制导致的异步问题)
2.14.3 promise的三种状态
- pending: 等待中,或者进行中,表示还没有得到结果
- resolved(Fulfilled): 已经完成,表示得到了我们想要的结果,可以继续往下执行
- rejected: 也表示得到结果,但是由于结果并非我们所愿,因此拒绝执行
这三种状态不受外界影响,而且状态只能从pending改变为resolved或者rejected,并且不可逆
2.14.4 promise的用法
- promise的实例方法
- then()得到异步任务的正确结果
- catch()获取异常信息
- finally()成功与否都会执行(尚且不是正式标准)
then方法可以接受两个函数,第一个函数为promise状态为成功的回调函数,第二个函数为promise状态为失败的回调函数(可以不写,一般用catch方法捕获promise状态为失败的异常信息)
- promise的对象方法(p1,p2,p3为promise的实例对象)
- Promise.all()并发处理多个异步任务,所有任务都执行完成才能得到结果
Promise.all([p1, p2, p3]).then((result) => {
console.log(result);
})
- Promise.race()并发处理多个异步任务,只要有一个任务完成就能得到结果
Promise.race ( [p1,p2,p3] ).then ((result) => {
console. log (result)
})
2.14.5 promise的特点
- 在Promise对象的构造函数中,将一个函数作为第一个参数。
- 而Promise对象的构造函数的第一个参数中的这个函数,就是
用来处理Promise的状态变化
,这个函数的第一个参数
表示promise的状态为成功
,第二个参数
表示promise的状态为失败
,这两个参数(名字可以自己命名)都为一个函数
,他们的作用分别是将promise状态修改为resolved(成功)
和rejected(失败)
。
const p = new Promise(function(resolve,reject) {
setTimeout(function() {
// let data = '数据库中的用户数据';
// resolve
// resolve(data);
let err = '数据读取失败';
reject(err);
}, 1000)
});
// 调用promise对象的then方法
p.then(function(value) {
console.log(value);
}, function(reason) {
console.log(reason);
});
Promise读取文件
// 1. 引入fs模块
const fs = require('fs');
const { writer } = require('repl');
// 2. 调用方法
// fs.readFile('./resources/为学.md', (err, data) => {
// // 如果失败 则抛出错误
// if (err) throw err;
// // 如果没有出错 则输出内容
// console.log(data.toString());
// });
// 3. 使用Promise封装
const p = new Promise(function (resolve, reject) {
fs.readFile("./resources/为学.md", (err, data) => {
// 判断如果失败
if (err) reject(err);
// 如果成功
resolve(data);
});
});
p.then(function (value) {
console.log(value.toString());
}, function (reason) {
console.log("读取失败!!!");
})
打开终端,输入:node 02/Promise读取文件.js
Promise封装AJAX
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>发送AJAX</title>
</head>
<body>
<script>
// 接口地址:https://api.apiopen.top/getJoke
const p = new Promise((resolve, reject) => {
// 1. 创建对象
const xhr = new XMLHttpRequest();
// 2. 初始化
xhr.open("GET", "https://api.apiopen.top/getJoke");
// 3. 发送
xhr.send();
// 4. 绑定事件 处理响应结果
xhr.onreadystatechange = function () {
// 判断
if (xhr.readyState === 4) {
// 判断响应状态码 200-299
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);
})
</script>
</body>
</html>
2.14.6 Promise的then方法
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Promise.prototype.then</title>
</head>
<body>
<script>
// 创建promise对象
const p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('用户数据');
}, 1000);
});
// 调用then方法 then方法的返回结果时Promise对象 对象状态由回调函数阿执行结果决定
// 1. 如果回调函数中返回的结果是 非promise类型的属性 状态为成功 返回值为对象的成功的值
// const result = p.then(value => {
// console.log(value);
// 1. 非promise类型的属性
// return 123;
// 2. 是promise对象
// return new Promise((resolve, reject) => {
// // resolve('ok');
// reject('error');
// });
// 3. 抛出错误
// throw '出错啦!';
// }, reason => {
// console.warn(reason);
// })
// console.log(result);
// 链式调用
p.then(value => {
}).then(value => {
})
</script>
</body>
</html>
Promise实践-读取多个文件
// 引入fs模块
const fs = require("fs");
// fs.readFile('./resources/为学.md', (err, data1) => {
// fs.readFile('./resources/插秧诗.md', (err, data2) => {
// fs.readFile('./resources/观书有感.md', (err, data3) => {
// let result = data1 + '\r\n' + data2 + '\r\n' + data3;
// console.log(result);
// });
// });
// });
// 使用promise实现
const p = new Promise((resolve, reject) => {
fs.readFile("./resources/为学.md", (err, data) => {
resolve(data);
});
});
p.then(value => {
return new Promise((resolve, reject) => {
fs.readFile("./resources/插秧诗.md", (err, data) => {
resolve([value, data]);
});
})
}).then(value => {
return new Promise((resolve, reject) => {
fs.readFile("./resources/观书有感.md", (err, data) => {
// 压入
value.push(data);
resolve(value);
});
})
}).then(value => {
console.log(value.join('\r\n'));
})
2.14.7 Promise的catch方法
相当于then方法没有第一个函数参数, 处理失败状态
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>catch方法</title>
</head>
<body>
<script>
const p = new Promise((resolve, reject) => {
setTimeout(() => {
// 设置p对象的状态为失败 并设置失败的值
reject("出错啦!");
}, 1000)
});
// p.then(function(value){}, function(reaosn){
// console.log(reason);
// });
p.catch(function(reason) {
console.log(reason);
})
</script>
</body>
</html>
2.15 Set
ES6提供了新的数据结构Set(集合)。它类似于数组,但成员的值都是唯
一的,集合实现了iterator接口,所以可以使用「扩展运算符」和「for…of…」进行遍历,集合的属性和方法:
- size 返回集合的元素个数
- add 增加一个新元素,返回当前集合
- delete 删除元素,返回boolean 值
- has 检测集合中是否包含某个元素,返回boolean值
- clear 清空集合,返回undefined
// 创建一个空集合
let s = new Set();
// 创建一个非空集合
let s1 = new Set([1, 2, 3, 1, 2, 3]);
// 返回集合元素个数
console.log(s2.size);
// 添加新的元素
s2.add('喜事儿');
// 删除元素
s2.delete('坏事儿');
// 检测是否存在某个值
console.log(s2.has('糟心事'));
// 清空集合
s2.clear();
2.16 Map
ES6提供了Map数据结构。它类似于对象,也是键值对的集合。但是“键”
的范围不限于字符串,各种类型的值(包括对象)都可以当作键。Map也实现
了iterator接口,所以可以使用[扩展运算]和[for…of…]进行遍历。Map
的属性和方法:
- size 返回Map的元素个数
- set 增加一个新元素,返回当前Map
- get 返回键名对象的值
- has 检测Map中是否包含某个元素,返回boolean值
- clear 清空集合,返回undefined
// 创建一个空map
let m = new Map();
// 创建一个非空map
let m2 = new Map([
['name','尚硅谷'],
['slogon','不断提高行业标准']
]);
//属性和方法
//获取映射元素的个数
console. log(m2.size);
//添加映射值
console.log(m2.set('age'
, 6));
//获取映射值
console.log(m2.get('age'));
//检测是否有该映射
console.log(m2.has('age'));
//清除
console.log (m2.clear());
2.17 class类
ES6提供了更接近传统语言的写法,引入了Class (类)这个概念,作为对
象的模板。通过class关键字,可以定义类。基本上,ES6的class可以看作只是一个语法糖,它的绝大部分功能,ES5都可以做到,新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而己。
知识点:
- class声明类
- constructor定义构造函数初始化
- extends继承父类
- super调用父类构造方法
- static定义静态方法和属性
- 父类方法可以重写
eg:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>类声明</title>
</head>
<body>
<script>
// ES5
// 手机
// function Phone(brand, price) {
// this.brand = brand;
// this.price = price;
// }
// // 添加方法
// Phone.prototype.call = function() {
// console.log("我可以打电话!!");
// }
// // 实例化对象
// let Huawei = new Phone('华为', 5999);
// Huawei.call();
// console.log(Huawei);
// class
class Phone {
// 构造方法 名字不能修改
constructor(brand, price) {
this.brand = brand;
this.price = price;
}
// 方法必须使用该语法,不能使用ES5的对象完整形式
call() {
console.log("我可以打电话!!");
}
}
let onePlus = new Phone("1 + ", 1999);
console.log(onePlus);
</script>
</body>
</html>
eg:类的静态成员
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>静态成员</title>
</head>
<body>
<script>
// function Phone() {
// }
// Phone.name ='手机';
// Phone.change = function() {
// console.log("我可以改变世界");
// }
// Phone.prototype.size = '5.5inch';
// let nokia = new Phone();
// console.log(nokia.name); // undefined
// // nokia.change();
// console.log(nokia.size); // 5.5inch
class Phone {
static name = '手机';
static change() {
console.log("我可以改变世界");
}
}
let nokia = new Phone();
console.log(nokia.name); // undefined
console.log(Phone.name); // 手机
</script>
</body>
</html>
eg:ES5构造函数的继承
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>对象继承</title>
</head>
<body>
<script>
// ES5构造函数的继承
// 手机
function Phone(brand, price) {
this.brand = brand;
this.price = price;
}
Phone.prototype.call = function() {
console.log("我可以打电话");
}
// 智能手机
function SmartPhone(brand, price, color, size) {
Phone.call(this, brand, price);
this.color = color;
this.size = size;
}
// 设置子级构造函数的原型
SmartPhone.prototype = new Phone;
SmartPhone.prototype.constructor = SmartPhone;
// 声明子类的方法
SmartPhone.prototype.photo = function() {
console.log("我可以拍照");
}
SmartPhone.prototype.playGame = function() {
console.log("我可以玩游戏");
}
const chuizi = new SmartPhone('锤子', 2499, '黑色', '5.5inch');
console.log(chuizi);
</script>
</body>
</html>
eg:ES6类继承和子类对父类方法的重写
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>类继承-2</title>
</head>
<body>
<script>
class Phone {
// 构造方法
constructor(brand, price) {
this.brand = bramd;
this.price = price;
}
// 父类的成员属性
call() {
console.log("我可以打电话!!");
}
}
class SmartPhone extends Phone {
// 构造方法
constructor(brand, price, color, size) {
super(brand, price);
this.color = color;
this.size = size;
}
photo() {
console.log("拍照");
}
playGame() {
console.log("玩游戏");
}
// 子类对父类方法的重写
call() {
console.log("我可以进行视频通话");
}
}
const xiaomi = new SmartPhone('小米', 799, '黑色', '4.7inch');
// console.log(xiaomi);
xiaomi.call();
xiaomi.photo();
xiaomi.playGame();
</script>
</body>
</html>
class中的getter和setter设置
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>get 和 set</title>
</head>
<body>
<script>
// get 和 set
class Phone {
get price() {
console.log("价格属性被读取了");
return "i love you"
}
set price(newVal) {
console.log('价格属性被修改了');
}
}
// 实例化对象
let s = new Phone();
// console.log(s.price);
s.price = 'free'
</script>
</body>
</html>
2.18 数值扩展
2.18.1 二进制和八进制
ES6提供了二进制和八进制数值的新的写法,分别用前缀0b和0o表示。
2.18.2 Number.isFinite()与Number.isNaN()
Number.isFinite()用来检查一个数值是否为有限的
Number.isNaN()用来检查一个值是否为NaN
2.18.3 Number.parselnt()与Number.parseFloat()
ES6将全局方法parseInt和parseFloat,移植到Number对象上面,使用不变。
2.18.4 Math.trunc
用于去除一个数的小数部分,返回整数部分。
2.18.5 Number.isInteger
Number.isInteger()用来判断一个数值是否为整数
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>数值扩展</title>
</head>
<body>
<script>
// 0. Number.EPSILON 是JavaScript表示的最小精度
// EPSILON 属性的值接近于2.220446049250310808472633361816E-16
console.log(0.1 + 0.2 === 0.3);
function equal(a, b) {
if (Math.abs(a - b) < Number.EPSILON) {
return true;
} else {
return false;
}
}
console.log(0.1 + 0.2 === 0.3);
console.log(equal(0.1 + 0.2, 0.3));
// 1. 二进制和八进制
let b = 0b1010;
let o = 0o777;
let d = 100;
let x = 0xff;
console.log(x);
// 2. Number.isFinite检测一个数值是否为有限数
console.log(Number.isFinite(100));
console.log(Number.isFinite(100/0));
console.log(Number.isFinite(Infinity));
// 3. Number.isNaN检测一个数值是否为NaN
console.log(Number.isNaN(123));
// 4. Number.parseInt Number.parseFloat字符串转整数
console.log(Number.parseInt('521123love'));
console.log(Number.parseFloat('3.1415926神奇'));
// 5. Number.isInteger 判断一个数是否为整数
console.log(Number.isInteger(5));
console.log(Number.isInteger(2.5));
// 6. Math.trunc 将数字的小数部分抹掉
console.log(Math.trunc(3.5));
// 7. Math.sign 判断一个数到底是正数 负数 还是0
console.log(Math.sign(100)); // 1
console.log(Math.sign(0)); // 0
console.log(Math.sign(-2000)); // -1
</script>
</body>
</html>
2.19 对象扩展
ES6新增了一些Object对象的方法
- Object.is 比较两个值是否严格相等,与「===」 行为基本一致(+0与NaN)
- Object.assign 对象的合并,将源对象的所有可枚举属性,复制到目标对象
- proto_、setPrototypeOf、setPrototypeOf可以直接设置对象的原型
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>对象方法扩展</title>
</head>
<body>
<script>
// 1. Object.is 判断两个值是否完全相等
console.log(Object.is(200, 200)); // true
console.log(Object.is(NaN, NaN)); // true
console.log(NaN === NaN); // ===
// 2. Object.assign对象的合并
const config1 = {
host:'localhost',
port: 3306,
name: 'root',
pass: 'root',
test: 'test'
};
const config2 = {
host:'http://atguigu.com',
port: 33060,
name: 'atguigu.com',
pass: 'iloveyou',
test2: 'test2'
};
console.log(Object.assign(config1, config2));
// 3. Object.setPrototypeOf Object.getPrototypeOf
const school = {
name: 'yaya'
}
const cities = {
xiaoqu: ['北京', '上海', '深圳']
}
Object.setPrototypeOf(school, cities);
console.log(Object.getPrototypeOf(school));
console.log(school);
</script>
</body>
</html>
2.20 模块化
模块化是指将一个大的程序文件,拆分成许多小的文件,然后将小文件组合起来。
2.20.1 模块化的好处
模块化的优势有以下几点:
- 防止命名冲突
- 代码复用
- 高维护性
2.20.2 模块化规范产品
ES6之前的模块化规范有:
- CommonJS => NodeJS、 Browserify
- AMD => requireJS ,
- CMD => seaJS
2.20.3 ES6模块化语法
模块功能主要由两个命令构成:export和import。
- export 命令用于规定模块的对外接口
- import命令用于输入其他模块提供的功能
- 分别暴露
// 分别暴露
export let school = 'yaya'
export function teach() {
console.log("我们可以教给你开发技能");
}
- 统一暴露
// 统一暴露
let school = 'yaya'
function findHappy() {
console.log('我可以帮助你得到快乐');
}
export { school, findHappy };
- 默认暴露
// 默认暴露
export default {
school: 'yaya',
change: function () {
console.log("我可以帮助你");
}
}
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ES6模块化</title>
</head>
<body>
<script type="module">
// 引入m1.js模块内容 将暴露的数据存到m1中
import * as m1 from "./m1.js";
console.log(m1);
// 引入m2.jhs模块内容
import * as m2 from "./m2.js";
console.log(m2);
// 引入m3.js模块内容
import * as m3 from "./m3.js"
console.log(m3);
</script>
</body>
</html>
ES6引入模块数据语法
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ES6模块化</title>
</head>
<body>
<script type="module">
// 1. 通用的导入方式
// 引入m1.js模块内容
// import * as m1 from "./m1.js";
// console.log(m1);
// // 引入m2.jhs模块内容
// import * as m2 from "./m2.js";
// console.log(m2);
// // 引入m3.js模块内容
// import * as m3 from "./m3.js"
// console.log(m3);
// 2. 解构赋值形式
// import {school, teach} from './m1.js';
// import {school as sc, findHappy} from "./m2.js";
// import {default as m3} from "./m3.js";
// // console.log(school);
// // console.log(teach);
// // console.log(sc, findHappy);
// console.log(m3);
// 3. 简便形式 针对默认暴露
// import m3 from "./m3.js";
// console.log(m3);
</script>
<script src="./js/app.js" type="module"></script>
</body>
</html>
app.js
// 入口文件
// 模块引入
import * as m1 from "./m1.js"
import * as m2 from "./m2.js"
import * as m3 from "./m3.js"
2.21 babel对ES6模块化代码转换
npm init --yes 初始化
npm i babel-cli babel-preset-env browserify -D
npx babel src/js -d dist/js --presets=babel-preset-env
npx browserify dist/js/app.js -o dist/bundle.js 打包
运行完之后,会多出一个文件夹:
bundle.js
文件里面就是转化之后的ES5代码
打开浏览器,控制台输出跟之前一样
修改app.js,如下:
// 入口文件
// 模块引入
import * as m1 from "./m1.js"
import * as m2 from "./m2.js"
import * as m3 from "./m3.js"
// console.log(m1);
// console.log(m2);
// console.log(m3);
m1.teach();
m2.findHappy();
m3.default.change();
修改完文件之后,要重新进行打包:
npx babel src/js -d dist/js --presets=babel-preset-env
npx browserify dist/js/app.js -o dist/bundle.js 打包
打开浏览器,查看控制台:
2.22 ES6模块化引入NPM包
通过jquery包对home.html的背景颜色进行修改
安装jquery包:
npm i jquery
然后重新打包:
npx babel src/js -d dist/js --presets=babel-preset-env
npx browserify dist/js/app.js -o dist/bundle.js
app.js
// 修改背景颜色为粉色
import $ from 'jquery'; // const $ = require("jquery");
$('body').css('background', 'pink');