10.迭代器
迭代器(iterator)是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署iterator接口,就可以完成遍历操作
ES6创造了一种新的遍历命令for...of循环,iterator接口主要供for...of消费
注:for...of遍历的是键值,for...in遍历的是键名
for...of不能对属性值进行修改,forEach()可以
原生具备iterator接口的数据(可用for...of遍历)
Array
Arguments
Set
Map
String
TypedArray
NodeList
工作原理:
- 创建一个指针对象,指向当前数据结构的起始位置
- 第一次调用对象的next方法,指针自动指向数据结构的第一个成员
- 接下来不断调用next方法,指针一直往后移动,直到指向最后一个成员
- 每调用next方法返回一个包含value和done属性的对象,done属性表示遍历是否结束
const food = ['鱼香肉丝','糖醋里脊','酸菜鱼'] for(let item of food){ console.log(item) } let iterator = food[Symbol.iterator]() console.log(iterator.next()) // {value: "鱼香肉丝", done: false} console.log(iterator.next()) // {value: "糖醋里脊", done: false} console.log(iterator.next()) // {value: "酸菜鱼", done: false} console.log(iterator.next()) // {value: undefined, done: true} true 表示遍历已经结束
注:需要自定义遍历数据的时候,要想到迭代器
迭代器应用-自定义遍历数据(即自己手动实现一个迭代器)
// 声明一个对象 const school = { name: '三中', students: [ 'LiMing', 'HanMeimei', 'WangFang', ], [Symbol.iterator](){ // 声明一个索引变量 let index = 0 return { next: ()=>{ if(index < this.students.length){ // if(index < 3){ const result = {value: this.students[index], done: false} // 下标自增 index++ // 返回结果 return result }else{ return {value: undefined, done: true} } } } } } // 遍历这个对象 for(let item of school){ console.log(item) }
11.生成器
生成器本身是一个特殊的函数,生成器函数是ES6提供的一种异步编程解决方案,语法行为与传统函数不同
执行生成器函数,返回的是一个迭代器对象,通过iterator.next()调用执行函数内语句
function * gen(){ console.log('hello generator') } let iterator = gen() // 返回的是一个迭代器对象 // console.log(iterator) // 通过.next()调用执行函数内语句 iterator.next() // hello generator
yield是函数代码的分隔符,结合调用iterator.next()方法,实现函数gen1的语句的分段执行
function * gen1(){ console.log('--- 1 ---') yield '耳朵' // 函数代码的分隔符 console.log('--- 2 ---') yield '尾巴' console.log('--- 3 ---') } let iterator1 = gen1() iterator1.next() // --- 1 --- iterator1.next() // --- 2 --- iterator1.next() // --- 3 --- // 通过调用.next()方法,实现函数gen1的语句的分段执行
使用for...of遍历函数执行后返回的迭代器对象,每一次遍历的item为yield后的表达式或者自变量的值
function * gen1(){ yield '耳朵' // 函数代码的分隔符 yield '尾巴' } // 遍历,每一次遍历的item为yield后的表达式或者自变量的值 for(let item of gen1()){ console.log(item) } // 执行结果: // 耳朵 // 尾巴 // 注:next调用和for...of调用同时存在,只会支持最先的一个
生成器函数的参数传递
function * gen(args){ console.log(args) // 'aaa' let one = yield 111 console.log(one) // 'bbb' let two = yield 222 console.log(two) // 'ccc' let three = yield 333 console.log(three) } // 执行生成器函数获取迭代器对象 let iterator = gen('aaa') console.log(iterator.next()) // {value: 111, done: false} // next方法可以传入实参,传入的实参会作为上一个yield后返回的结果 console.log(iterator.next('bbb')) // {value: 222, done: false} console.log(iterator.next('ccc')) // {value: 333, done: false} console.log(iterator.next('ddd')) // {value: undefined, done: true}
生成器函数实例1:
1s后控制台输出111 --> 2s后控制台输出222 --> 3s后控制台输出333 ==> 总计耗时6s
// 异步编程,如文件操作、网络请求、数据库操作 // 1s后控制台输出111 --> 2s后控制台输出222 --> 3s后控制台输出333 ==> 总计耗时6s // 用生成器函数实现 function one (){ setTimeout(()=>{ console.log(111) iterator.next() }, 1000) } function two (){ setTimeout(()=>{ console.log(222) iterator.next() }, 2000) } function three (){ setTimeout(()=>{ console.log(333) }, 3000) } function * gen(){ yield one() yield two() yield three() } let iterator = gen() iterator.next() // 以下为回调地域做法 // setTimeout(()=>{ // console.log(111) // setTimeout(()=>{ // console.log(222) // setTimeout(()=>{ // console.log(333) // }, 3000) // }, 2000) // }, 1000)
生成器函数实例2:
模拟获取 用户数据 --> 订单数据 --> 商品数据
// 模拟获取 用户数据 --> 订单数据 --> 商品数据 function getUsers(){ setTimeout(()=>{ let data = '用户数据' iterator.next(data) // 相当于把得到的数据,传回users }, 1000) } function getOrders(){ setTimeout(()=>{ let data = '订单数据' iterator.next(data) }, 2000) } function getGoods(){ setTimeout(()=>{ let data = '商品数据' iterator.next(data) },3000) } // 定义生成器函数 function * gen (){ let users = yield getUsers() console.log(users) // 用户数据 let orders = yield getOrders() console.log(orders) // 订单数据 let goods = yield getGoods() console.log(goods) // 商品数据 } // 调用生成器函数,获取迭代器对象 let iterator = gen() iterator.next()
12.Promise
Promise是ES6引入的异步编程的新解决方案。语法上Promise是一个构造函数,用来封装异步操作并可以获取其成功或失败的结果
Promise构造函数:Promise(excutor){}
Promise.prototype.then方法
Promise.prototype.catch方法
基本使用
// 实例化Promise对象 const p = new Promise(function(resolve, reject){ setTimeout(()=>{ let data = '数据库中的用户数据' // resolve(data) let err = '数据读取失败' reject(err) },1000) }) p.then((value)=>{ console.log('enter success') console.log(value) },err=>{ console.log('enter failed') console.log(err) })
Promise封装读取文件
// 1.引入fs模块 fileSystem 文件系统 const fs = require('fs') // 2.调用方法读取文件 // fs.readFile('./resource/file.md', (err, data)=>{ // data是一个buffer,用来存储2进制文件,用法跟数组类似 // // 如果失败,抛出错误 // if(err) throw err // // 如果成功,读取文件 // console.log(data.toString()) // }) // 3.使用promise封装 const p = new Promise(function(resolve, reject){ fs.readFile('./resource/file.md', (err,data)=>{ if(err){ reject(err) }else{ resolve(data.toString()) } }) }) p.then((value)=>{ console.log(value) },(reason)=>{ console.error(reason) })
Promise封装ajax
// 接口地址:https://api.apiopen.top/getJoke // 原生ajax发送请求 // // 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 ){ // // 判断响应状态码 // if(xhr.status >= 200 && xhr.status < 300){ // // 如果状态码为成功,打印返回结果 // console.log(xhr.response) // }else{ // // 如果失败 // console.error(xhr.status) // } // } // } // 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((value)=>{ console.log(value) },(reason)=>{ console.log(reason) })
Promise.prototype.then方法
then方法的返回结果,依然是一个promise对象,其状态的成功或失败,由then里的回调函数的执行结果决定
如果回调函数中返回的结果是非promise类型的属性,状态为成功,且return的结果就是该promise成功后resolve的值
注: 如果没有return,则默认返回undefined,所以状态依然为成功
如果返回的结果是promise对象,则该promise对象的状态就是then返回的promise对象的状态
如果是抛出一个值(如一个错误),则状态为失败
const p = new Promise((resolve, reject)=>{ setTimeout(()=>{ resolve('用户数据') // reject('出错啦') }, 1000) }) const result = p.then((value)=>{ // p执行resolve后,状态为成功,执行第一个函数 console.log(value ) // 1.返回非promise对象的情况 // return 233 // 233 // 2.返回promise对象 return new Promise((resolve, reject)=>{ resolve('ok') // 状态为成功,值=ok // reject('error!!') // 状态为失败 }) // 3.抛出错误 // throw new Error('error!!!') },(reason)=>{ // p执行reject后,状态为失败,执行第二个函数 console.warn(reason) }) // then方法的返回结果,依然是一个promise对象,其状态的成功或失败,由then里的回调函数的执行结果决定 console.log(result)
then方法的这个特性,决定了then方法可以进行链式调用
const p = new Promise((resolve, reject)=>{ setTimeout(()=>{ resolve('用户数据') // reject('出错啦') }, 1000) }) // 链式调用 p.then((value)={ // ... }).then((value)=>{ // ... })
通过链式调用实现按顺序读取文件
// 1.引入fs模块 fileSystem 文件系统 const fs = require('fs') // 使用传统方式实现 读取文件1 => 读取文件2 => 读取文件3 // fs.readFile('./resource/file1.md', (err, data)=>{ // let data1 = data.toString() // fs.readFile('./resource/file2.md', (err,data)=>{ // let data2 = data.toString() // fs.readFile('./resource/file3.md', (err,data)=>{ // let data3 = data.toString() // let data_all = {data1,data2,data3} // console.log(data_all) // }) // }) // }) // 使用promise方式实现 读取文件1 => 读取文件2 => 读取文件3 const p = new Promise((resolve, reject)=>{ fs.readFile('./resource/file1.md', (err,data)=>{ resolve(data) }) }) p.then((value) => { return new Promise((resolve, reject)=>{ fs.readFile('./resource/file2.md',(err,data)=>{ let data_all = { data1: value.toString(), data2: data.toString() } resolve(data_all) }) }) }).then((value)=>{ return new Promise((resolve,reject)=>{ fs.readFile('./resource/file3.md', (err,data)=>{ value.data3 = data.toString() resolve(value) }) }) }).then(value=>{ console.log(value) // { data1: '# 这是文件1', data2: '# 这是文件2', data3: '# 这是文件3' } })
Promise对象catch方法
用于指定promise对象失败的回调
const p = new Promise((resolve,reject)=>{ setTimeout(()=>{ // 设置p对象的状态为失败 reject('opps error') },1000) }) // p.then((value)=>{}, (reason)=>{ // console.error(reason) // }) // 相当于then里面的第二个回调函数 p.catch((reason)=>{ console.warn(reason) })
13.set(集合)
ES6提供了新的数据结构set(集合),本质上是一个对象。它类似于数组,但成员的值都是唯一的,集合实现了iterator接口,所以可以使用「扩展运算符」和for...of进行遍历
集合的属性和方法
size,返回集合的元素个数
add,增加一个新元素,返回当前集合
delete,删除元素,返回Boolean值
has,检测集合中是否包含某个元素,返回Boolean值
let s = new Set(['风声','雨声','读书声','风声']) // 可以接受可迭代数据,一般传入数组 // '风声','雨声','读书声' let size = s.size // 查看元素个数 let has = s.has('读书声') // 检测是否含该元素 true s.add('水声') // 添加元素 s.delete('读书声') // 删除元素 let has2 = s.has('读书声') // 检测是否含该元素 false // 遍历集合 for(let item of s){ console.log(item) } s.clear() // 清空集合 console.log(s, has,has2, typeof s)
set的应用
数组去重
let arr = [1, 2, 3, 4, 5, 4, 3, 2, 1] // 数组去重 let result = [...new Set(arr)] console.log(result) // [1, 2, 3, 4, 5]
求交集
let arr = [1, 2, 3, 4, 5, 4, 3, 2, 1] let arr2 = [4, 5, 6, 5, 6] // 求交集 let result = [...new Set(arr)].filter(item => { // 对arr去重并进行遍历 let s2 = new Set(arr2) // 将arr2变为元素不重复的集合 if(s2.has(item)){ // 如果元素存在s2中 return true }else{ return false } }) console.log(result) // [4, 5] // 简化写法 let result2 = [...new Set(arr)].filter(item => new Set(arr2).has(item)) console.log(result2)
求并集
let arr = [1, 2, 3, 4, 5, 4, 3, 2, 1] let arr2 = [4, 5, 6, 5, 6] // 求并集:连接两个数组 => 转为元素不重复的集合 => 转为数组 let union = [...new Set([...arr, ...arr2])] console.log(union) // [1, 2, 3, 4, 5, 6]
求差集
let arr = [1, 2, 3, 4, 5, 4, 3, 2, 1] let arr2 = [4, 5, 6, 5, 6] // 求差集-arr对arr2求差集,即求arr里面有但是arr2里没有的元素,相当于求交集的逆运算 let diff = [...new Set(arr)].filter(item => !(new Set(arr2).has(item))) console.log(diff) // [1, 2, 3]
14.Map
ES6提供了Map数据结构。它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当做键。Map也实现了iterator接口,所以可以使用「扩展运算符」和for...of进行遍历
Map的属性和方法:
size,返回Map的元素个数
set,增加一个新元素,返回当前Map
get,返回键名对象的键值
has,检测Map中是否包含某个元素,返回Boolean值
clear,清空集合,返回undefined
// 声明Map let m = new Map() // 添加元素 m.set('name','LiMing') // 键名,键值 m.set('tell',function(){ console.log('I am LiMing ') }) let friend = { school: '三中' } m.set(friend,['小吴','小王','小芳']) // 删除元素 m.delete('tell') // 获取元素 let friends = m.get(friend) console.log(friends) // 获取元素个数 let size = m.size console.log(size) // 遍历Map for(let item of m){ console.log('item---',item) // 每一个item都是一个数组,第一个元素为键,第二个元素为值 } // 清空Map m.clear() console.log(m) console.log(typeof m)