我是靠谱客的博主 落寞纸飞机,最近开发中收集的这篇文章主要介绍JavaScript 精选:哪些能提高开发效率的es6 新语法糖一 ECMAScript 相关介绍二 ECMASript 6 新特性三 ECMASript 7 新特性四 ECMAScript 8 新特性,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

JavaScript 精选:哪些能提高开发效率的es6 新语法糖

文章目录

  • 一 ECMAScript 相关介绍
    • 1 什么是 ECMA
    • 2 什么是 ECMAScript
    • 3 什么是 ECMA-262
    • 4 谁在维护 ECMA-262
    • 5 为什么要学习 ES6
    • 6 ES6 兼容性
  • 二 ECMASript 6 新特性
    • 1. let 关键字
          • 案例
    • 2. const 关键字
    • **3. 变量的解构赋值**
          • 应用场景:
    • 4. 模板字符串
          • 应用场景:
    • 5. 简化对象写法
          • 应用场景:
    • 6. 箭头函数
          • 案例1:箭头函数 this 始终指向声明时所在作用域下 this 的值,call 等方法无法改变其指向
          • 案例2:筛选偶数
          • 案例3:点击 div两秒后变成粉色
    • 7. 函数参数默认值设定
    • 8. rest 参数
          • 应用场景:
          • 案例1:求不定个数数字的和
    • 9. spread 扩展运算符
    • 10. Symbol
      • 10.1 Symbol 基本介绍与使用
        • Symbol 的特点
        • Symbol 的创建
      • 10.2 对象添加 Symbol 类型的属性
      • 10.3 Symbol 内置值
    • 11. 迭代器
      • 11.1 定义
      • 11.2 工作原理
      • 11.3 自定义遍历数据
    • 12. Generator 生成器函数
      • 12.1 生成器函数的声明和调用
      • **12.2 生成器函数的参数传递**
      • 2.3 生成器函数案例
    • 13. Promise
      • 13.1 Promise 的定义和使用
      • **13.2 Promise 封装读取文件**
      • **13.3 Promise 封装 Ajax 请求**
      • **13.4 Promise.prototype.then 方法**
      • 13.4 链式调用
      • 13.5 Promise.prototype.catch
      • **13.6 链式调用练习-多个文件读取**
    • 14. Set
    • 15. Map
    • 16. class 类
      • 16.1 复习 ES5 function 构造函数 的继承
      • **16.2 extends 类继承和方法的重写**
      • 16.3 静态成员
      • 16.4 getter 和 setter
    • 17. 数值扩展
    • 18. 对象方法扩展
      • 18.1 Object.is()
      • 18.2 Object.assign
      • 18.3 Object.setPrototypeOf 和 Object.getPrototypeof
    • 19. ES6 模块化
      • 19.1 模块化的好处
      • 19.2 模块化规范产品
      • 19.3 ES6 模块化语法
        • **1 模块导出数据语法**
          • 1 分别暴露
          • 2 统一暴露
          • 3 默认暴露
        • 2 模块导入数据语法
          • 1 通用导入方式
          • 2 解构赋值导入
          • 3. 简便方式导入,只能用于默认暴露
        • **3 ES6 使用模块化方式二**
      • **19.4 使用 babel 对模块化代码转换**
        • 1. 安装工具
        • 2. 编译
        • 3 打包
      • **19.5 ES6 模块化引入 npm 安装的包**
  • 三 ECMASript 7 新特性
    • 1. Array.prototype.includes
    • 2. 指数运算符
  • **四 ECMAScript 8 新特性**
    • 1. async 和 await
      • 1.1 async
      • **1.2 await**
      • **1.3 综合应用-读取文件**
      • **1.4 综合应用-封装ajax**
    • **2. Object.values 和 Object.entries**

一 ECMAScript 相关介绍

1 什么是 ECMA

ECMA(European Computer Manufacturers Association)中文名称为欧洲计算机制造商协会,这个组织的目标是评估、开发和认可电信和计算机标准,1994 年后该组织改名为Ecma 国际

2 什么是 ECMAScript

ECMAScript 是由 Ecma 国际通过ECMA-262 标准化的脚本程序设计语言。

ECMASCRIPT

image.png

3 什么是 ECMA-262

Ecma 国际制定了许多标准,而ECMA-262 只是其中的一个,所有标准列表查看:http://www.ecma-international.org/publications/standards/Standard.htm

ECMA-262 历史版本查看网址: http://www.ecma-international.org/publications/standards/Ecma-262-arch.htm

  • ES5 是 ECMAScript 第5版,2009年发布

  • ES6 是 ECMAScript 第6版,2015年发布,也叫 ES2015

  • 从 ES6 开始,每年发布一个版本,版本号比年份最后一位大 1

4 谁在维护 ECMA-262

TC39(Technical Committee 39)是推进ECMAScript 发展的委员会。其会员都是公司(其中主要是浏览器厂商,有苹果、谷歌、微软、因特尔等)。TC39 定期召开会议,会议由会员公司的代表与特邀专家出席。

5 为什么要学习 ES6

  • ES6 的版本变动内容最多,具有里程碑意义

  • ES6 加入许多新的语法特性,编程实现更简单、高效

  • ES6 是前端发展趋势,就业必备技能

6 ES6 兼容性

可查看兼容性:http://kangax.github.io/compat-table/es6/

二 ECMASript 6 新特性

1. let 关键字

let 关键字用来声明变量,使用 let 声明的变量有几个特点

  • 不允许重复声明

  • 块级作用域

  • 不存在变量提升

  • 不影响作用域链

应用场景:以后声明变量使用let 就对了

案例
  • 给多个 div 循环注册点击事件
    • 错误示例
<h2 class="page-header">点击切换颜色</h2>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>

<script>
  // 错误示例,divs.length === 3
  document.addEventListener('DOMContentLoaded', function () {
    let divs = document.querySelectorAll('div');
    for (var i = 0; i < divs.length; i++) {
      divs[i].addEventListener('click', function () {
        divs[i].style.backgroundColor = 'pink';
      });
    }
  });
  /*
  i 为当前作用域下的共享变量。
  当每次点击 div 的时候,各个点击事件共享 i 的值,此时 i = 3,将报错
*/
</script>

正确实例:将以上代码中的 var 改为 let。

  • 1s 后循环输出所有数字
    • 错误示例
for (var i = 1; i <= 5; i++) {
  setTimeout(() => {
    console.log(i);
  }, 1000);
}
/*
  输出:6 6 6 6 6
  循环从1-5的时间很短暂,远不及 1s。
  此时五个异步事件瞬间加入到异步事件队列中,等待 1s后依次执行。
  而此时i为6,故瞬间输出 5 个 6。
  异步事件队头
  (1) console.log(i);
  (2) console.log(i);
  (3) console.log(i);
  (4) console.log(i);
  (5) console.log(i);
*/
  • 正确示例
