2.11 迭代器
迭代器是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署 【Iterator】接口,就可以完成遍历操作。
ES6 创造了一种新的遍历命令【for...of】循环,Iterator 接口主要供 【for...of】消费
原生具备 iterator 接口的数据(可用【for...of】遍历)
Array、Arguments、Set、Map、String、TypedArray、NodeList
let arr = ['jackson','mark','bambam']; for(let i in arr){ console.log(i); // 输出键名0 1 2 } for(let j of arr){ console.log(j); // 输出键值 }
- 工作原理
- 创建一个指针对象,指向当前数据结构的起始位置
- 第一次调用对象的【next】方法,指针自动指向数据结构的第一个成员
- 接下来不断调用【next】方法,指针一直往后移动,直到指向最后一个成员
- 每调用【next】方法返回一个包含 value 和 done 属性的对象
let iterator = arr[Symbol.iterator](); console.log(iterator.next()); console.log(iterator.next()); console.log(iterator.next()); console.log(iterator.next());
注意:需要自定义遍历数据的时候,要想到迭代器!!
迭代器应用 -- 自定义遍历数据
自定义遍历数据{ if(i
2.12 生成器
生成器函数是 ES6 提供的一种【异步编程】解决方案,语法行为与传统函数完全不同。
function * gen(){ console.log('Hello generator'); } // 直接调用无法输出结果 // 需要结合【.next()】 let res = gen(); console.log(res); res.next(); // Hello generator
function * gen(){ console.log(111); yield '一只没有眼睛'; console.log(222); yield '一只没有尾巴'; console.log(333); yield '真奇怪'; console.log(444); } let res = gen(); res.next(); // 111 res.next(); // 222
// 遍历 function * gen(){ yield '一只没有眼睛'; yield '一只没有尾巴'; yield '真奇怪'; } for(let i of gen()){ console.log(i); // 返回【yield】的表达式 }
生成器函数参数
function * gen(args){ console.log(args); let one = yield 111; console.log(one); let two = yield 222; console.log(two); let three = yield 333; console.log(three); } // 执行获取迭代器对象 let res = gen('AAA'); console.log(res.next()); // next方法可以传入实参 // 传入的参数作为上一个yield语句返回的结果 console.log(res.next('BBB')); console.log(res.next('CCC')); console.log(res.next('DDD'));
生成器函数实例
// 异步任务 // 1s后输出111 2s后输出222 3s后输出333 function one(){ setTimeout(() => { console.log(111); iterator.next(); }, 1000); }; function two(){ setTimeout(() => { console.log(222); iterator.next(); }, 2000); }; function three(){ setTimeout(() => { console.log(333); iterator.next(); }, 3000); }; function * gen(){ yield one(); yield two(); yield three(); } let iterator = gen(); iterator.next();
// 模拟获取 用户数据 订单数据 商品数据 function getUser(){ setTimeout(() => { let data = '用户数据'; iterator.next(data); }, 1000); } function getOrder(){ setTimeout(() => { let data = '订单数据'; iterator.next(data); }, 1000); } function getGood(){ setTimeout(() => { let data = '商品数据'; iterator.next(data); }, 1000); } function * gen(){ let user = yield getUser(); console.log(user); let order = yield getOrder(); console.log(order); let good = yield getGood(); console.log(good); } let iterator = gen(); iterator.next();
2.13 Promise*
Promise 是 ES6 引入的【异步编程】的新解决方案,语法上 Promise 是一个构造函数,用来封装异步操作并可以获取其成功或失败的结果。
- Promise 构造函数:Promise(executor){}
// 实例化 Promise 对象 const p = new Promise(function(resolve,reject){ setTimeout(() => { // let data = '获取用户数据'; // resolve(data); let err = '获取用户数据失败'; reject(err); }, 1000); }) p.then(function(value){ console.log(value); },function(reason){ console.error(reason); })
Promise读取文件
// 1.引入fs模块 const fs = require('fs'); // 2.调用方法读取文件 fs.readFile('./0resources/枫桥夜泊.md',(err,data)=>{ // 如果失败 抛出错误 if(err) throw err; // 如果没有出错 则输出数据 console.log(data.toString()); }) // 3.使用 Promise 封装 const p = new Promise(function(resolve,reject){ fs.readFile('./0resources/枫桥夜泊.md',(err,data)=>{ if(err) reject(err); resolve(data); }) }) p.then(function(value){ console.log(value.toString()); },function(reason){ console.log("读取文件失败!"); })
打开终端,输入【node (文件名)】
Promise封装AJAX
// 段子接口 https://api.apiopen.top/getJoke const p = new Promise(function(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){ 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); })
- Promise.prototype.then 方法
const p = new Promise((resolve,reject)=>{ setTimeout(() => { resolve('用户数据'); // reject('出错啦!') }, 1000); }); // 调用【then】方法,then 方法的返回结果是 Promise 对象,对象状态由回调函数的执行结果决定 // 1.如果回调函数中返回的结果是【非Promise】类型的属性,则状态为成功,返回值为对象的成功的值 // 2.如果回调函数中返回的结果是【Promise】类型的属性,则状态由返回的Promise状态所决定 let res = 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(res);
Promise实践
// 读取多个文件 const fs = require('fs'); // 回调地狱 fs.readFile('./0resources/枫桥夜泊.md',(err1,data1)=>{ fs.readFile('./0resources/静夜思.md',(err2,data2)=>{ fs.readFile('./0resources/咏鹅.md',(err3,data3)=>{ let res = data1 + '\r\n' + data2 + '\r\n' + data3; console.log(res); }) }) }) // 使用【Promise】实现 const p = new Promise((resolve,reject)=>{ fs.readFile('./0resources/枫桥夜泊.md',(err,data)=>{ resolve(data); }) }); p.then(value=>{ return new Promise((resolve,reject)=>{ fs.readFile('./0resources/静夜思.md',(err,data)=>{ resolve([value,data]); }) }) }).then(value=>{ return new Promise((resolve,reject)=>{ fs.readFile('./0resources/咏鹅.md',(err,data)=>{ // 压入 value.push(data); resolve(value); }) }) }).then(value=>{ console.log(value.join('\r\n')); })
- Promise.prototype.catch 方法
const p = new Promise((resolve,reject)=>{ reject('出错啦!'); }); // 可以认为是【p.then()】方法的语法糖 p.catch(reason => { console.warn(reason); })
2.14 Set
ES6 提供了新的数据结构 Set(集合)。它类似于数组,但成员的值都是唯一的,集合实现了 iterator 接口,所以可以使用【扩展运算符】和【for...of...】进行遍历。
集合的属性和方法:
- size:返回集合的元素个数
- add:增加一个新元素,返回当前集合
- delete:删除元素,返回 boolean 值
- has:检测集合中是否包含某个元素,返回 boolean 值
let s = new Set(['好事儿','坏事儿','大事儿','小事儿','大事儿','大事儿']) console.log(s); // 集合个数 console.log(s.size); // 增加一个元素 s.add('喜事儿'); console.log(s); // 删除一个元素 s.delete('坏事儿'); console.log(s); // 判断是否含有某个元素 console.log(s.has('喜事儿')); // 清空 s.clear(); console.log(s); // 遍历 for(let v of s){ console.log(v); }
集合实践
let arr = [1,2,3,2,2,4,5,4,3,1,1] // 1.数组去重 let res = [...new Set(arr)]; console.log(res); // 2.交集 let arr2 = [4,5,6]; let result = [...new Set(arr)].filter(item => { let s = new Set(arr2); if(s.has(item)){ return true; }else{ return false; } }) // 简化写法 let result = [...new Set(arr)].filter(item => new Set(arr2).has(item)); console.log(result); // 3.并集 let union = [...new Set([...arr,...arr2])]; console.log(union); // 4.差集 let diff = [...new Set(arr)].filter(item => !(new Set(arr2).has(item))); console.log(diff);
2.15 Map
ES6 提供了 Map 数据结构。它类似于对象,也是键值对的集合。但是”键“的范围不限于字符串,各种类型的值(包括对象)都可以当作键。Map也实现了 iterator 接口,所以可以使用【扩展运算符】和【for...of...】进行遍历。
Map 的属性和方法:
- size:返回Map的元素个数
- set:增加一个新元素,返回当前 Map
- get:返回键名对象的键值
- has:检测 Map 中是否包含某个元素,返回 boolean 值
- clear:清空集合,返回 undefined
let m = new Map(); // 添加元素 m.set('name','爱学习'); m.set('change',function(){ console.log('知识改变命运!'); }) let key = { school: '学校' } m.set(key,['北京','上海','深圳']); // Map 个数 console.log(m.size); // 删除 m.delete('name'); // 获取 console.log(m.get('change')); console.log(m.get(key)); // 遍历 for(let v of m){ console.log(v); } console.log(m);
2.16 class*
ES6 提供了更接近传统语言的写法,引入了 Class(类)这个概念,作为对象的模板,通过 Class 关键字,可以定义类。基本上,ES6 的 Class 可以看作只是一个语法糖,他的绝大部分功能,ES5 都可以做到,新的 Class 写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。
知识点:
- class 声明类
- constructor 定义构造函数的初始化
- extends 继承父类
- super 调用父级构造方法
- static 定义静态方法和属性
- 父类方法可以重写
class 声明类
// ES5 构造函数 function Phone(brand,price){ this.brand = brand; this.price = price; } Phone.prototype.call = function(){ console.log('我可以打电话!'); } let Apple = new Phone('苹果',5199); Apple.call(); console.log(Apple); // calss类方法 class Phone{ // 构造器方法 名字不能修改 constructor(brand,price){ this.brand = brand; this.price = price; } // 方法必须使用该方法,不能使用 ES5 的对象完整形式 call(){ console.log("我可以打电话!!") } } let Huawei = new Phone("华为",3999); Huawei.call(); console.log(Huawei);
类的静态成员
function Phone(){ } Phone.name = '手机'; Phone.change = function(){ console.log('我可以改变世界'); } let nokia = new Phone(); console.log(nokia.name); nokia.change();
报错:
【name】、【change】这两个属性是属于【函数对象】的,并不属于实例对象,所以不可以直接调用。将这样的属性成为【静态成员】!!
class Phone{ // 静态属性 static name = '手机'; static change(){ console.log("我可以改变世界"); } } let nokia = new Phone(); console.log(nokia.name); console.log(Phone.name);
class 继承
ES5 【构造函数】继承
// 父类 function Phone(brand,price){ this.brand = brand; tprototype.didi = function(){ cohis.price = price; } Phone.nsole.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 oneplus = new SmartPhone("1加",1999,'黑色','5.5inch'); console.log(oneplus);
ES6 【类】继承
class Phone{ constructor(brand,price){ this.brand = brand; this.price = price; } didi(){ 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("玩游戏!") } // 父类方法重写 didi(){ console.log("我可以视频通话!") } } const xiaomi = new SmartPhone("小米",3999,"银色","5.2inch"); console.log(xiaomi);
class 的get - set
class Phone{ // get 属性 get price(){ console.log("价格属性被读取了!"); return 123; } // set 属性 set price(newP){ console.log("价格属性被修改了!!") } } const p = new Phone(); console.log(p.price); p.price = 'free';
2.17 数值扩展
// 0. Number.EPSILON 是 JavaScript 表示的最小精度 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(Equal(0.1+0.2,0.3)); // 1. 二进制和八进制 let b = 0b1010; // 10 let o = 0o777; // 511 let d = 100; // 100 let x = 0xff; // 255 // 2. Number.isFinite 检测一个数值是否为有限数 console.log(Number.isFinite(100)); // true console.log(Number.isFinite(100/0)); // false console.log(Number.isFinite(Infinity)); // false // 3. Number.isNaN 检测一个数值是否为 NaN console.log(Number.isNaN(100)); // false console.log(Number.isNaN(undefined)); // false console.log(Number.isNaN(NaN)); // true // 4. Number.parseInt Number.parseFloat 字符串转整数 console.log(Number.parseInt('123456自然数')); // 123456 console.log(Number.parseFloat('3.1415926535yu圆周率')); // 3.1415926535 // 5. Number.isInteger 判断一个数是否为整数 console.log(Number.isInteger(5)); // true console.log(Number.isInteger(2.5)); // false // 6. Math.trunc 将数字的小数部分抹掉 console.log(Math.trunc(3.5)); // 3 // 7. Math.sign 判断一个数到底是正数 负数 还是零 console.log(Math.sign(100)); // 1 console.log(Math.sign(0)); // 0 console.log(Math.sign(-20)); // -1
2.18 对象方法扩展
// 1. Object.is 判断两个值是否完全相等 console.log(Object.is(123,123)); // true console.log(Object.is(111,'111')); // false console.log(Object.is(NaN,NaN)); // true console.log(NaN === NaN); // false // 2. Object.assign 对象的合并 const config1 = { host: 'localhost', port: 3306, username: 'root', password: '123456', test: 'test' } const config2 = { host: 'http://127.0.0.1', port: 33060, username: 'admin', password: '000000' } // 重名时后面的将前面的覆盖 console.log(Object.assign(config1,config2)); // 3.Object.setPrototypeOf 设置原型对象 Object.getPrototypeOf const team = { name: '东北三省' } const people = { city: ['吉林','沈阳','长春'] } Object.setPrototypeOf(team,people); console.log(Object.getPrototypeOf(team)); console.log(team);
2.19 模块化*
模块化是指将一个大的程序文件,拆分成许多小的文件,然后将小文件组合起来。
模块化的好处
- 防止命名冲突
- 代码复用
- 高维护性
模块化规范产品
ES6 之前的模块化规范有:
- CommonJS ==> NodeJS 、Browserify
- AMD ==> requireJS
- CMD ==> seaJS
ES6 模块化语法
模块功能主要由两个命令构成:export 和 import
- export 命令用于规定模块的对外接口
- import 命令用于输入其他模块提供的功能
模块化方式一
demo1.js
// 分别暴露 export const name = '爱学习'; export function study(){ console.log("好好学习,天天向上!"); }
demo2.js
// 统一暴露 const name = '就是玩儿'; function Play(params) { console.log('我想出去玩!') } export {name,Play};
demo3.js
// 默认暴露 export default { name: '不爱学习', Game(){ console.log('想打游戏'); } }
index.html
模块化
总结
暴露接口方式:分别暴露、统一暴露、默认暴露
引入接口方式:
import * as (别名) from '文件路径'
import {...} from '文件路径' import {default as (别名)} from '文件路径'
import (别名) from '文件路径'
- 简便形式(仅针对默认暴露)
- 解构赋值形式
- 通用的导入方式
模块化方式2
app.js
// 入口文件 // 模块引入 import * as d1 from './demo1.js'; import * as d2 from './demo2.js'; import * as d3 from './demo3.js'; console.log(d1); console.log(d2); console.log(d3);
index.html
模块化
项目实战
- 安装工具 【babel-cli】、【babel-preset-env】、【browserify】
- 初始化: npm init --yes
- npm i babel-cli babel-preset-env browserify -D
- 文件转换 npx babel (待转换文件路径) -d (转换后路径) --presets=babel-preset-env
- 打包 npx browserify (入口文件) -o (输出文件)