2.11 迭代器

迭代器是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署 【Iterator】接口,就可以完成遍历操作。

  1. ES6 创造了一种新的遍历命令【for...of】循环,Iterator 接口主要供 【for...of】消费

  2. 原生具备 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);         // 输出键值
}
  1. 工作原理
  • 创建一个指针对象,指向当前数据结构的起始位置
  • 第一次调用对象的【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());

《JavaScript学习笔记》-ES6新特性(2)_JavaScript

注意:需要自定义遍历数据的时候,要想到迭代器!!

迭代器应用 -- 自定义遍历数据

自定义遍历数据{
                        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'));

《JavaScript学习笔记》-ES6新特性(2)_JavaScript_02

生成器函数实例

// 异步任务
// 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);

《JavaScript学习笔记》-ES6新特性(2)_JavaScript_03

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();

报错:

《JavaScript学习笔记》-ES6新特性(2)_JavaScript_04

【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);

《JavaScript学习笔记》-ES6新特性(2)_JavaScript_05

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

模块化

项目实战

  1. 安装工具  【babel-cli】、【babel-preset-env】、【browserify】
  • 初始化:  npm init --yes
  • npm i babel-cli babel-preset-env browserify -D
  1. 文件转换  npx babel (待转换文件路径) -d (转换后路径) --presets=babel-preset-env
  2. 打包  npx browserify (入口文件) -o (输出文件)