ES6 干货指南 (下)

在这里插入图片描述
下面所要介绍的是关于 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
    	});
    
  • 一旦状态推向了已决阶段,无法再去做任何的改变
    1. 当程序已经到达已决阶段后,通常要进行后续的处理,不同的已决解决,决定了不同的后续处理
    2. resolved 这是一个正常的已决状态,后续处理表示为result回调函数
    3. rejected这是一个非正常的已决状态,后续处理表示为error回调函数
    4. 后续的处理可能会有多个,因此会形成任务队列,这些后续处理会按照顺序,当达到对应的状态后依次执行
Promise 串联操作
  1. 如果当前的Promise是未决的,得到的新的pro对象是挂起状态

    	const pro = new Promise((resolve,reject) => {
    		// resolve();
    	});
    	
    	console.log(pro);	// Promise {<pending>}
    
  2. 通过调用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()则就是已经处理完成了的结果。因此,这里非正常输出状态,主要由浏览器处理所造成的。
    在这里插入图片描述

  3. 返回的全新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对象

  4. 返回 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对象状态为rejectedPromiseValue为返回的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 ~~~

发布了40 篇原创文章 · 获赞 31 · 访问量 2763

猜你喜欢

转载自blog.csdn.net/CodingmanNAN/article/details/105118366
今日推荐