for (let j = 1; j <= 5; j++) {
    setTimeout(() => {
        console.log(j);
    }, 1000);
}
// 输出:1 2 3 4 5
// let 有块级作用域,每个 j 都会形成一个自己的块级作用域,与相应的异步事件共享:
// {j = 1;} {j = 2;} {j = 3;} {j = 4;} {j = 5;}
  • 解决方法2
// 给每一个 i 设置一个立即执行函数,会形成自己的块级作用域,不影响外部变量。
for (var i = 1; i <= 5; i++) {
    (function (i) {
        setTimeout(() => {
            console.log(i);
        }, 1000);
    })(i);
}

2. const 关键字

const 关键字用来声明常量,const 声明有以下特点

  • 声明必须赋初始值

  • 标识符一般为大写

  • 不允许重复声明

  • 值不允许修改

  • 块级作用域

    注意:对象属性修改和数组元素变化不会出发 const 错误

应用场景:声明对象类型使用 const,非对象类型声明选择 let

const arr = [1, 2, 3, 4];
arr.push(5, 6);
console.log(arr);	// 不报错

const obj = {
  uname: 'rick',
  age: 30
}
obj.age = 40;	// 只要不改变地址,就不报错

3. 变量的解构赋值

ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为 解构赋值
数组的解构赋值

const arr = ['red', 'green', 'blue'];
let [r, g, b] = arr;

对象的解构赋值

const obj = {
    uname: 'rick',
    age: 30,
    sayHi: function () {
        console.log('hello');
    },
    sayBye() {
        console.log('Bye~');
    }
}
let {name, age, sayHi} = obj;
let {sayBye} = obj;
应用场景:

频繁使用对象方法、数组元素,就可以使用解构赋值形式

4. 模板字符串

