下面所要介绍的是关于 ES6 进阶的内容,对于 ES6 新入门的玩家可以先来尝试看看ES6基础部分的内容 ES6干货(上)
一、Symbol
Symbol 是什么?
- ES6 新增的一个数据类型
- 利用 Symbol() 可创建了一个符号
- 符号的设计是给对象新增私有属性,并只能在对象内部进行访问
Symbol 特点
- 没有字面量的创建写法,必须用 Symbol 创建
const sym= Symbol();
- 新的基础数据类型,使用
typeof
返回symbol
const sym= Symbol(); console.log(typeof sym); // symbol
- 每次去调用 Symbol 函数得到的符号永远不会相等,不管符号描述是否相同
const sym1 = Symbol("sym"); const sym2 = Symbol("sym"); console.log(sym1,sym2); // Symbol(sym) Symbol(sym) console.log(sym1 === sym2); // false
- 符号可以作为对象的属性名使用,这种属性名叫符号属性
const sym = Symbol("sym"); const obj = { a : 1, b : 2, [sym] : 3 // 符号属性 [sym] } console.log(obj); // {a: 1, b: 2, Symbol(sym): 3}
- 符号属性不能被枚举
const sym1 = Symbol("sym1"); const sym2 = Symbol("sym2"); const obj = { a : 1, b : 2, [sym1] : 3, [sym2] : 4, } for(const prop in obj) console.log(prop); // a b console.log(Object.keys(obj)); // (2) ["a", "b"] console.log(Object.getOwnPropertyNames(obj)); // (2) ["a", "b"] // 要枚举则需使用针对符号属性的方法 getOwnPropertySymbols(obj) console.log(Object.getOwnPropertySymbols(obj)); // (2) [Symbol(sym1), Symbol(sym2)]
- 符号类型无法被隐式转换,数学运算,字符串拼接等
const sym = Symbol(); console.log(sym + 0); // Cannot convert a Symbol value to a number
二、Iterator 迭代器
什么是 Iterator ?
- 什么是迭代?
从一个数据集合中按照一定的顺序,不断的取出数据的过程 - 迭代和遍历有什么区别?
迭代强调是依次取出,不能确定取出的有多少,也不能保证把数据全部取完 - 迭代器
对迭代过程的封装,通常为对象,不同的语言中,表现出来的迭代形式不一样 - 迭代模式
一种设计模式,用于同一迭代的过程,并且规范迭代器的规格 - JavaScript 中的迭代器
迭代器有得到下一个数据的能力 判断是否有后续数据的能力
JavaScript 规定,如果一个对象有 next 方法,并且返回一个对象,就认为这个对象为迭代器 - 作用
为各种 数据结构 提供一个统一简便的访问接口,使数据结构的成员能够按某种次序排列
原生具备 iterator 接口的数据,可用 for of 遍历( 数组 字符串 arguments set容器 map容器 )
迭代器基本结构,迭代返回结果为 遍历器对象 / 指针对象
const IterObj = {
next(){ // next() 用于得到下一个数据
return {
value : "xxx", // 数据值 迭代执行获取数据
done : "xxx" // 状态 判断是否有后续数据,一般为 boolean
}
}
}
/* 这里调用 IterObj.next() 输出的是指针对象 */
模拟迭代器返回指针对象
- 模拟迭代器在这里数据结构类似一个数组
- 开始指向是数据结构的起始位置(索引值
0
的位置) - 第一次调用
next()
指针自动下移,指针自动指向数据结构第一个成员,
next()
返回值一个对象{vlaue: 数据结构第一个数据, done: false}
......
- 最后一次调用
next
下移,指针指向无数据位置(指向索引值为length
的位置),返回{value: undefiend, done: true}
const arr = [1,2,3,4,5];
const iterator = {
i : 0,
next: function() {
var result = {
value : arr[this.i],
done : this.i >= arr.length
}
this.i ++;
return result;
}
}
console.log(iterator); // {i: 0, next: ƒ}
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: 4, done: false}
console.log(iterator.next()); // {value: 5, done: false}
console.log(iterator.next()); // {value: undefined, done: true}
封装迭代器
const arr = [1,2,3,4,5];
function createIterator(arr){
let i = 0;
return {
next(){
var result = {
value : arr[i],
done : i >= arr.length
}
i ++;
return result;
}
}
}
const iter = createIterator(arr);
console.log(iter.next()); // {value: 1, done: false}
console.log(iter.next()); // {value: 2, done: false}
console.log(iter.next()); // {value: 3, done: false}
console.log(iter.next()); // {value: 4, done: false}
console.log(iter.next()); // {value: 5, done: false}
console.log(iter.next()); // {value: undefined, done: true}
Fibonacci 数列迭代器
function createFeiboIterator(){
let prev1 = 1;
let prev2 = 1; //定义前两位
let n = 1; //当前第几位
return {
next(){
let value;
if(n <= 2){
value = 1;
}else{
value = prev1 + prev2; // C = A + B
}
const result = {
value,
done : false //迭代器数据量的多少,只关心是否拿得到下一个数据
}
prev2 = prev1; // B = A
prev1 = value; // A = C
n ++;
return result
}
}
}
const iterator = createFeiboIterator();
console.log(iterator .next()); // {value: 1, done: false}
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: 5, done: false}
可以迭代对象
- 在 ES6 中,如果对象具有知名符号的属性
Symbol.iterator
,则表示对象可以迭代
ES6及之后,数组/类数组对象可以直接创建迭代对象 - 当使用
for of
去遍历某一个数据结构的时候,首先找symbol. iterator
找到了则遍历,未找都则输出xxx is not iterrable
const arr = [1,22,3333,44444,55555];
const iterator = arr[Symbol.iterator]();
console.log(iterator.next()); // {value: 1, done: false}
// for of 遍历可迭代对象
for(const item of arr)
console.log(item); // 1 22 333 4444 55555
三、Generator 生成器
什么是 Generator
- ES6 提供的解决异步编程的方案
- Generrator 函数是一个状态机,内部封装了不同状态的数据
- 生成器就是通过构造函数 Generator 生成遍历器对象/指针对象
- 生成器既是一个迭代器,同时又是一个可迭代的对象
- Generrator 是一个可暂停函数(惰性求值),
yield
可暂停,next()
可启动。每次暂停返回的是yield
后的表达式结果
Generator 函数写法及用法
- 只需要把一般函数变成 Generator 函数(即
function
与函数名之间有 * ) - 内部用
yield
表达式来定义不同的状态,关键字yield
只能在函数内部使用,表示产生一个迭代数据 - Generrator 函数返回值是指针对象,不会执行函数内部逻辑
- 开始调用
next()
方法函数内部逻辑开始执行,遇到yield
表达式暂停,返回指针对象{value: " ", done: " "}
;
每一次调用生成器的next
方法,会从上次暂停的yield
处开始将生成器函数运行到下一个yield
关键字位置 ;直到最后返回的指针对象done: true
; yield
语句返回结果一般为undefined
,当调用next()
方法时传参内容会作为启动时yield
语句的返回值。- 注:Generator 中使用
return
方法,可以提前结束生成器函数,整个的迭代过程提前结束
function *test(){
console.log('第1次执行')
yield 1;
console.log('第2次执行')
yield 2;
console.log('第3次执行')
yield 3;
return "over";
}
const generator = test();
console.log(generator.next()); // 第1次执行 {value: 1, done: false}
console.log(generator.next()); // 第2次执行 {value: 2, done: false}
console.log(generator.next()); // 第3次执行 {value: 3, done: false}
console.log(generator.next()); // {value: "over", done: true}
console.log(generator.next()); // {value: undefined, done: true}
可以看出生成器原理上是封装了的迭代器,相比原生的迭代器他是更可操控的
yield 返回值
- 调用生成的
next
方法时,可以传递参数,传递的参数会交给yield
表达式的返回值 - 执行
next
方法后停留的地方是"终点","起点"在上一个停留的地方,执行的内容也即是从起点到终点过程中的代码 - 即:
next
方法传参后的返回值,是起点yield
语句接收的。
function *test(){
let info = yield 1; // 第一次暂停
console.log(info);
info = yield 2; // 第二次暂停
console.log(info);
return "OVER";
}
const generator = test();
console.log(generator.next()); // {value: 1, done: false}
console.log(generator.next(222)); // 222 {value: 2, done: false}
console.log(generator.next(333)); // 333 {value: "OVER", done: true}
第一次执行 generator.next()
运行至第一次暂停,第一次启动无需传参,因为函数运行至第一次暂停才有yield
表达式语句,即启动时是从下标 0
开始,没有yield
表达式接收返回值 ;
第二次执行 generator.next()
运行至第二次暂停,第二次启动传参 222
,接收参数的yield
表达式是第一次暂停的yield
表达式,后续next
执行同理 ;
Generator 内部调用其它 Generator 函数
/* 在函数内部是调用别的生成器 一定要加上 * */
function *foo(){
yield "a";
yield "b";
}
function *bar(){
yield *foo();
yield 1;
yield 2;
return "OVER";
}
const generator = bar();
console.log(generator.next()); // {value: "a", done: false}
console.log(generator.next()); // {value: "b", done: false}
console.log(generator.next()); // {value: "1", done: false}
console.log(generator.next()); // {value: "2", done: false}
console.log(generator.next()); // {value: "OVER", done: true}
四、Promise
Promise 定义
- 代表了未来某个将要发生的事件(通常是异步操作)
- ES6的 Promise 是一个构造函数,用来生成
promise
对象 - 作用:
promise
对象可以将异步操作以同步的流程表达出来,避免了层层嵌套的回调函数(回调地狱)
Promise 基本写法
// 创建 promise 对象(sync)
let promise = new Promise((resolve, reject) => {
// 初始化 promise状态为 pending
console.log("pending") ;
if(异步操作成功){
resolve("resolve"); // 成功:修改promise状态为 resolved
} else {
reject("reject"); // 失败:修改promise状态为 rejected
}
});
// 调用 promise 的 then(async)
promise.then(result=> {
console.log( result, "成功了!" );
},error => {
console.log( error, "失败了!" );
});
ES6 将程序分为了三种状态
- pending:挂起(等待) 处于未决阶段,表示事情还是在挂起,最后的结果没有出来
- resolved:已处理 处于已决阶段,表示整个事情已经出现结果,并且可以按照正常的逻辑进行下去的结果
- rejected:已拒绝 处于已决阶段,表示整个事情已经出现结果,并且是一个无法按照正常逻辑进行下去的结果
Promise 阶段状态
Promise 各状态注意点
- 未决阶段的处理函数是同步的,会立即执行
new Promise((resolve, reject) => { console.log("未决阶段的处理函数是同步的,会立即执行!"); resolve("resolve"); });
then
函数是异步的,就算会立即执行,也是会加入等待队列中,加入微队列new Promise(resolve => resolve("resolve")).then(result=> { console.log("then 函数是异步的加入微队列 " ); });
then
可以只添加成功的回调函数,catch
可以单独添加失败的回调函数let pro = new Promise((resolve, reject)=> { if(Math.random() < 0.5){ resolve("resolve"); } else { reject("reject"); } }) /* then 只添加成功的回调函数 */ pro.then(result=> { console.log(result); // resolve }); /* catch 单独添加失败的回调函数 */ pro.catch(err => { console.log(err); // reject Uncaught (in promise) reject });
- 一旦状态推向了已决阶段,无法再去做任何的改变
- 当程序已经到达已决阶段后,通常要进行后续的处理,不同的已决解决,决定了不同的后续处理
resolved
这是一个正常的已决状态,后续处理表示为result
回调函数rejected
这是一个非正常的已决状态,后续处理表示为error
回调函数- 后续的处理可能会有多个,因此会形成任务队列,这些后续处理会按照顺序,当达到对应的状态后依次执行
Promise 串联操作
-
如果当前的
Promise
是未决的,得到的新的pro
对象是挂起状态const pro = new Promise((resolve,reject) => { // resolve(); }); console.log(pro); // Promise {<pending>}
-
通过调用
then
以后,返回的是一个全新promise
对象const pro = new Promise(resolve => resolve(1)); console.log(pro); // Promise {<resolved>: 1} const proThen = pro.then(result => result*2); console.log(proThen); // Promise {<pending>}
这里有一个需要注意的点:理论上
console.log(proThen)
执行顺序是早于pro.then()
的执行的。即这里的pro.then()
状态正常输出应该是Promise {<pending>}
,[[PromiseStatus]]: "pending"
,[[PromiseValue]]: undefined
但实际上是:
那么这里的proThen
结果为何能正常打印出来?经过实验发现可以利用定时器延缓来pro.then()
的执行的,这样在pro.then()
还未执时可以在浏览器查看到pro.then()
正常的"pending"
状态,而如果等待定时器执行完毕再去查看pro.then()
则就是已经处理完成了的结果。因此,这里非正常输出状态,主要由浏览器处理所造成的。
-
返回的全新
promise
对象调用then
方法,触发resolved / rejected
?
细心的伙伴应该发现了上一个栗子返回的新的promise
对象,状态是<resolved>
,因此新的promise
对象调用then
方法则执行成功的回调函数。那么新对象的<resolved>
状态如何来的?const pro = new Promise(resolve => resolve(1)); const proThen1 = pro.then(result => { return result; // 控制台打印 proThen1 ==> Promise {<resolved>: 1} }); const proThen2 = pro.then(result => { throw result; // 控制台打印 proThen2 ==> Promise {<rejected>: 1} });
const pro = new Promise((resolve,reject) => reject(1)); const proThen1 = pro.catch(error => { return error; // 控制台打印 proThen1 ==> Promise {<resolved>: 1} }); const proThen2 = pro.catch(error => { throw error; // 控制台打印 proThen2 ==> Promise {<rejected>: 1} });
上述栗子可以看到
pro
对象状态无论是什么,调用then / catch
方法 :
将结果return
,则新的promise
对象PromiseStatus: "resolved"
,PromiseValue: 1
将结果throw
,则新的promise
对象PromiseStatus: "rejected"
,PromiseValue: 1
注意:这里返回的是非promise
对象 -
返回 pormise 对象
上一个栗子可以看到,新的promise
对象状态却决于前一个promsie
对象调用then/catch
的返回方式,但是这是建立在返回是非promise
对象状态。如果返回的是promise
对象呢?1)
return promise
,则新的promise
对象和返回的promise
状态及值保持一致const promise1 = new Promise((resolve,reject) => resolve(1)); const promise2 = new Promise((resolve,reject) => reject(1)); const pro = new Promise(resolve => resolve()); const proThen = pro.then(result => { return promise1 ; // 控制台打印 proThen1 ==> Promise {<resolved>: 1} }); const proThen = pro.then(result => { return promise2 ; // 控制台打印 proThen2 ==> Promise {<rejected>: 1} });
2)
throw promise
,则新的promise
对象状态为rejected
,PromiseValue
为返回的promise
对象const promise1 = new Promise((resolve,reject) => resolve(1)); const promise2 = new Promise((resolve,reject) => reject(1)); const pro = new Promise(resolve => resolve()); const proThen1 = pro.then(result => { throw promise1 ; // 控制台打印 proThen1 ==> Promise {<rejected>: Promise} }); const proThen2 = pro.then(result => { throw promise2 ; // 控制台打印 proThen2 ==> Promise {<rejected>: Promise} });
Promise 静态方法
- Promise.resolve()
Promise.resolve("resolve") 等价于 new Promise((resolve,reject) => resolve("resolve"));
- Promise.reject()
Promise.reject("reject") 等价于 new Promise((resolve,reject) => reject("reject"));
- Promise.all(arr)
作用:返回一个新的promise
对象,如果里面所有的promise
对象都成功才会触发,一旦有一个失败,则该promise
对象为失败const proms = []; for(let i = 0; i < 10; i++){ proms.push(new Promise((resolve,reject) => { Math.random() < 0.9 ? resolve(i) : reject(i); })); } /** * 所有的都成功才为成功,只要有一个失败,就失败 * proms数组存储的元素为不同状态的 promise 对象 * proms数组中,全是resolve状态,则pro状态:"resolved",pro值:Array(10),数组中元素为 promise值 * proms数组中,只要有reject状态,则pro状态:"rejected",pro值:数组中第一个reject状态下的 i 值 */ const pro = Promise.all(proms); pro.then(data => { console.log('全部完成', data); }); pro.catch(err => { console.log('有失败的', err) }); console.log(proms); console.log(pro);
- Promise.race(arr)
作用:当参数中的任意一个 promise对象完成时候,就马上回去使用完成的这个promise
对象的结果,不管这个结果成功还是失败const proms = []; for(let i = 0; i < 10; i++){ proms.push(new Promise((resolve,reject) => { setTimeout(()=> { console.log(i); Math.random() < 0.6 ? resolve(i) : reject(i) }, Math.random() * 3000 + 1000) })) } /** * 所有的都成功才为成功,只要有一个失败,就失败 * proms数组存储的元素为不同状态的 promise 对象 * race(proms)中的promise对象状态那个先执行,则pro状态和值与其保持一致 */ const pro = Promise.race(proms); pro.then(data => { console.log('有人完成了', data); }) pro.catch(err => { console.log('有失败的', err); }) console.log(pro);
五、Async
什么是 async ?
- ES2016 新增了 async 函数,作用是 结合生成器来简化 Promise 的操作
- 真正意义上去解决异步回调的问题,异步操作表现为同步流程表达式
- async 用于去修饰函数(函数声明和函数表达式),放在函数的开始位置,
- 被 async 修饰的函数返回一个 promise 对象
/* 原生 promsie */
function promiseFoo(){
return new Promise(resolve => resolve("init promise"));
}
const initPro = promiseFoo();
console.log(initPro); // Promise {<resolved>: "init promise"}
/* async */
async function asyncFoo(){
return "async promise";
}
const asyncPro = asyncFoo();
console.log(asyncPro); // Promise {<resolved>: "async promise"}
async 本质上是 Generator 语法糖
- async 不必像 Generator 那样调用
next
方法,async 遇到await
等待,当前的异步操作完成就往下执行 - async 返回的是 Promise 对象,可以使用
then
方法链式操作 - async 取代 Generator 的星号,
await
取代yield
- async 语义上更加明确,使用更简单
async function asyncBar1(){
console.log(123);
return 456;
}
async function asyncBar2(){
console.log(789);
return 321;
}
async function asyncFoo(){
let result = await asyncBar1(); // 遇到 await 等待当前的异步操作完成再往下执行
console.log(result);
result = await asyncBar2();
console.log(result);
return 0;
}
console.log(asyncFoo());
console.log(asyncBar1());
console.log(asyncBar2());
关于 ES6 的进阶内容的干货先介绍这么多,后续有新的内容会再继续更新,Bye ~~~