ES6新特性(下)
- let和const命令
- es6的模板字符串
- 增强的函数
- 解构赋值
- 扩展的字符串、对象、数组功能
- Symbol
- Map和Set
- 迭代器和生成器
- Promise对象
- Proxy对象
- async的用法
- 类 class
- 模块化实现
参考链接:
1-8章节请看→ES6新特性(上):https://wuyea.top/posts/2174201661.html
9. Promise对象
什么是Promise
Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了Promise
对象。
所谓Promise
,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。
各种异步操作都可以用同样的方法进行处理,比如axios内部就是用Promise实现的。
Promise
对象有以下两个特点。
- 对象的状态不受外界影响。
Promise
对象代表一个异步操作,有三种状态:pending
(进行中)、fulfilled
(已成功)和rejected
(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是Promise
这个名字的由来,它的英语意思就是“承诺”,表示其他手段无法改变。 - 一旦状态改变,就不会再变,任何时候都可以得到这个结果。
Promise
对象的状态改变,只有两种可能:从pending
变为fulfilled
和从pending
变为rejected
。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 resolved(已定型)。如果改变已经发生了,你再对Promise
对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。
基本用法
基础用法示例:
// Promise接收一个函数作为参数, 默认的relove和reject分别是两个函数 let pro = new Promise(function(resolve,reject) { // .....一坨代码 来模拟异步 let res = { code:200, data:{ name: 1, }, error: "失败了" } setTimeout(() => { if(res.code === 200){ resolve(res.data); }else{ reject('失败',res.error); } }, 1000); }); console.log(pro); // then能接收两个回调函数,一个成功的,一个失败的 pro.then((val)=>{ console.log(val); }, (err)=>{ console.log(err); })
封装传参示例:
// 对示例1中的代码进行改造。为什么改造? 因为上述代码 不能传参 function timeout(ms) { return new Promise((resolve, reject) => { setTimeout(() => { resolve('hello world') }, ms); }) } timeout(1000).then((value) => { console.log(value); })
Promise对象的实例:
const getJSON = function (url) { return new Promise((resolve, reject) => { const xhr = new XMLHttpRequest(); xhr.open('GET', url); xhr.onreadystatechange = handler; xhr.responseType = 'json'; xhr.setRequestHeader('Accept', 'application/json'); xhr.send(); // 发送 function handler() { console.log(this.readyState); if (this.readyState !== 4) { return; } if (this.status === 200) { resolve(this.response); } else { reject(new Error(this.statusText)); } } }) } getJSON('https://free-api.heweather.net/s6/weather/now?location=beijing&key=4693ff5ea653469f8bb0c29638035976') .then((json) => { console.log(json); }, function (error) { console.error(error); })
Promise对象中的相关方法
then() 方法
then()
方法的第一个参数是 resolve
的回调函数,第二个参数是可选的,是 reject
状态的回调函数。
then()
返回一个新的 Promise
实例,可以采用链式编程。
getJSON('https://www.oschina.net/action/ajax/get_tool_ad')
.then(data => {
console.log(data);
}).catch(err=>{
console.log(err);
})
// catch(err=>{}) 等价于 then(null, err=>{})
resolve() 方法
resolve()
方法能将现有的任何对象转换成 Promise
对象。
let p = Promise.resolve('foo');
// 等价于 new Promise(resolve=>resolve('foo'));
console.log(p);
p.then((val)=>{
console.log(val);
})
reject() 方法
Promise.reject(reason)
方法也会返回一个新的 Promise 实例,该实例的状态为rejected
。
const p = Promise.reject('出错了');
// 等同于
const p = new Promise((resolve, reject) => reject('出错了'))
p.then(null, function (s) {
console.log(s) // 出错了
});
all() 方法
Promise
的 all()
方法提供了并行执行异步操作的能力,并且在所有异步操作执行完后才执行回调。
let Promise1 = new Promise(function (resolve, reject) {})
let Promise2 = new Promise(function (resolve, reject) {})
let Promise3 = new Promise(function (resolve, reject) {})
let p3 = Promise.all([Promise1, Promise2, Promise3])
p3.then(() => {
// 三个都成功 则成功
}).catch(err => {
// 只要有失败,都失败
})
// 应用场景:一些游戏类的素材比较多的应用,打开网页时,预先加载需要用到的各种资源如图片、flash以及各种静态文件。
// 所有的都加载完后,我们再进行页面的初始化。
race() 方法
Promise.race()
方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例。
const p = Promise.race([p1, p2, p3]);
上面代码中,只要p1
、p2
、p3
之中有一个实例率先改变状态,p
的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给p
的回调函数。
race()
的应用场景:比如我们可以用 race
给某个异步请求设置超时时间,并且在超时后执行相应的操作。
//请求某个图片资源
function requestImg(imgSrc) {
return new Promise((resolve, reject) => {
const img = new Image();
img.onload = function () {
resolve(img);
}
img.src = imgSrc;
});
}
//延时函数,用于给请求计时
function timeout() {
var p = new Promise((resolve, reject) => {
setTimeout(() => {
reject(new Error('图片请求超时'));
}, 3000);
});
return p;
}
// race([]) 中是一个列表,第一个是要请求的,第二个是计时的。
// 比如示例中请求未超过3秒请求图片的先返回成功了就进入then,否则进入catch
Promise.race([requestImg('https://img2.baidu.com/it/u=1310029438,409566289&fm=253&app=138&size=w931&n=0&f=JPEG&fmt=auto?sec=1735750800&t=038096a189bcdc2e2497bebb8aeda910'), timeout()]).then((data) => {
console.log(data);
document.body.appendChild(data);
}).catch((err) => {
console.log(err);
});
finally()
finally()
方法用于指定不管 Promise
对象最后状态如何,都会执行的操作。该方法是 ES2018 引入标准的。
promise
.then(result => {···})
.catch(error => {···})
.finally(() => {···});
上面代码中,不管 promise
最后的状态,在执行完 then
或 catch
指定的回调函数以后,都会执行 finally
方法指定的回调函数。
10. Proxy对象
概述
Proxy 用于修改某些操作的默认行为,等同于在语言层面做出修改,所以属于一种“元编程”(meta programming),即对编程语言进行编程。
Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。Proxy 这个词的原意是代理,用在这里表示由它来“代理”某些操作,可以译为“代理器”。
11. async的用法
含义
ES2017 标准引入了 async 函数,使得异步操作变得更加方便。
async 函数是什么?一句话,它就是 Generator 函数的语法糖。
重点概要:async函数返回一个Promise对象,可以使用then方法添加回调函数。
但函数执行的时候,一旦遇到await就会先返回,等到异步操作完成,再接着执行函数体内后面的语句。
详细说明:async
函数对 Generator 函数的改进,体现在以下四点:
内置执行器。
Generator 函数的执行必须靠执行器,所以才有了
co
模块,而async
函数自带执行器。也就是说,async
函数的执行,与普通函数一模一样,只要一行。更好的语义。
async
和await
,比起星号和yield
,语义更清楚了。async
表示函数里有异步操作,await
表示紧跟在后面的表达式需要等待结果。更广的使用性。
co
模块约定,yield
命令后面只能是 Thunk 函数或 Promise 对象,而async
函数的await
命令后面,可以是 Promise 对象和原始类型的值(数值、字符串和布尔值,但这时会自动转成立即 resolved 的 Promise 对象)。返回值是 Promise。
async
函数的返回值是 Promise 对象,这比 Generator 函数的返回值是 Iterator 对象方便多了。你可以用then
方法指定下一步的操作。进一步说,
async
函数完全可以看作多个异步操作,包装成的一个 Promise 对象,而await
命令就是内部then
命令的语法糖。
使用示例
/*
Promise 对象的状态变化
async函数返回的 Promise 对象,必须等到内部所有await命令后面的 Promise
对象执行完,才会发生状态改变,除非遇到return语句或者抛出错误。
也就是说,只有async函数内部的异步操作执行完,才会执行then方法指定的回调函数。
*/
function requesetData(url) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open('GET', url);
xhr.onreadystatechange = handler;
xhr.responseType = 'json';
xhr.setRequestHeader('Accept', 'application/json');
// 发送
xhr.send();
function handler() {
if (this.readyState === 4) {
if (this.status === 200) {
resolve(this.response);
} else {
reject(new Error(this.statusText));
}
}
}
});
}
async function getNowWeather(url) {
// 这里是请求一天的天气
let response = await requesetData(url);
// 假设这个data是请求3-7天的天气
let data = await response.HeWeather6;
// return是把前两次请求的数据进行了合并,返回后才会执行then函数
return data[0].now;
}
getNowWeather(
'https://free-api.heweather.net/s6/weather/now?location=beijing&key=4693ff5ea653469f8bb0c29638035976')
.then((nowData) => {
console.log(nowData);
})
12. 类 class
类的基本使用
ES6 提供了更接近传统语言的写法,引入了 Class(类)这个概念,作为对象的模板。通过class
关键字,可以定义类。
基本上,ES6 的class
可以看作只是一个语法糖,它的绝大部分功能,ES5 都可以做到,新的class
写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。
class Person {
// 实例化的时候自动调用该方法。
// 一个类必须有constructor()方法,如果没有显式定义,一个空的constructor()方法会被默认添加。
constructor(name, age) {
this.name = name;
this.age = age;
}
sayName() { // 类的方法,方法之间不需要写,号隔开
return this.name;
}
}
// 也可以通过Object.assign()方法一次性向类中添加多个方法
Object.assign(Person.prototype, {
sayAge() {
return this.age;
}
sayName() {
return this.name;
}
})
// 错误
// let p1 = Person('wuye', 18)
// 正确
// 类在实例化的时候必须加上new
let p1 = new Person('wuye', 18);
console.log(p1);
类的继承
Class 可以通过 extends
关键字实现继承,让子类继承父类的属性和方法。
class Animal {
constructor(name, age) {
this.name = name;
this.age = age;
}
sayName() {
return this.name;
}
sayAge() {
return this.age;
}
}
class Dog extends Animal{
constructor(name, age, color) {
super(name,age); // Animal.call(this, name, age)
this.color = color;
}
// 子类自己的方法
sayColor() {
return `${this.name}的颜色是:${this.color}。`;
}
// 重写父类的方法
sayName() {
return this.name + super.sayAge() + this.color;
}
}
let d1 = new Dog('小黄', 8, 'yellow');
console.log(d1.sayColor());
console.log(d1.sayName());
13. 模块化实现
ES6模块功能主要由两个命令构成:export
和import
。
export
命令用于规定模块的对外接口,import
命令用于输入其它模块提供的功能。
一个模块就是一个独立的文件。该文件内部的所有变量,外部无法获取。如果你希望外部能够读取模块内部的某个变量,就必须使用export
关键字输出该变量。
下面是示例,先来看一下文件结构:
demo
¦-- modules
¦ Ⅼ- index.js
Ⅼ- hello.html
export命令:
index.js
文件内容如下:
export const name = 'wuye';
export const age = 18;
export function sayName() {
return 'my name is wuye';
}
// 上面的写法也可以改为如下写法:
const name = 'wuye';
const age = 18;
function sayName() {
return 'my name is wuye';
}
export {
name, age, sayName
}
// export default在一个js文件中只能使用一次,普通的export可以使用多次
const obj = {foo:'foo'}
export default obj;
import命令
hello.html
文件内容如下:
<html>
<head></head>
<body>
<script type="module">
// 这里的type类型一定要改为 module!!!
// 运行的时候需要在服务器上运行才有效果。vscode可以使用插件"Live Server"
// import {name,age,sayName} from './modules/index.js'
// console.log(name);
// 如果要导入default的,使用如下:
// import obj,{name,age,sayName} from './modules/index.js'
// 也可以全部导入
import * as f from './modules/index.js'
console.log(f.default) // 输出obj对象
</script>
</body>
</html>