模板字符串(template string)是增强版的字符串,用反引号 `` `标识,特点

  • 字符串中可以出现换行符

  • 可以使用 ${xxx} 形式输出变量,近似 EL 表达式

应用场景:

当遇到字符串与变量拼接的情况使用模板字符串

let name = 'jack';
console.log(`hello, ${name}`);

let ul = `<ul>
            <li>apple</li>
            <li>banana</li>
            <li>peach</li>
          </ul>`

5. 简化对象写法

ES6 允许在大括号里面,直接写入变量和函数,作为对象的属性和方法。这样的书写更加简洁

应用场景:

对象简写形式简化了代码,所以以后用简写就对了

let uname = 'rick';
let age = 30;
let sayHi = function () {
  console.log('Hi');
}

// 创建对象,因属性、方法 的 k v 同名,可以简化
const obj = {
  uname,
  age,
  sayHi() { console.log('Hi'); }
}

6. 箭头函数

ES6 允许使用箭头=>定义函数

  • function 写法

    function fn(param1, param2, …, paramN) { 
        // 函数体
        return expression; 
    }
    
  • => 写法

let fn = (param1, param2, …, paramN) => {
    // 函数体
    return expression;
}

注意

  • 如果形参只有一个,小括号可以省略
  • 如果函数体只有一条语句,花括号可以省略,函数的返回值为该条语句的执行结果,如果是 return 语句,return 必须省略
  • 箭头函数 this 是静态的,始终指向声明时所在作用域下 this 的值

this就是调用方法的那个对象

箭头函数 this 始终指向声明时所在作用域下 this 的值(不停的往上找),call 等方法无法改变其指向

  • 箭头函数不能作为构造函数实例化
  • 不能使用 arguments
// 省略小括号
let fn1 = n => {
    return n * n;
}

// 省略花括号
let fn2 = (a + b) => a + b;

// 箭头函数 this 始终指向声明时所在作用域下 this 的值
const obj = {
    a: 10,
    getA () {
        let fn3 = () => {
            console.log(this);		// obj {...}
            console.log(this.a);	// 10
        }
        fn3();
    }
}
案例1:箭头函数 this 始终指向声明时所在作用域下 this 的值,call 等方法无法改变其指向
let obj = {
  uname: 'rick',
  age: 30
};
let foo = () => { console.log(this) }
let bar = function () { console.log(this) }

// call 对箭头函数无效
foo.call(obj); // window
bar.call(obj); // obj {...}
案例2:筛选偶数
const arr = [1, 6, 9, 10, 100, 25]
// const result = arr.filter(function(item) {
//     if(item % 2 === 0) {
//         return true;
//     } else {
//         return false;
//     }
// });

const result = arr.filter(item => item % 2 === 0);

console.log(result);
案例3:点击 div两秒后变成粉色
  • 方案1:使用 _this 保存 div 下的 this,从而设置 div 的 style 属性
<style>
  div {
    width: 200px;
    height: 200px;
    background: #58a;
  }
</style>

<div id="ad"></div>

<script>
  let ad = document.getElementById('ad')
  
  ad.addEventListener("click", function(){
    //保存 this 的值
    // let _this = this;
    setTimeout(() => {
      // console.log(this);
      // _this.style.background = 'pink';
      this.style.background = 'pink';
    }, 2000);
  })
</script>
  • 方案2:使用 => 箭头函数

    // 箭头函数适合与 this 无关的回调. 定时器, 数组的方法回调
    // 箭头函数不适合与 this 有关的回调.  事件回调, 对象的方法
    div.addEventListener('click', function () {
      setTimeout(() => {
        console.log(thid); // <div id="ad" style="background: pink;"></div>
        this.style.backgroundColor = 'pink';
      }, 2000);
    });
    

7. 函数参数默认值设定

ES6 允许给函数参数设置默认值,当调用函数时不给实参,则使用参数默认值
具有默认值的形参,一般要靠后。

let add = (x, y, z=3) => x + y + z;
console.log(add(1, 2)); // 6

可与解构赋值结合

function connect({ host = '127.0.0.1', uesername, password, port }) {
    console.log(host); // 127.0.0.1
    console.log(uesername);
    console.log(password);
    console.log(port);
}
connect({
    // host: 'docs.mphy.top',
    uesername: 'root',
    password: 'root',
    port: 3306
})

8. rest 参数

ES6 引入 rest 参数,用于获取函数的实参,用来代替 arguments,作用与 arguments 类似,将接收的参数序列转换为一个数组对象(arguments 是伪数组)
语法格式:fn(a, b, ...args),写在参数列表最后面

应用场景:

rest 参数非常适合不定个数参数函数的场景

let fn = (a, b, ...args) => {
  console.log(a);
  console.log(b);
  console.log(args);
};
fn(1,2,3,4,5);

// 1 2 Array(4)
案例1:求不定个数数字的和
let add = (...args) => {
    let sum = args.reduce((pre, cur) => pre + cur, 0);
    return sum;
}
console.log(add(1, 2, 3, 4, 5)); // 15

9. spread 扩展运算符

扩展运算符spread也是三个点...,它好比 rest 参数的逆运算,将一个数组、伪数组转为用逗号分隔的参数序列,对数组进行解包,扩展运算符也可以将对象解包
可用在调用函数时,传递的实参,将一个数组转换为参数序列(与rest参数的区别,一个用在形参,一个实参)

  • 展开数组
function fn(a, b, c) {
    console.log(arguments);
    console.log(a + b + c);
}
let arr = ['red', 'green', 'blue']; 
fn(...arr)
// [Arguments] { '0': 'red', '1': 'green', '2': 'blue' }
// redgreenblue
  • 案例1:数组合并
let A = [1, 2, 3];
let B = [4, 5, 6];
let C = [...A, ...B];
console.log(C); // [1, 2, 3, 4, 5, 6]
  • 案例2:数组克隆,这种数组克隆属于浅拷贝
let arr1 = ['a', 'b', 'c'];
let arr2 = [...arr1];
console.log(arr2); // ['a', 'b', 'c']
  • 案例3:将伪数组转换为真实数组
const divs = document.querySelectorAll('div');
let divArr = [...divs];
console.log(divArr);
  • 案例4:对象合并
let obj1 = {
    a: 123
};
let obj2 = {
    b: 456
};
let obj3 = {
    c: 789
};
let obj = { ...obj1, ...obj2, ...obj3 };
console.log(obj);
// { a: 123, b: 456, c: 789 }

10. Symbol

10.1 Symbol 基本介绍与使用

JavaScript 的七种基本数据类型

  • 值类型(基本类型):string、number、boolean、undefined、null、symbol

  • 引用数据类型:object(包括 array、function)

ES6 引入了一种新的原始数据类型 Symbol,表示独一无二的值。它是 JavaScript 语言的第七种数据类型,是一种类似于字符串的数据类型

Symbol 的特点

  • Symbol 的值是唯一的,用来解决命名冲突的问题

  • Symbol 值不能与其他数据进行运算

  • Symbol 定义的对象属性不能使用 for…in 循环遍历,但是可以使用 Reflect.ownKeys 来获取对象的所有键名

Symbol 的创建

  • 创建 Symbol()创建

  • 使用 Symbol.for() 方法创建,名字相同的 Symbol 具有相同的实体

  • 输出 Symbol 变量的描述,使用 description 属性

// Symbol() 创建
let s = Symbol();
console.log(s, typeof s);

let s2 = Symbol('cess');
let s3 = Symbol('cess');
console.log(s2 === s3);	// false

// Symbol.for 创建
let s4 = Symbol.for('cess');
let s5 = Symbol.for('cess');
console.log(s2 === s3);	// true

// 不能与其他数据进行运算
let result = s + 100;
let result = s > 100;
let result = s + s;

let f = Symbol('测试');
console.log(f.description); // 测试

10.2 对象添加 Symbol 类型的属性

案例:安全的向对象中添加属性和方法。
分析:如果直接向对象中添加属性或方法,则原来对象中可能已经存在了同名属性或方法,会覆盖掉原来的。所以使用 Symbol 生成唯一的属性或方法名,可以更加安全的添加

// 这是一个 game 对象,假设我们不知道里面有什么属性和方法
const game = {
  uname: '俄罗斯方块',
  up: function () { },
  down: function () { }
}

// 通过 Symbol 生成唯一的属性名,然后给 game 添加方法
let [up, down] = [Symbol('up'), Symbol('down')];
game[up] = function () {
  console.log('up');
}
game[down] = function () {
  console.log('down');
}

// 调用刚刚创建的方法
game[up]();
game[down]();


let youxi = {
  name:"狼人杀",
  [Symbol('say')]: function(){
    console.log("我可以发言")
  },
  [Symbol('zibao')]: function(){
    console.log('我可以自爆');
  }
}

console.log(youxi)

10.3 Symbol 内置值

除了定义自己使用的 Symbol 值以外,ES6 还提供了11 个内置的 Symbol 值,指向语言内部使用的方法。可以称这些方法为魔术方法,因为它们会在特定的场景下自动执行

方法描述
Symbol.hasInstance当其他对象使用 instanceof运算符,判断是否为该对象的实例时,会调用这个方法
Symbol.isConcatSpreadable对象的 Symbol.isConcatSpreadable属性等于的是一个布尔值,表示该对象用于Array.prototype.concat()时,是否可以展开
Symbol.species创建衍生对象时,会使用该属性
Symbol.match当执行 str.match(myObject)时,如果该属性存在,会调用它,返回该方法的返回值。
Symbol.replace当该对象被 str.replace(myObject)方法调用时,会返回该方法的返回值
Symbol.search当该对象被 str.search(myObject)方法调用时,会返回该方法的返回值
Symbol.split当该对象被 str.split(myObject)方法调用时,会返回该方法的返回值
Symbol.iterator对象进行 for…of 循环时,会调用 Symbol.iterator方法,返回该对象的默认遍历器
Symbol.toPrimitive该对象被转为原始类型的值时,会调用这个方法,返回该对象对应的原始类型值。
Symbol. toStringTag在该对象上面调用 toString()方法时,返回该方法的返回值
Symbol. unscopables该对象指定了使用 with关键字时,哪些属性会被 with环境排除

案例1:Symbol.hasInstance 方法判断是否属于这个对象时被调用

class A {
    static [Symbol.hasInstance]() {
        console.log('判断是否属于这个对象时被调用');
    }
}
let obj = {};
console.log(obj instanceof A)
// 判断是否属于这个对象时被调用
// false

案例2:数组使用 concat 方法时,是否可以展开

let arr1 = [1, 2, 3];
let arr2 = [4, 5, 6];
let arr3 = [4, 5, 6];
arr2[Symbol.isConcatSpreadable] = false;
console.log(arr1.concat(arr2));
// [ 1, 2, 3, [ 4, 5, 6, [Symbol(Symbol.isConcatSpreadable)]: false ] ]
console.log(arr1.concat(arr3));
// [ 1, 2, 3, 4, 5, 6 ]

11. 迭代器

11.1 定义

迭代器Iterator是一种接口,为各种不同的数据结构提 供统一的访问机制。任何数据结构只要部署 Iterator 接口(在 js 中接口就是对象的一个属性),就可以完成遍历操作

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

  • for...in 取的是索引,for...of 取的是 value

  • 原生具备 iterator 接口的数据(可用 for of 遍历)

    • Array

    • Arguments

    • Set

    • Map

    • String

    • TypedArray

    • NodeList

案例:使用 next() 方法遍历原生自带 iterator 接口的数据

// 遍历数组
const xiyou = ['唐僧', '孙悟空', '猪八戒', '沙僧'];

//使用 for...of 遍历数组
for (let v of xiyou) {
  console.log(v);
}

let iterator = xiyou[Symbol.iterator]();

//调用对象的next方法
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());


// 遍历 Map
const map = new Map();
map.set('a', 1);
map.set('b', 2);
map.set('c', 3);
let iter = map[Symbol.iterator]();
// next() 方法每执行一次,指针自增
console.log(iter.next());	// { value: [ 'a', 1 ], done: false }
console.log(iter.next()); // { value: [ 'b', 2 ], done: false }
console.log(iter.next()); // { value: [ 'c', 3 ], done: false }
console.log(iter.next()); // { value: undefined, done: true }

上面的案例只是为了证明这些数据类型自带 iterator 接口,实际上直接使用 for...of 方法即可完成,使用 for [k, v] of map 来遍历 Map 数据结构中的键和值

const map = new Map();
map.set('a', 1);
map.set('b', 2);
map.set('c', 3);

for (let [k, v] of map) {
  console.log(k, v);
}

// 结果
a 1
b 2
c 3

11.2 工作原理

1 创建一个指针对象,指向当前数据结构的起始位置

2 第一次调用对象的 next 方法,指针自动指向数据结构的第一个成员

3 接下来不断调用 next 方法,指针一直往后移动,直到指向最后一个成员

4 每调用 next 方法返回一个包含 value 和 done 属性的对象

应用场景:需要自定义遍历数据的时候,要想到迭代器

11.3 自定义遍历数据

我们可以通过给数据结构添加自定义 [Symbol.iterator]() 方法来使该数据结构能够直接被遍历,从而使 for...of 能够直接遍历指定数据

const banji = {
  name: "终极一班",
  stus: ['xiaoming', 'xiaoning', 'xiaotian', 'knight'],
  [Symbol.iterator]() {
    let index = 0;	// 索引变量
    let _this = this;
    return {
      next: function () {
        if (index < _this.stus.length) {
          const result = { value: _this.stus[index], done: false };
          index++;	// 下标自增
          return result;	// 返回结果
        } else {
          return {value: undefined, done: true};
        }
      }
    };
  }
}

// 遍历这个对象 
for (let v of banji) {
  console.log(v);
}

12. Generator 生成器函数

12.1 生成器函数的声明和调用

生成器函数是 ES6 提供的一种 异步编程解决方案,语法行为与传统采用纯回调函数完全不同

一般函数从开始运行到它结束之前,不会被任何事情打断。而生成器可以在执行当中暂停自身,可以立即恢复执行,也可以过一段时间之后恢复执行,所以生成器它不能像普通函数那样保证运行到完毕

生成器在每次 暂停/恢复 都提供了一个双向传递信息的功能,生成器可以返回一个值,恢复它的控制代码也可以接收一个值。

  • 使用 function * generator()yield 可以声明一个生成器函数,* 的位置随意
function * generator(){};
function *generator(){};
function* generator(){};
function*generator(){};
  • 生成器函数返回的结果是迭代器对象,调用迭代器对象的 next() 方法可以得到 yield 语句后的值
  • 每一个 yield 相当于函数的暂停标记,类似 return 要返回值,但不退出函数,每调用一次 next(),生成器函数就往下执行一段
  • next 方法可以传递实参,作为 yield 语句的返回值
    如下生成器函数中,3 个 yield 语句将函数内部分成了 4 段
// 生成器其实就是一个特殊的函数
// 步编程  纯回调函数  node fs  ajax mongodb
// yield 暂停标记
function * gen(){
  // console.log(111);
  yield '一只没有耳朵';
  // console.log(222);
  yield '一只没有尾部';
  // console.log(333);
  yield '真奇怪';
  // console.log(444);
}

let iterator = gen();
console.log(iterator.next());	// 每调一次,执行一段,并返回yield后面的值
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());

// 遍历
for(let v of gen()) 
  console.log(v);
}

12.2 生成器函数的参数传递

function * gen(arg){
  console.log(arg);
  let one = yield 111;
  console.log(one);
  let two = yield 222;
  console.log(two);
  let three = yield 333;
  console.log(three);
}

let iter = generator('AAA'); // 传给生成器第 1 段
console.log(iter.next());
console.log(iter.next('BBB'));
console.log(iter.next('CCC'));
console.log(iter.next('DDD'));

// 结果
AAA
Object{value: 111, done: false}
BBB
Object{value: 222, done: false}
CCC
Object{value: 333, done: false}
DDD
Object{value: undefined, done: true}

2.3 生成器函数案例

案例1:1s后输出111,2s后输出222,3s后输出333

  • 传统方式:嵌套太多,代码复杂,产生 回调地狱
setTimeout(() => {
    console.log(111);
    setTimeout(() => {
        console.log(222);
        setTimeout(() => {
            console.log(333);
        }, 3000);
    }, 2000);
}, 1000);
  • 生成器实现:结构简洁明了
function one() {
    setTimeout(() => {
        console.log(111);
        iter.next();
    }, 1000);
}

function two() {
    setTimeout(() => {
        console.log(222);
        iter.next();
    }, 2000);
}

function three() {
    setTimeout(() => {
        console.log(333);
    }, 3000);
}

function * generator() {
    yield one();
    yield two();
    yield three();
}

let iter = generator();
iter.next();
  • 案例2:生成器函数模拟每隔1s获取商品数据
function getUsers() {
    setTimeout(() => {
        let data = '用户数据';
        iter.next(data); // 传参给生成器函数的第 2 段,后面类似
    }, 1000);
}

function getOrders() {
    setTimeout(() => {
        let data = '订单数据';
        iter.next(data);
    }, 1000);
}

function getGoods() {
    setTimeout(() => {
        let data = '商品数据';
        iter.next(data);
    }, 1000);
}

function * generator() {
    let users = yield getUsers();
    console.log(users);
    let orders = yield getOrders();
    console.log(orders);
    let goods = yield getGoods();
    console.log(goods);
}

let iter = generator();
iter.next();

13. Promise

13.1 Promise 的定义和使用

Promise是 ES6 引入的异步编程的新解决方案,语法上 Promise 是一个构造函数,用来封装异步操作并可以获取其成功或失败的结果

一个 Promise 必然处于以下几种状态之一

  • 待定 pending:初始状态,既没有被兑现,也没有被拒绝

  • 已兑现 fulfilled:意味着操作成功完成

  • 已拒绝 rejected:意味着操作失败

Promise 的使用

  • Promise 构造函数new Promise((resolve, reject) => {})
  • Promise.prototype.then()方法
  • Promise.prototype.catch()方法
let p = new Promise(function (resolve, reject) {
    // 使用 setTimeout 模拟请求数据库数据操作
    setTimeout(function () {
        let isRight = true;	// 这个异步请求数据库数据操作是否正确返回数据
        if (isRight) {
            let data = '数据库中的数据';
            resolve(data);	// 设置 Promise 对象的状态为操作成功
        } else {
            let err = '数据读取失败!'
            reject(err);	// 设置 Promise 对象的状态为操作失败
        }
    }, 1000);
});
p.then(function (value) {
    console.log(value);
}, function (reason) {
    console.error(reason);
})

13.2 Promise 封装读取文件

// 使用 nodejs 的 fs 读取文件模块
const fs = require('fs');

const p = new Promise(function (resolve, reject) {
    fs.readFile('./resources/为学.txt', (err, data) => {
        if (err) reject(err);	// err 是一个异常对象
        resolve(data);
    })
})

p.then(function (value) {
    console.log(value.toString());	// 转为字符串输出
}, function (reason) {
    console.log('读取失败!!');
})

13.3 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(function (value) {
    console.log(value);
}, function (reason) {
    console.error(reason);
})

13.4 Promise.prototype.then 方法

Promise.prototype.then 方法返回的结果依然是 Promise 对象,对象状态由回调函数的执行结果决定
具体情况如下

  • 若 then 方法没写写返回值,则 then 方法返回的对象的状态值为成功 fulfilled,返回结果值为 undefined
const p = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('用户数据')
        // reject('出错了');
    }, 1000);
})
// 未设定返回值
const res = p.then((value) => {
    console.log(value);
}, (reason) => {
    console.warn(reason);
})
// 打印 then 方法的返回值
console.log(res);

image.png

  • 如果回调函数中返回的结果是非 Promise 类型的属性,则 then 方法返回的对象,其状态为成功 fulfilled,返回结果值取决于 then 方法所执行的是哪个函数(resolve 或 reject)
const p = new Promise((resolve, reject) => {
    setTimeout(() => {
        // resolve('用户数据')
        reject('出错了');
    }, 1000);
})
 // 返回的非 Promise 对象
const res = p.then((value) => {
    console.log(value);
    return '成功了!!';
}, (reason) => {
    console.warn(reason);
    return '出错啦!!'
})
// 打印 then 方法的返回值
console.log(res);

RES

PROMISE:{:'出错啦!!!

[[PROTOTYPE]]: PROMISE

[[PROMISESTATE]]: “FULFILLED”

[[PROMISERESULT]]:"出错啦!

image.png

如果回调函数中返回的结果是 Promise 类型 return new Promise(),则 then 方法返回的 Promise 对象状态与该返回结果的状态相同,返回值也相同

const p = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('用户数据')
        // reject('出错了');
    }, 1000);
})
const res = p.then((value) => {
    console.log(value);
    // 返回 Promise 对象
    return new Promise((resolve, reject) => {
        resolve('(1)成功了!!!');
        // reject('(1)出错了!!!')
    })
}, (reason) => {
    console.warn(reason);
    return new Promise((resolve, reject) => {
        // resolve('(2)成功了!!!');
        reject('(2)出错了!!!')
    })
})
// 打印 then 方法的返回值
console.log(res);

image.png

  • 如果回调函数中返回的结果是 throw 语句抛出异常,则 then 方法的对象的状态值为 rejected,返回结果值为 throw 抛出的字面量或者 Error 对象
const p = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('用户数据');
    }, 1000);
});
const res = p.then((value) => {
    console.log(value);
    return new Promise((resolve, reject) => {
        throw new Error('错误了!!');
    })
});
// 打印结果
console.log(res);

image.png

13.4 链式调用

Promise.prototype.then 方法返回的结果还是 Promise 对象,这意味着我们可以继续在该结果上使用 then 方法,也就是链式调用,杜绝回调地狱

const p = new Promise(resolve=>{}, reject=>{});
p.then(value=>{}, reason=>{})
.then(value=>{}, reason=>{})
.then(value=>{}, reason=>{})
...

13.5 Promise.prototype.catch

catch() 方法返回一个 Promise,并且处理拒绝的情况
它的行为与调用 Promise.prototype.then(undefined, onRejected) 相同

obj.catch(onRejected);
等同于
obj.then(undefined, onRejected);

语法

p.catch(onRejected);

p.catch(function(reason) {
   // 拒绝
});

举例

var p1 = new Promise(function (resolve, reject) {
    resolve('Success');
});

p1.then(function (value) {
    console.log(value); // "Success!"
    throw 'oh, no!';
}).catch(function (e) {
    console.log(e); // "oh, no!"
}).then(function () {
    console.log('有 catch 捕获异常,所以这句输出');
}, function () {
    console.log('没有 catch 捕获异常,这句将不会输出');
});

// 结果
Success
oh, no!
有 catch 捕获异常,所以这句输出

13.6 链式调用练习-多个文件读取

const fs = require('fs');

// 回调方式
fs.readFile('./resources/为学.md', (err, data1)=>{
    fs.readFile('./resources/插秧诗.md', (err, data2)=>{
        fs.readFile('./resources/观书有感.md', (err, data3)=>{
            let result = data1 + 'rn' +data2  +'rn'+ data3;
            console.log(result);
        });
    });
});

// promise 方式
new Promise((resolve, reject) => {
    fs.readFile('./resources/users.md', (err, data) => {
        resolve(data);	// 设置状态
    })
}).then(value => {
    return new Promise((resolve, reject) => {
        // value 为第一次读取的文件数据,data 为第二次(当前)读取的数据
        fs.readFile('./resources/orders.md', (err, data) => {
            resolve([value, data]);	// 将上轮读取结果和本轮合并传到下一轮轮读取操作
        });
    });
}).then(value => {
    return new Promise((resolve, reject) => {
        fs.readFile('./resources/goods.md', (err, data) => {
            // value 为上一轮传递过来的文件数据数组
            value.push(data);
            // 传给下一轮操作
            resolve(value);
        });
    });
}).then(value => {
    // 合并数组元素,输出
    console.log(value.join('rn'));
});

14. Set

ES6 提供了新的数据结构Set(集合),它类似于数组,但 成员的值都是唯一的,集合实现了 iterator 接口,所以可以使用扩展运算符 … 和 for…of 进行遍历

属性和方法

  • st.size:返回集合个数
  • st.add(item):往集合中添加一个新元素 item,返回当前集合
  • st.delete(item):删除集合中的元素,返回 boolean 值
  • st.has(item):检测集合中是否包含某个元素,返回 boolean 值
  • st.clear():清空集合
  • 集合转为数组:[…st]
  • 合并两个集合:[…st1, …st2]

案例1: 数组去重

let arr1 = [1, 2, 2, 3, 3, 3, 4, 1, 2];
let res1 = [...new Set(arr1)];
console.log(res1); // [ 1, 2, 3, 4 ]

案例2:数组求交集

let arr2_1 = [1, 2, 2, 3, 4, 5];
let arr2_2 = [3, 6, 6, 7, 1, 4];
let res2 = arr2_1.filter(v => new Set(arr2_2).has(v))
console.log(res2); // [ 1, 3, 4 ]

案例3:数组求并集

let arr3_1 = [1, 2, 2, 3, 4, 5];
let arr3_2 = [3, 6, 6, 7, 1, 4];
let res3 = [...new Set([...arr3_1, ...arr3_2])];
console.log(res3); // [ 1, 2, 3, 4, 5, 6, 7 ]

案例4:数组求差集

let arr4_1 = [1, 2, 2, 3, 4, 5];
let arr4_2 = [3, 6, 6, 7, 1, 4];
let res4 = [...new Set(arr4_1)].filter(v => !(new Set(arr4_2).has(v)))
console.log(res4); // [ 2, 5 ]

15. Map

ES6 提供了 Map 数据结构。它类似于对象,也是键值对的集合。但是 “键” 的范围不限于字符串,各种类型的值(包括对象)都可以当作键。Map 也实现了 iterator 接口,所以可以使用扩展运算符 … 和 for…of 进行遍历

let mp1 = new Map();
mp1.set('aaa', 111);
mp1.set('bbb', 222);
mp1.set('ccc', 333);

let mp2 = new Map([
    ['aaa', 111],
    ['bbb', 222],
    ['ccc', 333]
]);

console.log(mp1['aaa']); // 111
console.log(mp2.get('bbb')); // 222

Map 的属性和方法:(k 为键,v为值)

  • size:返回 Map 的元素(键值对)个数
  • set(k, v):增加一个键值对,返回当前 Map
  • get(k):返回键值对的键值
  • has():检测 Map 中是否包含某个元素
  • clear():清空集合,返回 undefined
let m = new Map();

//添加元素
m.set('name','school');
m.set('change', function(){
  console.log("我们可以改变你!!");
});
let key = {
  school : 'ATSCHOOL'
};
m.set(key, ['北京','上海','深圳']);

//size
console.log(m.size);

//删除
m.delete('name');

//获取
console.log(m.get('change'));
console.log(m.get(key));

//清空
m.clear();

//遍历
for(let v of m) {
  console.log(v);
}

16. class 类

可跳转 JS 高阶 class
ES6 提供了更接近传统语言的写法,引入了 Class(类)这个概念,作为对象的模板。通过class 关键字,可以定义类。基本上,ES6 的 class 可以看作只是一个语法糖,它的绝大部分功能,ES5 都可以做到,新的 class 写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已

16.1 复习 ES5 function 构造函数 的继承

// 手机
function Phone(brand, price){
this.brand = brand;
this.price = price;
}

Phone.prototype.call = function(){
console.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 chuizi = new SmartPhone('锤子',2499,'黑色','5.5inch');

console.log(chuizi);

16.2 extends 类继承和方法的重写

ES6 中直接使用 extends 语法糖(更简洁高级的实现方式)来实现继承,同时可以重写父类的方法,直接在子类中重新写一次要重写的方法即可覆盖父类方法。

class Phone{
  // 构造方法
  constructor(brand, price){
    this.brand = brand;
    this.price = price;
  }
  // 父类的成员属性
  call(){
    console.log("我可以打电话!!");
  }
}

class SmartPhone extends Phone {
  // 构造方法
  constructor(brand, price, color, size){
    super(brand, price);	// Phone.call(this, brand, price)
    this.color = color;
    this.size = size;
  }

  photo() {
    console.log("拍照");
  }

  playGame() {
    console.log("玩游戏");
  }

  // 子类重写父类方法,super.call()可以调父类方法
  call() {
    console.log('我可以进行视频通话');
  }
}

const xiaomi = new SmartPhone('小米',799,'黑色','4.7inch');
// console.log(xiaomi);
xiaomi.call();
xiaomi.photo();
xiaomi.playGame();

16.3 静态成员

函数对象的属性 方法是属于函数对象的,其创建的实例对象无法使用,这就是静态成员

function Phone(){}
Phone.name = '手机';
Phone.change = function(){
  console.log("我可以改变世界");
}
Phone.prototype.size = '5.5inch';

let nokia = new Phone();
// console.log(nokia.name);
// nokia.change();
console.log(nokia.size);
class Phone{
  //静态属性
  static name = '手机';
  static change(){
    console.log("我可以改变世界");
  }
}

let nokia = new Phone();
console.log(nokia.name); // undefined
console.log(Phone.name);

16.4 getter 和 setter

实际上,getter和setter是 ES5(ES2009)提出的特性 当属性拥有 get/set 特性时,属性就是访问器属性。代表着在访问属性或者写入属性值时,对返回值做附加的操作。而这个操作就是 getter/setter 函数

使用场景: getter 是一种语法,这种 get 将对象属性绑定到 查询该属性时将被调用的函数。适用于某个需要动态计算的成员属性值的获取。setter 则是在修改某一属性时所给出的相关提示

// get 和 set  
class Phone {
  get price() {
    console.log("价格属性被读取了");
    return 'iloveyou';
  }

  set price(newVal) {
    console.log('价格属性被修改了');
  }
}

//实例化对象
let s = new Phone();

// console.log(s.price);
s.price = 'free';

17. 数值扩展

  1. Number.EPSILON 是 JavaScript 表示的最小精度,一般用来处理浮点数运算。例如可以用于两个浮点数的比较
let equal = (x, y) => Math.abs(x - y) < Number.EPSILON;

console.log(0.1 + 0.2 === 0.3); // false
console.log(equal(0.1 + 0.2, 0.3)); // true
  1. 二进制和八进制:二进制以 0b 开头,八进制以 0o 开头
let b = 0b1010;
let o = 0o777;
let d = 100;
let x = 0xff;

console.log(x);
  1. Number.isFinite 检测一个数值是否为有限数。

    console.log(Number.isFinite(100)); // true
    console.log(Number.isFinite(100 / 0)); // false
    console.log(Number.isFinite(Infinity)); // false
    
  2. Number.parseIntNumber.parseFloat ES6 给 Number 添加了 parseInt 方法,Number.parseInt 完全等同于 parseInt。将字符串转为整数,或者进行进制转换。Number.parseFloat 则等同于 parseFloat()

Number.parseInt(s, base);
  • s:待转换的字符串
  • base:进位制的基数
Number.parseInt === parseInt; // true
Number.parseFloat === parseFloat; // true

console.log(Number.parseInt('5211314love')); // 5211314
console.log(Number.parseFloat('3.1415926神奇')); // 3.1415926
  1. Number.isInteger() 判断一个数是否为整数
  2. Math.trunc() 将数字的小数部分抹掉
  3. Math.sign 判断一个数到底为正数 负数 还是零
  4. Number.isNaN() 检测一个数值是否为 NaN
console.log(Number.isInteger(5)); // true
console.log(Number.isInteger(2.5)); // false

console.log(Math.trunc(3.5)); // 3

console.log(Math.sign(100)); // 1
console.log(Math.sign(0)); // 0
console.log(Math.sign(-20000)); // -1

18. 对象方法扩展

ES6 新增了一些 Object 对象的方法

18.1 Object.is()

Object.is() 方法判断两个值是否完全相同。Object.is 比较两个值是否严格相等,与 === 行为 基本一致,返回一个 Boolean 类型

Object.is(value1, value2);

Object.is() 方法判断两个值是否为同一个值。如果满足以下条件则两个值相等

  • 都是 undefined

  • 都是 null

  • 都是 true 或 false

  • 都是相同长度的字符串且相同字符按相同顺序排列

  • 都是相同对象(意味着每个对象有同一个引用)

  • 都是数字且
    ○ 都是 +0
    ○ 都是 -0
    ○ 都是 NaN
    ○ 或都是非零而且非 NaN 且为同一个值

与 == 运算不同。 == 运算符在判断相等前对两边的变量(如果它们不是同一类型)进行强制转换(这种行为的结果会将 “” == false 判断为 true),而 Object.is 不会强制转换两边的值
与 === 算也有不同,=== 运算符 (也包括 == 运算符) 将数字 -0 和 +0 视为相等,而将 Number.NaN 与 NaN 视为不相等

18.2 Object.assign

Object.assign 对象的合并,相当于浅拷贝

const config1 = {
    host: 'localhost',
    port: 3306,
    name: 'root',
    pass: 'root',
    test: 'test'
};
const config2 = {
    host: 'http://atguigu.com',
    port: 33060,
    name: 'atguigu.com',
    pass: 'iloveyou',
    test2: 'test2'
}
console.log(Object.assign(config1, config2));	// 有相同属性,后面的会覆盖前面的

18.3 Object.setPrototypeOf 和 Object.getPrototypeof

Object.setPrototypeOf 用于设置对象的原型对象
Object.getPrototypeof 用于获取对象的原型对象,相当于 proto

const school = {
    name: '尚硅谷'
}
const cities = {
    xiaoqu: ['北京','上海','深圳']
}
Object.setPrototypeOf(school, cities);	// 将school的隐式原型设置为cities
console.log(Object.getPrototypeOf(school));
console.log(school);

19. ES6 模块化

模块化是指将一个大的程序文件,拆分成许多小的文件,然后将小文件组合起来

19.1 模块化的好处

模块化的优势有以下几点

  • 防止命名冲突

  • 代码复用

  • 高维护性

19.2 模块化规范产品

ES6 之前的模块化规范有(左侧规范,右侧产品)

  • CommonJS => NodeJS、Browserify

  • AMD => requireJS

  • CMD => seaJS

19.3 ES6 模块化语法

模块功能主要由两个命令构成

  • export 命令用于规定模块的对外接口

  • import 命令用于输入其他模块提供的功能

// m1.js
expert let school = 'w3c';

expert function teach() {
    console.log("我们可以教给你开发技能");
}
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>ES6 模块化</title>
  </head>
  <body>
    <script type="module">
      import * as m1 from "./src/js/m1.js";
      console.log(m1)
    </script>
  </body>
</html>

1 模块导出数据语法

1 分别暴露
export let school = 'w3c';

export function teach() {
    console.log("我们可以教给你开发技能");
}
2 统一暴露
let school = 'w3c';
function findJob(){
    console.log("我们可以帮助你找工作!!");
}

export {school, findJob}
3 默认暴露
export default {
    school: 'w3c',
    change: function(){
        console.log("我们可以改变你!!");
    }
}

2 模块导入数据语法

1 通用导入方式
import * as m1 from './js/m1.js';
import * as m2 from './js/m2.js';
import * as m3 from './js/m3.js';

m3.default.change()
2 解构赋值导入
import {school, teach} from "./src/js/m1.js";
import {school as guigu, findJob} from "./src/js/m2.js";
import {default as m3} from "./src/js/m3.js";
3. 简便方式导入,只能用于默认暴露
import m3 from "./src/js/m3.js";

3 ES6 使用模块化方式二

将文件导入都写进一个 app.js 文件中,然后在里面写入要导入的模块。app.js 中的内容如下

// 入口文件

// 模块引入
import * as m1 from "./m1.js";
import * as m2 from "./m2.js";
import * as m3 from "./m3.js";

// console.log(m1);
// console.log(m2);
// console.log(m3);

// m1.teach();
// m2.findJob();
// m3.default.change();

//修改背景颜色为粉色
import $ from 'jquery';// const $ = require("jquery");
$('body').css('background','pink');

在 index.html 中引入 app.js 文件内容:

<script src="./src/js/app.js" type="module"></script>

19.4 使用 babel 对模块化代码转换

有的浏览器不支持 ES6 语法,这时候就需要使用 babel 来将其转换成 ES5 等价语法

1. 安装工具

babel-cli babel命令行工具
babel-preset-env babel预设包,支持新的ES特性,转换为ES5语法
browserify/webpack 打包工具

npm i babel-cli babel-preset-env browserify(webpack) -D

2. 编译

// 局部安装使用 -d 目标目录
npx babel src/js -d dist/js --presets=babel-preset-env
// 全局安装可以省略 npx

3 打包

// -o 输出到的位置
npx browserify dist/js/app.js -o dist/bundle.js

此时再引入

<script src="dist/bundle.js"></script>

19.5 ES6 模块化引入 npm 安装的包

npm install jquery

再通过 import 导入即可

import $ from 'jquery'

三 ECMASript 7 新特性

1. Array.prototype.includes

includes 方法用来检测数组中是否包含某个元素,返回布尔类型值

2. 指数运算符

在 ES7 中引入指数运算符 **,用来实现幂运算,功能与 Math.pow(a, b) 结果相同

// includes   indexOf也可以判断
const mingzhu = ['西游记','红楼梦','三国演义','水浒传'];

//判断
console.log(mingzhu.includes('西游记'));    // 
console.log(mingzhu.includes('金瓶梅'));

// **
console.log(2 ** 10);	// 1024
console.log(Math.pow(2, 10));

四 ECMAScript 8 新特性

1. async 和 await

asyncawait 两种语法结合可以让异步代码像同步代码一样(看起来是同步的,实质上是异步的)

先从字面意思理解,async 意为异步,可以用于声明一个函数前,该函数是异步的。await 意为等待,即等待一个异步方法完成

1.1 async

asyncfunction 变为成为 async 函数

  • async 内部可以使用 await,也可以不使用,因此执行这个函数时,可以使用 then 和 catch 方法
  • async 函数的返回值是一个 Promise 对象
  • Promise 对象的结果由 async 函数执行的返回值决定
async function funcName() {
    //statements 
}
  • 函数体不 return 返回值,则 async 函数返回值为一个成功 fulfilled 的 Promise 对象,值为 undefined
let a = async function() {}
let res = a()
console.log(res) // Promise{<fullfilled>: undefined}
  • return 结果不是一个 Promise ,则 async 函数返回值为一个成功 fulfilledPromise 对象,状态值为这个内部返回值
let a = async function () {
  return 'hello'
}
let res = a()
console.log(res) // Promise{<fullfilled>: 'hello'}
  • 内部抛出错误,则 async 函数返回值为一个失败 reject 的 Promise 对象
let a = async function foo() {
  throw new Error('出错了')
}
a().catch(reason => {
  console.log(reason)
})
  • 若函数内部返回值是一个 Promise 对象,则 async 函数返回值的状态取决于这个 Promise 对象 是resolve 还是 reject
let a = async function () {
  return new Promise((resolve, reject) => {
    resolve("成功")
  })
}
a().then(value => {
  console.log(value)
})

1.2 await

await 相当于一个运算符,右边接一个值。一般为一个 Promise 对象,也可以是一个非 Promise 类型。

  • 当右接一个非 promise 类型,await 表达式返回的值就是这个值;
  • 当右接一个 promise 对象,则 await 表达式会阻塞后面的代码,等待当前 promise 对象 resolve 的值
  • 综合 async 和 await 而言
    • await 必须写在 async 函数中
    • await 右侧的表达式一般为 promise 对象
    • await 返回的是 promise 成功的值
    • await 的 promise 失败了就会抛出异常,需要使用 try-catch 捕获处理

Promise 使用链式调用解决了传统方式回调地狱的问题,而 async-await 又进一步优化了代码的可读性

// 创建 promise 对象
const p = new Promise((resolve, reject) => {
  // resolve("用户数据");
  reject("失败啦!");	// 设置状态跟值
})

// await 要放在 async 函数中.
async function main() {
  try {
    let result = await p;	// 成功的值
    console.log(result);
  } catch (e) {
    console.log(e);	// 失败的值
  }
}
// 调用函数
main();	// '失败'

1.3 综合应用-读取文件

需求:先后读取三个md文件
对于这种异步操作很容易想到使用 Promise

const fs = require('fs')

let p = new Promise((resolve, reject) => {
  fs.readFile('./files/user.md', (err, data) => {
    if (err) reject(err)
    resolve(data)
  })
})

p.then(value => {
  return new Promise((resolve, rejecet) => {
    fs.readFile('./files/order.md', (err, data) => {
      if (err) rejecet(err)
      resolve([value, data])
    })
  })
}, reason => {
  console.log(reason)
}).then(value => {
  return new Promise((resolve, reject) => {
    fs.readFile('./files/goods.md', (err, data) => {
      if (err) reject(err)
      value.push(data)
      resolve(value)
    })
  })
}, reason => {
  console.log(reason)
}).then(value => {
  console.log(value.join('n'))
}, reason => {
  console.log(reason)
})

但是,使用 Promise 链式调用虽然避免了回调地狱,但这种链式调用过多难免引起代码复杂,看起来不直观。可以使用 async 和 await 方法优化

//1. 引入 fs 模块
const fs = require("fs");

//读取『为学』
function readWeiXue() {
    return new Promise((resolve, reject) => {
        fs.readFile("./resources/为学.md", (err, data) => {
            if (err) reject(err);
            resolve(data);
        })
    })
}

function readChaYangShi() {
    return new Promise((resolve, reject) => {
        fs.readFile("./resources/插秧诗.md", (err, data) => {
            if (err) reject(err);
            resolve(data);
        })
    })
}

function readGuanShu() {
    return new Promise((resolve, reject) => {
        fs.readFile("./resources/观书有感.md", (err, data) => {
            if (err) reject(err);
            resolve(data);
        })
    })
}

//声明一个 async 函数
async function main(){
    let weixue = await readWeiXue();	// 获取为学内容
    let chayang = await readChaYangShi();	// 获取插秧诗内容
    let guanshu = await readGuanShu();	// 获取观书有感

    console.log(weixue.toString());
    console.log(chayang.toString());
    console.log(guanshu.toString());
}

main()

1.4 综合应用-封装ajax

axios 返回的就是一个 promise 对象

// 发送 AJAX 请求, 返回的结果是 Promise 对象
function sendAJAX(url) {
  return new Promise((resolve, reject) => {
    const x = new XMLHttpRequest();
    x.open('GET', url);
    x.send();
    x.onreadystatechange = function () {
      if (x.readyState === 4) {
        if (x.status >= 200 && x.status < 300) {
          resolve(x.response);
        }
        reject(x.status);
      }
    }
  })
}

//promise then 方法测试
// sendAJAX("https://api.apiopen.top/getJoke").then(value=>{
//     console.log(value);
// }, reason=>{})

// async 与 await 测试  axios
async function main(){
  //发送 AJAX 请求
  let result = await sendAJAX("https://api.apiopen.top/getJoke");
  let tianqi = await sendAJAX('https://www.tianqiapi.com/api/?version=v1&city=%E5%8C%97%E4%BA%AC&appid=23941491&appsecret=TXoD5e8P')
  console.log(tianqi);
}

main();

2. Object.values 和 Object.entries

  • Object.values() 方法返回一个给定对象的所有可枚举属性值的数组
  • Object.keys(),只是前者返回属性值,后者返回键值组合的数组
  • Object.entries() 方法返回一个给定对象自身可遍历属性 [key,value] 的数组,可以使用 for…of 遍历
let obj = {
  a: 1,
  b: {1:2},
  c: [1,2,3]
}

console.log(Object.values(obj))	// [1, {1: 2}, [1,2,3]]
console.log(Object.keys(obj))	// ['a', 'b', 'c']

const obj = {a: 1, b: 2, c: 3};

console.log(Object.entries(obj))	// [ [ 'a', 1 ], [ 'b', 2 ], [ 'c', 3 ] ]
for (let [k, v] of Object.entries(obj)) {   
  console.log(k, v) 
}
  • Object.getOwnPropertyDescriptors() 获取对象属性的描述对象
console.log(Object.getOwnPropertyDescriptors(school));

const obj = Object.create(null, {
  name: {
    //设置值
    value: 'school',
    //属性特性
    writable: true,
    configurable: true,
    enumerable: true
  } 
});

最后

以上就是落寞纸飞机为你收集整理的JavaScript 精选:哪些能提高开发效率的es6 新语法糖一 ECMAScript 相关介绍二 ECMASript 6 新特性三 ECMASript 7 新特性四 ECMAScript 8 新特性的全部内容,希望文章能够帮你解决JavaScript 精选:哪些能提高开发效率的es6 新语法糖一 ECMAScript 相关介绍二 ECMASript 6 新特性三 ECMASript 7 新特性四 ECMAScript 8 新特性所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(44)

评论列表共有 0 条评论

立即
投稿
返回
顶部