《JavaScript学习笔记》-ES6新特性(2)
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());

注意:需要自定义遍历数据的时候,要想到迭代器!!
迭代器应用 -- 自定义遍历数据
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>自定义遍历数据</title>
</head>
<body>
<script>
const Group = {
name: "东北F4",
people: [
'赵四','刘能','小沈阳','沈腾'
],
[Symbol.iterator](){
// 索引变量
let i = 0;
return {
next: ()=>{
if(i<this.people.length){
const res = {value: this.people[i], done: false};
i++;
return res;
}else{
return {value: undefined, done: true};
}
}
}
}
}
for(let index of Group){
console.log(index);
}
</script>
</body>
</html>
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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>模块化</title>
</head>
<body>
<script type="module">
// 1.通用的导入方式
// 引入 demo1.js 模块内容
import * as d1 from './src/demo1.js';
console.log(d1)
// 引入 demo2.js 模块内容
import * as d2 from './src/demo2.js';
console.log(d2)
// 引入 demo3.js 模块内容
import * as d3 from './src/demo3.js';
console.log(d3)
// 调用 demo3.js 里的方法
d3.default.Game();
// 2.解构赋值方式
import {name,study} from './src/demo1.js';
import {name as n2,Play} from './src/demo2.js';
import {default as d3} from './src/demo3.js';
console.log(d3);
// console.log(name);
// console.log(study);
console.log(n2);
console.log(Play);
// 3.简便形式(仅针对默认形式)
import d3 from './src/demo3.js'
console.log(d3);
</script>
</body>
</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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>模块化</title>
</head>
<body>
<script src="./src/app.js" type="module"></script>
</body>
</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 (输出文件)

浙公网安备 33010602011771号