Skip to content

ES6-ES11

一、ECMASript

1.1 什么是 ECMA

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

1.2.什么是 ECMAScript

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

学习参考网站:https://es6.ruanyifeng.com/

1.3 ECMAScript 和 JavaScript 的关系

前者是后者的规格,后者是前者的一种实现。

JavaScript = ECMAScript + DOM +BOM

1.4 ES6兼容性

IE等低版本浏览器可能会不兼容ES6 需要使用Babel编译器进行编译为ES5在去执行

1.5 JavaScript数据类型

七种基本数据类型:

USONB

U:Undefined

S:String,Symbol

O:Object

N:Null,Number

B:Boolea

JavaScript的基本数据类型包括以下几种:

  1. 数字(Number):用于表示整数或浮点数,例如:1、3.14等。
  2. 字符串(String):用于表示文本数据,例如:“Hello World”、"CSDN"等。
  3. 布尔值(Boolean):用于表示真或假,只有两个取值:true和false。
  4. 空值(Null):表示一个空的或不存在的值。
  5. 未定义(Undefined):表示一个未定义的值,通常用于声明但未赋值的变量。
  6. 符号(Symbol):ES6引入的新类型,表示唯一的标识符。

除了以上基本数据类型,JavaScript还有一种引用数据类型:

  1. 对象(Object):用于存储多个键值对的集合,可以是自定义对象、数组、函数等

二、ES6

2.1 let 关键字

2.1.1 let定义变量

在ES5中变量只有全局和函数内部两种作用域,而在ES6中增加了块级作用域(通过let实现)

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

  • 不允许重复声明 (避免重复声明造成变量污染)
  • 块级作用域 (一对大括号内)
  • 不存在变量提升 (不能先使用再声明)
  • 不影响作用域链

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

2.1.2 let与var的区别

相同点:他们都可以用来定义一个变量。

不同点:

  • let定义的变量只在它所在的块中有效(一对 { } 就是一个块),var是全局变量;

  • let不存在变量位置的提升(必须先声明后使用),而var存在变量位置提升(即变量可以在声明之前使用,值为undefined);

  • let不允许在相同作用域内重复声明同一个变量,而var可以;

  • var可能会存在变量泄漏。

2.1.3 实践案例

通过for循环遍历元素绑定事件并触发

js
<script>
      // 获取div元素对象
      let items = document.getElementsByClassName("item");
      // 遍历并绑定事件
      for (let i = 0; i < items.length; i++) {
        items[i].onclick = function () {
          // 修改当前元素背景色  this指向当前元素
          // this.style.background = 'pink';
          items[i].style.background = "pink";
          // 当i使用var定义时,当执行会回调函数时i为全局变量值为3

          console.log(i); // 0 1 2
          // 当i使用let定义时,i的作用域为块级作用域,当执行回调函数时i为初次循环的值
        };
      }
      // console.log(i);
      // var i=3
</script>

使用var定义循环变量i时当事件触发时,i为全局变量值

js
{
var i = 0;
onclick = function(){
  console.log(i);  3
}
}
{
var i = 1;
}
{
var i = 2;
}
var i = 3

使用let定义循环变量时,i为局部作用域,触发事件时,i的值为局部作用域的值

js
{
let i = 0;
onclick = function(){
  console.log(i);  0
}
}
{
let i = 1;
}
{
let i = 2;
}

2.2. const 关键字

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

常量:值不能修改

  1. 声明必须赋初始值
  2. 标识符一般为大写
  3. 不允许重复声明
  4. 值不允许修改 (指向的地址不允许改变,不能被整体替换)
  5. 块级作用域

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

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

2.3 变量的解构赋值

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

2.3.1 数组解构

数组解构赋值就是下标的模式匹配

js
const f4 = ["张三", "李四", "王五"];

let [zs, ls, ww] = f4;
console.log(zs, ls, ww);
2.3.2 对象解构

对象解构按照键名的模式匹配

js
const zhao = {
  name: "赵四",
  age: 30,
  sex: "男",
  xiaopin() {
    // 对象方法简洁写法
    console.log("我们可以演小品");
  },
};

let { name, sex, age: nianling } = zhao;
// age: nianling 为age取别名
console.log(name, sex, nianling);

let {xiaopin} = zhao;
xiaopin();

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

2.3.3 复杂解构
js
//复杂解构
const wangfei = {
  name: "王菲",
  age: 18,
  songs: ["红豆", "流年", "暧昧", "传奇"],
  history: [{ name: "窦唯" }, { name: "李亚鹏" }, { name: "谢霆锋" }],
};
let {
  songs: [one, two, three],
  history: [first, second, third],
} = wangfei;

console.log(one, two, three, first, second, third);

2.4.模板字符串

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

  • 字符串中可以出现换行符,无需续行符 \
  • 可以使用 ${xxx} 形式输出变量
js
// 1. 声明
let str = `我也是一个字符串`;
console.log(str, typeof str);
// 2. 内容中可以直接出现换行符
let str2 =
  "<ul>\
 <li>沈腾</li>\
 <li>玛丽</li>\
   </ul>";
let str3 = `<ul>
 <li>沈腾</li>
 <li>玛丽</li>
   </ul>`;
// 3. 模板字符串中可以使用变量
let name = "王五";
let str4 = `他的名字是${name}`;
console.log(str4);

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

2.5 字符串方法

  1. String.fromCodePoint(unicode):获取指定unicode编码所对应的字符
js
console.log(String.fromCodePoint(0x20BB7)); //𠮷  ES6编译出为正确结果

ES5中提供了 String.fromCharCode() 方法,但如果Unicode码位数较多时前两位会被舍弃,造成转译不准确

  1. str.codePointAt(index):获取str字符串指定位置字符所对应的unicode编码
js
let str = "abc嗨";
let code1 = str.codePointAt(0); //将str字符串下标为0的字符转换为所对应的Unicode编码
  1. normalize():将字符的多种表示方法统一为同样的形式,即 Unicode 标准化化
javascript
console.log("\u01D1".normalize() === "\u004F\u030C".normalize());
  1. includes(str):判断字符串中是否存在指定字符,返回布尔值
JavaScript
let str = "hi! welcome to font-end!";
console.log(str.includes("to")); // str 中是否有字符串 to
console.log(str.includes("to",10)); // 从下标10 开始找
  1. startsWith(str):判断字符串是否以指定字符开头,返回布尔值
JavaScript
console.log(str.startsWith("hi")); // 判断str是否以hi开头
console.log(str.startsWith("welcome", 4)); //true 从指定下标开始匹配是否以welcome开头
  1. endsWith(str):判断字符串是否以指定的字符结尾,返回布尔值
JavaScript
console.log(str.endsWith("!")); //判断字符串是否以!结尾
console.log(str.endsWith("font", str.length - 5)); //判断前指定个数的字符是否以font结尾
  1. repeat(num):重复字符串num次
JavaScript
console.log("*".repeat(10)); // *重复10次
console.log("abc".repeat('3'));//数字字符串会自动转换为数字
console.log(str.repeat(2.8)); // 参数自动向下取整  参数>=0
  1. padStart(num,str):字符串总长度为num,长度不足左边填充str字符
JavaScript
console.log('hello'.padStart(10, '*')); //*****hello 总长度为10,长度不够左侧补*
  1. padEnd(num,str):字符串总长度为num,长度不足右边填充str字符
JavaScript
console.log('hello'.padEnd(10, '*'));; //hello***** 总长度为10,长度不够右侧补*

2.6 简化对象写法

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

2.6.1 属性简写

键和值(仅当值为变量时)同名时,值可以省略

js
let name = "张三";
let change = function () {
  console.log("change");
};
const learn = {
  name,
  // name:name
  change,
  // change:change
};
2.6.2 方法简写
js
const obj = {
  name: "test",
  sing() {
    console.log("唱");
  },
  jump: function () {
    console.log("跳");
  },
  rap: () => {
    console.log("rap");
  },
};

2.7 箭头函数

2.7.1 定义&调用

ES6 允许使用「箭头」(=>)定义函数。

js
 // 声明一个函数
 let fn1 = function () {};
 let fn2 = (a, b) => {
   return a + b;
 };
 // 调用函数
 let result = fn2(1, 2);
 console.log(result);
2.7.2 箭头函数简写
  1. 如果形参只有一个,则小括号可以省略

    js
    let pow = a => {
      console.log(Math.pow(a, 2));
    };
    pow(2);
  2. 函数体如果只有一条语句,则花括号可以省略,return语句也必须省略,且语句的执行结果就是函数的返回值

    js
    let add = (a, b) => a + b;
    console.log(add(1, 2));
2.7.3 箭头函数使用注意
  1. 箭头函数 this 是静态的 始终指向声明时所在作用域下 this 的值,即指向外层作用域的this。且使用call aply bind 都不能改变箭头函数中的this

    js
    function getName() {
      console.log(this.name);
    }
    let getName2 = () => {
      console.log(this.name);
    };
    // 设置 window对象的 name 属性
    window.name = "张三";
    const obj = {
      name: "李四",
    };
    // 直接调用
    getName(); // 张三
    getName2(); // 张三
    // call方法调用,改变this的指向
    getName.call(obj); // 李四
    getName2.call(obj); // 张三
  2. 箭头函数不能作为构造函数实例化

    js
    let Person = (name,age)=>{
      this.name = name; // this指向window
      this.age = age;
    }
    let me = new Person("xinxin",20);
    console.log(me); // Person is not a constructor
  3. 不能使用 arguments

    js
    let fn = () => {
      console.log(arguments);
    };
    fn(1, 2, 3); //  arguments is not defined
  4. 需要动态 this 的时候,不应使用箭头函数,如在用this获取触发事件的元素对象时

    js
    const button = document.getElementById("button");
    button.onclick = () => {
      console.log(this); // window
      this.style.background = "red"; // 报错
    };
    button.onclick = function () {
      console.log(this); // buttom元素
      this.style.background = "red";
    };
  5. 箭头函数定义的对象方法 this 指向 window

js
let obj = {
  name: "张三",
  getname() {
    console.log(this.name); // this 指向obj
  },
  getName: () => {
    console.log(this.name); // this 指向window
  },
};
2.7.4 使用场景

箭头函数适合与 this 无关的回调,定时器、数组方法的回调

箭头函数不适合与 this 有关的回调,事件回调(使用事件源)、对象的方法(在对象方法中使用this)

注意:箭头函数不会更改 this 指向,且指向外层作用域的this,用来指定回调函数会非常合适

  1. 案例1定时器内操作外层元素对象:
html
<body>
  <div id="ad"></div>
  <script>
  //需求-1  点击 div 2s 后颜色变成『粉色』
  // 获取元素
  let ad = document.getElementById("ad");
  // 绑定事件
  ad.addEventListener("click", function () {
    
    // ES5 做法
    // 保存 this
    let _this = this;
    // 定时器
    window.setTimeout(function () {
      console.log(this); // this 指向 window setTimeout属于window下的方法
      this.style.background = "pink"; // 报错
      // 解决办法 使用外部this
      _this.style.background = "pink";
    }, 2000);

    // 使用箭头函数 this指向为静态的始终指向声明时所在的作用域下的this,即指向外层作用域的this
    setTimeout(() => {
      console.log(this);
      this.style.background = "pink";
    }, 2000);
  });
  </script>
</body>
  1. filter() 使用箭头函数简化代码

filter()方法用于创建一个新的数组,包含通过回调函数返回为 true 的所有元素。filter()方法会遍历一个数组,对每个元素执行回调函数,然后返回所有使得该函数返回值为true的元素组成的新数组

js
// 需求-2  从数组中返回偶数的元素
const arr = [1, 6, 9, 10, 100, 25];

const newresult = arr.filter(function (item) {
  if (item % 2 === 0) {
    return true;
  } else {
    return false;
  }
});

// 使用箭头函数 简化代码
const newresult = arr.filter((item) => item % 2 == 0);
console.log(newresult); // [6, 10, 100]

2.8 函数参数默认值

ES6 允许给函数参数赋值初始化

可以方便明确的知道哪些参数有默认值,是可以省略的

js
// 1.形参初始值  具有默认值的参数,位置一般靠后
add = (a, b, c = 0) => a + b + c;
let result = add(1, 2);
console.log(result);
// 2.默认参数与解构赋值结合
function connect({ host, username, password, port='3306' }) {
  console.log(host, username, password, port);
}
connect({
  host: "localhost",
  username: "root",
  password: "root",
});

注意:具有默认值的参数,位置一般靠后

2.9 rest 参数

ES6 引入 rest 参数,用于获取函数的实参,用来代替 arguments

js
// ES5 获取实参的方式
function date() {
  console.log(arguments); // object
}
date("a", "b", "c");

// ES6 rest 参数
function data2(...args) {
  console.log(args); // ['a', 'b', 'c']
}
data2("a", "b", "c");

// rest 参数必须位于参数的最后面
function data3(a, b, ...rest) {
  console.log(a, b); // 1,2
  console.log(rest);
}
data3(1, 2, 3, 4); // [3, 4]

作用与 arguments 类似

rest 参数必须是最后一个形参

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

2.10 扩展运算符

2.10.1 认识扩展运算符

... 扩展运算符(spread)能将数组转换为逗号分隔的参数序列

js
const arr = ['张三','李四','王五']; 
console.log(...arr);// '张三','李四','王五'
2.10.2 扩展运算符的应用
  • 数组的合并

    js
    // 1.数组的合并
    const arr1 = [1, 2, 3];
    const arr2 = [4, 5, 6];
    
    // ES5
    const newAarr1 = arr1.concat(arr2);
    console.log(newAarr1);
    
    // 扩展运算符
    const newArr2 = [...arr1, ...arr2];
    const newArr3 = Array.of(...arr1, ...arr2);
    // Array.of() 会把任意参数转成数组
    console.log(newArr2, newArr3);
    // Arry.from() 将可迭代对象转换为数组
  • 数组的克隆

    js
    const sanzhihua = ["E", "G", "M"];
    const sanzhicao = [...sanzhihua]; // 浅拷贝
    console.log(sanzhicao);
  • 将为数组转换为真正的数组

    js
    const divs = document.querySelectorAll("div");
    console.log(divs); // [ovject] NodeList(3) [div, div, div] 伪数组不能直接遍历
    const divArr = [...divs];
    console.log(divArr);

2.11 Symbol

2.11.1 Symbol 基本使用

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

Symbol 特点

  • Symbol 的值是唯一的,用来解决命名冲突的问题
  • Symbol 值不能与其他数据进行运算
  • Symbol 定义的对象属性不能使用 for…in 循环遍历 ,但是可以使用 Reflect.ownKeys 来获取对象的所有键名
js
// 创建 Symbol
let s = Symbol();
console.log(s, typeof s); // Symbol() 'symbol'
let s2 = Symbol("一不小鑫"); // 传递参数描述字符串
let s3 = Symbol("一不小鑫");
console.log(s2 === s3); // false
// Symbol.for() 创建
let s4 = Symbol.for("一不小鑫");
let s5 = Symbol.for("一不小鑫");
console.log(s4, typeof s4); //Symbol(一不小鑫) 'symbol'
console.log(s4 == s5); // true

注: 遇到唯一性的场景时要想到 Symbol

2.11.2 Symbol创建对象属性

Symbol为对象添加属性和方法表示独一无二的

js
// 向对象中添加 up down 不破坏原有的属性和方法
let game = {
  name: "游戏",
  up() {},
  down() {},
};

// 声明一个对象
let method = {
  up: Symbol("新增的up方法"),
  down: Symbol("新增的down方法"),
};

game[method.up] = function () {
  console.log("我是up");
};

game[method.down] = function () {
  console.log("我是down");
};

console.log(game);
game[method.up]();

let youxi = {
  name: "王者荣耀",
  // 添加独一无二的方法
  [Symbol("libai")]: function () {
    console.log("我是李白");
  },
  [Symbol("yase")]: function () {
    console.log("我是亚瑟");
  },
};

console.log(youxi);
2.11.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 环境排除。
js
// 1. Symbol.hasInstance
class Person {
  // 当其他对象使用instanceof时会自动调用
  static [Symbol.hasInstance](param) {
    console.log(param);
    console.log("我被用来用于检测类型");
  }
}
let o = {};
console.log(o instanceof Person);

// 2.Symbol.isConcatSpreadable
const arr = [1, 2, 3];
const arr2 = [4, 5, 6];
// 设置arr2使用concat方法合并数组时是否展开
arr2[Symbol.isConcatSpreadable] = false;
console.log(arr.concat(arr2)); //[1, 2, 3, Array(3)]

2.12 迭代器

2.12.1 迭代器遍历

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

  1. ES6 创造了一种新的遍历命令 for...of 循环,Iterator 接口主要供 for...of 使用
  2. 原生具备 iterator 接口的数据(可用 for of 遍历)
    • Array
    • Arguments
    • Set
    • Map
    • String
    • TypedArray
    • NodeList
  3. 工作原理
    • 创建一个指针对象,指向当前数据结构的起始位置
    • 第一次调用对象的 next 方法,指针自动指向数据结构的第一个成员
    • 接下来不断调用 next 方法,指针一直往后移动,直到指向最后一个成员
    • 每调用 next 方法返回一个包含 value 和 done(是否结束) 属性的对象
js
// 声明一个数组
const arr = ["A", "B", "C", "D"];

// 使用 for....of 遍历数组
for (let item of arr) {
  // 保存数组中的键值
  console.log(item);
}

// 使用 for...in 遍历数组
for (let i in arr) {
  // 保存数组的键名(下标)
  console.log(i);
}

console.log(arr); // 存在Symbol(Symbol.iterator)
let iterator = arr[Symbol.iterator]();
console.log(iterator); // 存在 next()方法
console.log(iterator.next()); //{value: 'A', done: false}
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next()); //{value: undefined, done: true}
2.12.2 迭代器自定义遍历数据
js
// 声明一个对象
const banji = {
  name: "一班",
  stus: ["xiaomming", "xiaoning", "xiaotian"],
  // 创建interator接口
  [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...of遍历这个对象每次返回数组中的成员
for (let v of banji) {
  // 使用for...of遍历必须要有iterator接口
  console.log(v);
}

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

2.13 生成器

2.13.1 生成器函数声明与调用

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

js
// 生成器函数其实就是一个特殊的函数
// 异步编程 纯回调函数
function* gen() {
  console.log("111");
  // 函数代码的分隔符
  yield "一只没有耳朵";
  console.log("222");
  yield "一只没有尾巴";
  console.log("333");
  yield "真奇怪";
  console.log("444");
}

let iterator = gen();
console.log(iterator);

iterator.next(); // 111
iterator.next(); // 222
iterator.next(); // 333
iterator.next(); // 444

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

代码说明:

  • *的位置没有限制 在function和函数名之间
  • 生成器函数返回的结果是迭代器对象,调用迭代器对象的 next 方法可以得到 yield 语句后的值
  • yield 相当于函数的暂停标记,也可以认为是函数的分隔符,每调用一次 next 方法,执行一段代码
  • next 方法可以传递实参,作为 yield 语句的返回值
2.13.2 生成器函数的参数传递
js
function* gen(arg) {
  console.log(arg); // AAA
  let one = yield 111;
  console.log(one); //BBB
  let two = yield 222;
  console.log(two); // CCC
  let three = yield 333;
  console.log(three); // DDD
}

// 执行获取迭代器对象
let iteratir = gen("AAA");
// next方法可以传入实参
console.log(iteratir.next()); //{value: 111, done: false}
// 在第二次调用next方法传递的实参,会作为第一个yield表达式的值
console.log(iteratir.next("BBB"));
console.log(iteratir.next("CCC"));
console.log(iteratir.next("DDD")); //{value: undefined, done: true}
2.13.3 回调地狱

多个回调函数嵌套,会产生回调地狱

js
setTimeout(() => {
  console.log(111);
  setTimeout(() => {
    console.log(222);
    setTimeout(() => {
      console.log(333);
    }, 3000);
  }, 2000);
}, 1000);
2.13.4 生成器函数实例

需求:1s 后控制台输出 111 2s 后控制台输出 222 3s后输出 333

js
// 单个任务封装
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);
}

// 将三个函数的调用放在了yield中
function* gen() {
  yield one();
  yield two();
  yield three();
}

// 调用生成器函数生成一个迭代器对象
let iterator = gen();
iterator.next(); // 调用迭代器对象的next方法
3.13.5 生成器函数实例2

模拟获取 用户数据 订单数据 商品数据

在next函数中传递参数

js
function getUsers() {
  setTimeout(() => {
    let data = "用户数据";
    // 调用next 方法,并且将数据传入
    iterator.next(data);
    // 第二次调用next方法传入实参将作为第一个yield的返回值
  }, 1000);
}

function getOrders() {
  setTimeout(() => {
    let data = "订单数据";
    // 第三次调用next方法传入实参作为第二个yield的返回值
    iterator.next(data);
  }, 1000);
}

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

// 声明一个生成器函数
function* gen() {
  // 接收第一个yield的返回值(为第二个next方法传入的参数)
  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(); //调用迭代器对象的next方法

2.14 Promise

2.14.1 Promise介绍

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

  1. Promise 构造函数: Promise (excutor) {}
  2. Promise.prototype.then 方法
  3. Promise.prototype.catch 方法

Promise对象的两个特点

  • 对象的状态不受外界影响
  • 一旦状态改变,就不会再变,任何时候都可以得到这个结果
2.14.2 Promise基本语法
js
// 实例化 Promise 对象,回调函数需要传递两个参数,resolve(变为成功状态) 和 reject(变为失败状态)
const p = new Promise(function (resolve, reject) {
  setTimeout(() => {
    // 读取数据成功
    /* let data = "数据库中的数据";
    resolve(data);
    // resolve 改变状态为成功,调用then中的第一个回调函数,并传递成功信息 */

    // 读取数据失败
    let err = "数据读取失败";
    reject(err); //改变状态为失败,调用then中的第二个回调函数,并传递失败信息
  }, 1000);
});

// 调用promise对象的then方法,有两个回调函数,第一个为成功回调,第二个为失败回调
p.then(
  function (value) { // 成功回调
    console.log(value);
  },
  function (reason) { // 失败回调
    console.log(reason);
  }
);
2.14.3 Promise封装读取文件

需求:读取resources目录下的为学.md文件

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

// 2.使用Promise封装
const p = new Promise((resolve, rejecct) => {
  fs.readFile("./resources/为学.md", (err, data) => {
    //错误优先回调
    // 如果读取失败
    if (err) rejecct(err);
    // 如果读取成功
    resolve(data);
  });
});
// 使用then方法处理成功失败的回调
p.then(
  (value) => {
    console.log(value.toString());
  },
  (reason) => {
    console.log("读取失败");
  }
);
2.14.4 Promise封装AJAX请求
  • 封装原生ajax请求
js
const p = new Promise((resolve, reject) => {
  // 1.创建对象
  const xhr = new XMLHttpRequest();

  // 2.初始化
  xhr.open("GET", "https://api.apiopen.top/getJoke");

  // 3.发送
  xhr.send();

  // 4.绑定事件,处理结果
  xhr.onreadystatechange = function () {
    // 判断状态是否为4,表示请求已经完成
    if (xhr.readyState === 4) {
      // 判断响应状态码 200-299 成功
      if (xhr.status >= 200 && xhr.status < 300) {
        // 表示成功
        resolve(xhr.response);
        // 修改为成功状态并传参
      } else {
        // 如果失败
        reject(xhr.status);
        // 修改为失败状态并传参
      }
    }
  };
});

// 指定回调
p.then(
  function (value) {
    console.log(value);
  },
  function (reason) {
    console.log(reason);
  }
);

封装JQuery AJAX请求

js
const getInfo = () => {
  return new Promise((resolve, reject) => { //返回一个Promise对象
    // Ajax请求
    $.ajax({
      type: 'get',
      url: '/api/info',
      success: function(data) {//请求成功
        resolve(data);//调用resolve函数,将promise对象状态变为成功,并将异步操作的结果作为参数传递出去;
      },
      error: function(err) {//请求失败
        reject(err);//调用reject函数,将promise状态转变为失败,并将错误信息作为参数传递出去
      }
    });
  });
};

getInfo().then(response => { //返回请求成功的数据,response为调用resolve函数传递出的数据
  console.log(response);
}).catch(err =>{  //返回错误信息,err为调用reject函数传递出的数据
  console.log(response);
});
2.14.5 Promise.prototype.then()方法返回结果状态

调用 then 方法,then方法的返回结果是 Promise对象,对象的状态由回调函数的执行结果决定

  1. 如果回调函数返回值是 非Promise 类型的属性,状态为成功,返回值为对象的成功的值
  2. 如果回调函数返回值是 promise 类型的属性,状态由返回的Promise的状态决定
  3. 抛出错误 Promise 对象的状态为失败
js
const p = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("用户数据");
    // reject("出错了!");
  }, 1000);
});
const result = p.then(
  (value) => {
    console.log(value); // 用户数据
    // 1.非 Promise 类型的属性
    return "123";
    // 2.是 promise 对象
    return new Promise((resolve, reject) => {
      resolve("ok");
      // reject("出错了");
    });
    // 3.抛出错误
    throw new Error("出错了");
    // throw '出错了';
  },
  (reason) => {
    console.warn(reason);
  }
);

then 方法返回的是一个Promise对象,因此可以链式调用,在then方法中执行异步任务,再通过then方法处理

js
p.then(
  (value) => {},
  (reason) => {}
).then(
  (value) => {},
  (reason) => {}
);
2.14.6 Promise读取多个文件

需求:逐个读取resourses文件夹下的三个文件,再将其内容拼接输出

  • 回调函数嵌套回调函数实现
js
// 引入fs模块
const fs = require("fs");

fs.readFile("./resources/为学.md", (err, data1) => {
  if (err) throw err;
  fs.readFile("./resources/插秧诗.md", (err, data2) => {
    if (err) throw err;
    fs.readFile("./resources/观书有感.md", (err, data3) => {
      let result = data1 + "\r\n" + data2 + "\r\n" + data3; // 字符串拼接会自动将buffer转换为字符串
      console.log(result);
    });
  });
});

产生回调地狱,会出现命名冲突,代码缩进问题

  • 使用 Promise 解决地狱回调问题
js
// 引入fs模块
const fs = require("fs");

const p = new Promise((resolve, reject) => {
  // 读取第一个文件内容
  fs.readFile("./resources/为学.md", (err, data) => {
    if (err) reject(err);
    resolve(data);
  });
});
p.then((value) => {
  // console.log(value.toString()); // 上次Promise返回成功状态的数据
  // 返回一个Promise对象进行链式调用
  return new Promise((resolve, reject) => {
    // 读取第二个文件内容
    fs.readFile("./resources/插秧诗.md", (err, data) => {
      if (err) reject(err);
      // 将读取的内容作为promise的返回值传递给then方法
      resolve(value + "\r\n" + data);
    });
  });
})
  .then((value) => {
    // console.log(value);
    // 读取第三个文件内容
    return new Promise((resolve, reject) => {
      fs.readFile("./resources/观书有感.md", (err, data) => {
        if (err) reject(err);
        resolve(value + "\r\n" + data);
      });
    });
  })
  .then((value) => {
    console.log(value);
  });

解决层层回调嵌套,和命名冲突问题

2.14.6 catch 方法

用于指定Promise对象失败的回调

js
// 指定成功失败的回调
// 方法一
p.then(
  (value) => {
    console.log(value);
  },
  (reason) => {
    console.log(reason);
  }
);
// 方法二 then方法不指定第二个参数,使用catch代替then中的第二个错误回调
p.then((value) => {
  console.log(value);
}).catch((reason) => {
  console.warn(reason);
});

2.15 Set

2.15.1 集合介绍与API

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

  1. size 返回集合的元素个数
  2. add 增加一个新元素,返回当前集合
  3. delete 删除元素,返回 boolean 值
  4. has 检测集合中是否包含某个元素,返回 boolean 值
  5. clear 清空集合,返回 undefined
js
// 1.创建
// 声明一个 set
let s = new Set();
// 传入初始参数(可迭代数据)
let s2 = new Set([1, 2, 2, 3, 4, 4]); // 会自动进行去重
console.log(s2, typeof s2);

// 2.属性
// 元素的个数
console.log(s2.size); // 4 去重后的个数
// 添加新的元素
s2.add(5);
// 删除元素
s2.delete(2);
// 检测元素是否存在
console.log(s2.has(3));
// 清空集合
// s2.clear();

console.log(s2);

// 使用 for...of进行遍历
for (v of s2) {
  console.log(v);
}
2.15.2 集合实践
  1. 数组去重

    js
    let arr = [1, 2, 3, 4, 5, 4, 2, 3, 1];
    let s1 = new Set(arr);
    console.log([...s1]);
    // console.log(Array.of(...s1));
  2. 交集

    js
    let arr = [1, 2, 3, 4, 5, 4, 2, 3, 1];
    let arr2 = [4, 5, 6, 5, 4, 6];
    // filter回调函数形参为被遍历数组的元素,返回一个新数组,新数组中的元素是原始数组中符合条件(返回结果为true)的元素
    let result = [...new Set(arr)].filter((item) => {
      // item 是数组中的元素
      let s2 = new Set(arr2); // 集合
      return s2.has(item); // 判断集合中是否有数组中的元素
    });
    // 代码简化
    let result = [...new Set(arr)].filter((item) => new Set(arr).has(item)); 
    console.log(result);
  3. 并集

    js
    let union = [...new Set([...arr, ...arr2])];
    console.log(union);
  4. 差集

    js
    let difference = [...new Set(arr)].filter((item) => !new Set(arr2).has(item)); // 返回结果为true 将item添加到新数组中
    console.log(difference);

2.16 Map

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

方法:

  1. size 返回 Map 的元素个数
  2. set 增加一个新元素,返回当前 Map
  3. delete删除指定键名的元素
  4. get 返回键名对象的键值
  5. has 检测 Map 中是否包含某个元素,返回 boolean 值
  6. clear 清空集合,返回 undefined
js
// 声明一个Map对象
let m = new Map();

// 添加元素
m.set("name", "xinxin");
m.set("change", function () {
  console.log("改变");
});

// 对象作为Map的键
let key = {
  school: "ATGUIGU",
};
m.set(key, ["北京,上海,深圳"]);

// 删除元素
m.delete("name");

// 获取元素
console.log(m.get("change"));
console.log(m.get(key));

// 检测
console.log(m.has("name"));

// 清空
// m.clear();

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

console.log(m, m.size);

对象的key只能为字符串,Map的key可以为任意类型

2.17 class 类

2.17.1 calss介绍

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

知识点:

  1. class 声明类
  2. constructor 定义构造函数初始化
  3. extends 继承父类
  4. super 调用父级构造方法
  5. static 定义静态方法和属性
  6. 父类方法可以重写
2.17.2 类声明

ES5 构造函数

js
function Phone(brand, price) {
  this.brand = brand;
  this.price = price;
}

// 添加方法
Phone.prototype.call = function () {
  console.log("我可以打电话");
};

// 实例化对象
let Huawei = new Phone("华为", 4999);
console.log(Huawei);
Huawei.call();

ES6 类声明

js
class Phone {
  // 构造方法 名字固定,当使用new 类名时自动执行
  constructor(brand, price) {
    this.brand = brand;
    this.price = price;
  }
  // 注意:方法必须使用该语法,不能使用ES5对象完整语法
  call() {
    // 方法挂载于原型上(提高复用避免每次创建时重新生成减少内存消耗)
    console.log("可以打电话");
  }
}

let xiaomi = new Phone("小米", 1999);
console.log(xiaomi);
xiaomi.call();
2.17.3 静态成员

特征:

  1. 使用 static 声明属性和方法
  2. 不属于实例化对象,只能通过类进行调用
  3. 可以被子类继承
js
class Phone {
  // 静态属性 属于类不属于实例化对象
  static name = "手机"; // 静态属性
  static change() { // 静态方法
    console.log("科技改变世界");
  }
}
 
let nokia = new Phone(); // 实例化对象
console.log(nokia.name); // undefined
// 可以通过类名进行调用
console.log(Phone.name); // 手机
2.17.4 私有成员

特征:

  1. 使用 #声明属性和方法
  2. 只能在类的内部使用
  3. 不属于实例化对象
  4. 不能被继承
js
class Phone {
  // 私有属性
  #cpu = "apple";
  active() {
    // 调用私有方法
    this.#call();
  }
  // 私有方法
  #call() {
    // 使用私有属性
    console.log(`${this.#cpu}可以视频电话`);
  }
}

let iphone = new Phone();
iphone.active(); //apple可以视频电话
// iphone.#active(); 报错
// console.log(iphone.#cpu); 报错
// Phone.#cpu 报错 只能再类内部使用
2.17.5 ES5构造函数继承
js
// 父构造函数
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); // 改变Phone中this的指向,指向SmartPhone的实例化对象
  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 xiaomi = new SmartPhone("小米", 1999, "白色", 5.5);
console.log(xiaomi);
xiaomi.call();
2.17.6 class的类继承
  • 使用 extends 继承

  • 使用 super 调用父类的构造方法

  • 除了私有成员不能被继承,其它的属性和方法的可以被继承

  • 实现效果与构造函数相同

js
// 父类
class Phone {
  // 构造方法
  constructor(brand, price) {
    this.brand = brand;
    this.prcie = price;
  }
  // 父类的成员方法
  call() {
    console.log("我可以打电话");
  }
}
// 子类 继承Phone
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("玩游戏");
  }
}

// 实例化子类
const xiaomi = new SmartPhone("小米", 1999, "白色", 5.5);
console.log(xiaomi);
xiaomi.call();
xiaomi.photo();
2.17.7 父类方法的重写

在子类声明一个与父类同名的方法

子类实例化对象调用时执行子类重写的方法

js
// 父类
class Phone {
  call(brand) {
    console.log(`${brand}手机能打电话`);
  }
}
// 子类
class SmartPhone extends Phone {
  call() {
    console.log("能打视频电话");
  }
}
// 实例化子类
const xiaomi = new SmartPhone();
xiaomi.call(); // 能打视频电话  调用的是子类重写的方法

不能使用super方法调用父类的成员方法

2.17.8 getter和setter

当对某一属性进行获取时调用getter方法

当对某一属性进行设置时调用setter方法

js
class Phone {
  // 对动态属性进行封装
  get price() {
    console.log("价格属性被获取");
    return "100";
  }
  set price(val) {
    console.log("价格属性被设置");
  }
}

// 实例化对象
let s = new Phone();
console.log(s.price);
s.price = "free";

2.18 数值扩展

2.18.1 二进制和八进制

ES6 提供了二进制和八进制数值的新的写法,分别用前缀 0b 和 0o 表示。

js
let b = 0b1010; // 二进制
console.log(b); // 10
let o = 0o777; // 八进制
console.log(o); //  511
let d = 100; // 十进制
console.log(d); // 100
let x = 0xff; // 十六进制
console.log(x); // 255
2.18.2 Number.isFinite() 与 Number.isNaN()

Number.isFinite() 用来检查一个数值是否为有限的

js
console.log(Number.isFinite(100)); // true
console.log(Number.isFinite(Infinity)); // false

Number.isNaN() 用来检查一个值是否为 NaN(not a number:非数字)

js
console.log(Number.isNaN(123)); //fasle
console.log(isNaN(123)); // false
console.log(isNaN("a")); // true
2.18.3 Number.parseInt() 与 Number.parseFloat()

将字符串转换为Number类型

ES6 将全局方法 parseInt 和 parseFloat,移植到 Number 对象上面,使用不变。

js
console.log(Number.parseFloat("100.133ABC")); // 100.133
console.log(Number.parseInt("12.94hello")); // 12
console.log(parseInt("12")); // 12
console.log(parseFloat("12.44")); // 12.44
2.18.4 Number.isInteger

Number.isInteger() 用来判断一个数值是否为整数

js
console.log(Number.isInteger(12)); // true
console.log(Number.isInteger("12")); //false
2.18.5 Number.EPSILON

Number.EPSILON是 Javascript 表示的最小精度

EPSILON 属性的值接近于 2.2 的 -16次方,这是最小的精度

当两个数的差值小于Number.EPSILON时,认为这两个数相等

js
console.log(0.1 + 0.2 === 0.3); // false
function equal(a, b) {
  if (Math.abs(a - b) < Number.EPSILON) { // 当两个数的差的绝对值小于NUmber.EPSILON时认为两个数相等
    return true;
  } else {
    return false;
  }
}
console.log(equal(0.1 + 0.2, 0.3)); // true
2.18.6 Math.trunc

用于去除一个数的小数部分,返回整数部分。

js
console.log(Math.trunc(12.999)); // 12
2.18.7 Math.sign

判断一个数到底是整数 负数 还是零

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

2.19 对象扩展

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

  1. Object.is 比较两个值是否严格相等,与『===』行为基本一致(+0 与 NaN)
js
console.log(Object.is(1, 1.0)); // true
console.log(Object.is(NaN, NaN)); // true
console.log(NaN === NaN); // false
console.log(Object.is(1, 1)); // true
console.log(Object.is([1], [1])); // false
  1. Object.assign 对象的合并,将源对象的所有可枚举属性,复制到目标对象
js
const config1 = {
  host: "localhost",
  port: 3306,
  name: "root",
  pass: "root",
  one: 1,
};
const config2 = {
  host: "127.0.0.1",
  port: 80,
  name: "admin",
  pass: "admin",
  tow: 2,
};
// Object.assign(被覆盖,覆盖);
console.log(Object.assign(config1, config2)); //两者进行合并 config2会覆盖config1内的同名属性

用于配置文件合并

  1. __ proto__、setPrototypeOf、 getPrototypeOf 可以直接设置对象的原型
js
const school = {
  name: "xx",
};
const cities = {
  xiaoqu: ["北京", "上海", "深圳"],
};
// 为school设置原型 将cities内容作为school的原型
Object.setPrototypeOf(school, cities);
// 获取原型
console.log(Object.getPrototypeOf(school));
console.log(school);

一般不这样使用

2.20 模块化

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

2.20.1 模块化的好处

模块化的优势有以下几点:

  1. 防止命名冲突
  2. 代码复用
  3. 高维护性
2.20.2 模块化规范产品
  1. ES6 之前的模块化规范有:
  2. CommonJS => NodeJS、Browserify
  3. AMD => requireJS
  4. CMD => seaJS
2.20.3 ES6 模块化语法

模块功能主要由两个命令构成:export 和 import。

  • export 命令用于规定模块的对外接口
  • import 命令用于输入其他模块提供的功能
2.20.4 export暴露
  • 分别暴露

    js
    export let school = 'xx';
    export function teach(){
      console.log("好好学习");
    }
  • 统一暴露

    js
    let school = "xx";
    function play() {
      console.log("让我们愉快的玩耍吧");
    }
    
    export { school, play };
  • 默认暴露

    将所有内容放在 default 对象下 对default对象进行暴露

    js
    export default {
      name:"张三",
      age:'13',
      say(){
        console.log(`我是${this.name}`);
      }
    }

注意:一个js文件只能有一个默认暴露

2.20.5 import 导入
  1. 通用的导入方式

    js
    // 引入 分别/统一暴露 模块内容
    import * as m1 from "./src/js/m1.js";
    // 将m1.js下暴露的内容全部都存在 m1 对象中
    console.log(m1);
    console.log(m1.school);
    m1.teach();
    
    // 引入 默认暴露 模块内容
    import * as m3 from "./src/js/m3.js";
    console.log(m3);
    m3.default.say(); // 使用时需要加上default
    console.log(m3.default.name);
  2. 解构赋值形式

    js
    // 导入 分别/统一暴露 模块内容
    import { school, teach as th } from "./src/js/m1.js";
    // as 取别名
    console.log(school);
    th();
    
    // 导入 默认暴露 模块内容
    import { default as m3 } from "./src/js/m3.js";
    // 固定格式  默认暴露将要暴露的数据存放在default中,必须将default起别名
    console.log(m3);
    m3.say();
  3. 简便形式(只能用于默认暴露)

    js
    import m3 from "./src/js/m3.js";
    console.log(m3);
    m3.say();

注意:在使用script标签引入模块时要在加上 type="module"

html
<script type="module"></script>
2.20.6 浏览器使用ES6 模块化方式

将引入模块的语句放在入口文件 app.js 中

然后再将 app.js 引入html页面中

app.js

js
import * as m1 from "./m1.js";
import * as m2 from "./m2.js";
import m3 from "./m3.js";
console.log(m1);
console.log(m2);
console.log(m3);

index.html

html
<script src="" data-missing="app.js" type="module"></script>

将一个大文件分割成多个小文件,将代码分散简化

2.20.7 babel对ES6模块化代码转换

在项目中一般不直接使用script标签引入入口文件,而是使用 babel 将 js 文件编译打包后将引入

原因:

  • 不是所有的浏览器都识别 ES6 模块化语法
  • ES6 模块化不能直接对npm安装的模块直接导入

babel官网:将ES6 转换为 ES5

步骤

  1. 安装工具 babel-cli babel-preset-env browserify(打包工具)

    npm init

    npm i babel-cli babel-preset-env browserify -D

  2. 编译输出文件

    npx babel src/js -d dist/js --presets=babel-preset-env

  3. 打包

    npx browserify dist/js/app.js -o dist/bundle.js

  4. 引入打包好的入口 bundle.js 文件

    html
    <script src="" data-missing="bundle.js"></script>
2.20.8 ES6模块化引入npm包

需求:使用 jquery 修改html背景色

  1. 安装jquery

    npm i jquery

  2. 导入jquery

    js
    import $ from 'jquery'; 
    // 等同于使用 commonJS: const $ = require('jquery)
  3. 使用jquery修改背景色

    js
    $('body').css('background','pink');
  4. 重新编译打包

三、ES7 新特性

3.1 Array.prototype.includes

Includes 方法用来检测数组中是否包含某个元素,返回布尔类型值,功能与indexOf相同

js
// includes 判断指定元素是否在数组中
const names = ["张三", "李四", "王五"];
console.log(names.includes("李四")); // true
console.log(names.includes("赵六")); // false
// ES5 中使用 indexOf 返回值为下标,不存在返回-1
console.log(names.indexOf("李四")); // 1

3.2 指数操作符

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

js
// ** 幂运算
console.log(2 ** 10); // 1024
// ES5 中使用Math.pow()
console.log(Math.pow(2, 10)); // 1024

四、ES8 新特性

4.1 async 和 await

async 和 await 两种语法结合可以让异步代码像同步代码一样

用于处理异步编程

4.1.1 async 函数
  1. 在函数名前加 async 该函数就变成了一个异步函数,不会阻塞该函数后面代码的执行
  2. async 函数的返回值为 promise 对象,
  3. promise 对象的结果由 async 函数执行的返回值决定
js
async function fn() {
  // return返回的结果不是一个promise对象,则函数返回的结果为一个成功的promise对象
  // return 123;
  // 抛出错误,返回的结果是一个失败的promise对象
  // throw new Error('错误');
  // 返回结果是一个Promise对象
  return new Promise((resolve, reject) => {
    resolve("成功"); // 返回Promise的状态就是fn函数的状态
    // reject("失败");
  });
}

const result = fn();
console.log(result);

// 调用then方法
result.then(
  (value) => {
    console.log(value); // 成功
  },
  (reason) => {
    console.warn(reason);
  }
);
4.1.2 await 表达式
  1. await 必须写在 async 函数中 await语句会阻塞async函数await语句后面的代码执行,直到await右侧的Promise对象状态改变
  2. await 右侧的表达式一般为 promise 对象
  3. await 接收的是 promise 成功的值
  4. await 的 promise 失败了, 就会抛出异常, 需要通过 try...catch 捕获处理
js
// 创建Promise对象
const p = new Promise((resolve, reject) => {
  // resolve("用户数据");
  reject("失败了");
});

// await 要放在 async 函数中
async function main() {
  // Promise 失败使用 try...catch进行错误捕获
  try {
    // await 返回Promise成功的值
    let result = await p;
    console.log(result);
  } catch (e) {
    console.log(e);
  }
}

// 调用函数main
main();
4.1.3 async与await结合读取文件

需求:逐个读取resources文件夹下的三个文件,并将结果进行拼接

js
// 引入 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 readGuanShuYouGan() {
  return new Promise((resolve, reject) => {
    fs.readFile("./resources/观书有感.md", (err, data) => {
      // 如果失败
      if (err) reject(err);
      // 成功
      resolve(data);
    });
  });
}

// 三个读取文件函数返回的都是一个Promise对象,在async函数内使用await获取Promise成功的值,不再使用then进行回调处理,将异步操作变的像同步操作
// 声明一个 async 函数
async function main() {
  // 获取为学内容
  let weixue = await readWeiXue();
  // 获取插秧诗内容
  let chayang = await readChaYangShi();
  // 获取观书有感内容
  let guanshu = await readGuanShuYouGan();

  console.log(weixue + "\r\n" + chayang + "\r\n" + guanshu);
}

main();

使用asyc 和 await 可以代替Promise的then方法,让代码看起来能加同步化

4.1.4 saync与await封装AJAX请求

用async与await去接收AJAX请求返回的结果

js
// 发送 AJAX 请求 返回结果为promise对象
function sedAJAX(url) {
    // 返回一个promise对象 await后要跟promise对象
  return new Promise((resolve, reject) => {
    // 1.创建对象
    const x = new XMLHttpRequest();
    // 2. 初始化
    x.open("GET", url);
    // 3. 发送请求
    x.send();
    // 4. 事件绑定
    x.onreadystatechange = function () {
      if (x.readyState === 4) {
        if (x.status >= 200 && x.status < 300) {
          // 成功了
          resolve(x.response);
        } else {
          // 失败
          reject(x.status);
        }
      }
    };
  });
}
// Promise then方法测试
sedAJAX("").then((value) => {
  console.log(value);
});
sedAJAX("").then((value) => {
  console.log(value);
});

// async与 await 测试
async function main() {
  // 获取数据 await能得到后面promise成功的数据
  let result1 = await sedAJAX("https://api.apiopen.top/getJoke");
  console.log(result1);
  let reslut2 = await sedAJAX("");
  console.log(reslut2);
}
main();

常用:使用async await 发送 axios 请求

js
async function getDog() {
try {
 // result 接收请求成功返回的数据
 let result = await axios.get(
   "https://dog.ceo/api/breed/pembroke/images/random"
 );
 dogList.push(result.data.message); // 成功处理
} catch (error) { // 请求失败 error:错误信息
 alert(error); // 失败处理
}
}

4.2 对象扩展方法

4.2.1 Object.keys,Object.values 和 Object.entries
  1. Object.keys()方法返回一个给定对象的所有键组成的数组
js
// 声明一个对象
const school = {
  name: "xx",
  cities: ["北京", "上海", "深圳"],
  xueke: ["语文", "数学", "英语"],
};

// 获取对象所有的键
console.log(Object.keys(school));
// 使用for...of 和 Object.keys()遍历对象的键
for (let key of Object.keys(school)) {
  console.log(key);
}
  1. Object.values()方法返回一个给定对象的所有值组成的数组
js
// 声明一个对象
const school = {
  name: "xx",
  cities: ["北京", "上海", "深圳"],
  xueke: ["语文", "数学", "英语"],
};

// 获取对象所有的值
console.log(Object.values(school));
// 使用for...of 和 Object.values()遍历对象的值
for (let v of Object.values(school)) {
  console.log(v);
}
  1. Object.entries()方法返回一个给定对象键值组成的 [key,value] 数组
js
// 声明一个对象
const school = {
  name: "xx",
  cities: ["北京", "上海", "深圳"],
  xueke: ["语文", "数学", "英语"],
};

// 获取对象所有的键值对组成的数组
console.log(Object.entries(school));
// 创建一个map对象
const m = new Map(Object.entries(school));
console.log(m);
console.log(m.get("name"));
4.2.2 Object.getOwnPropertyDescriptors

该方法返回指定对象所有自身属性的描述对象

js
// Object.getOwnPropertyDescriptors 获取对象的描述对象
console.log(Object.getOwnPropertyDescriptors(school));

// 创建一个对象,并为其添加描述对象
const obj = Object.create(null, {
  name: {
    // 属性值
    value: "xx",
    // 属性特性
    writable: true,
    configurable: true,
    enumerable: true,
  },
});

五、ES9 新特性

5.1 .Rest/Spread 属性

Rest 参数与 spread 扩展运算符在 ES6 中已经引入,不过 ES6 中只针对于数组, 在 ES9 中为对象提供了像数组一样的 rest 参数和扩展运算符

reset参数

js
function connecct({ host, port, ...user }) { // 对象解构获取剩余参数
  console.log(host, port);
  console.log(user); // 返回一个对象
}
connecct({
  host: "127.0.0.1",
  port: 80,
  username: "root",
  password: "123456",
});

对象的reset参数用于获取参数为对象形式

扩展运算符

js
const one = {
  name: "张三",
  age: 18,
};
// 使用扩展运算符
// ...one => name:"张三",age:"18"
const two = {
  sex: "男",
  address: "北京",
};
const three = {
  phone: "13888888888",
  asy() {
    console.log("您好呀");
  },
};

// 使用扩展运算符合并对象
const user = { ...one, ...two, ...three };
console.log(user);

对象的扩展运算符可用于对象的合并

5.2 正则表达式命名捕获分组

ES9 允许命名捕获组使用符号『?』,这样获取捕获结果可读性更强

js
let str = '<a href="http://www.atguigu.com">尚硅谷</a>';
// 提取 url和标签文本 正则匹配
// 使用 (?<name>正则表达式) 进行分组
const reg = /<a href="(?<url>.*)">(?<text>.*)<\/a>/;
const result = reg.exec(str);
console.log(result.groups);
console.log(result.groups.url, result.groups.text);

5.3 正则表达式反向断言

ES9 支持反向断言,通过对匹配结果前面的内容进行判断,对匹配进行筛选。

正向断言匹配结果后面的内容进行判断

js
// 字符串
let str = "JS12345嘿嘿6789啦啦";
// 提取6789
// 正向断言 匹配结果后面的内容进行判断
const reg = /\d+(?=啦)/;
// \d+(?=啦)匹配以"啦"结尾的数字字符串,但不会将 "啦" 作为匹配结果返回
const result = reg.exec(str);
console.log(result);
// 反向断言
const reg2 = /(?<=嘿)\d+/;
//(?<=嘿)\d+ 匹配以嘿开头的数字字符串,但不会将 "啦" 作为匹配结果返回
const result2 = reg2.exec(str);
console.log(result2);

5.4 正则表达式 dotAll 模式

正则表达式中点.匹配除回车外的任何单字符,标记『s』改变这种行为,允许行终止符出现

js
// dot . 元字符 除换行符以外的任意单个字符
let str = `<ul>
        <li>
            <a>肖生克的救赎</a>
            <p>上映日期: 1994-09-10</p>
        </li>
        <li>
            <a>阿甘正传</a>
            <p>上映日期: 1994-07-06</p>
        </li>
    </ul>`;
// 需求:提取电影名称和上映时间
// 声明正则
/* const reg = /<li>\s+<a>(.*?)<\/a>\s+<p>(.*?)<\/p>/; */
const reg = /<li>.*?<a>(.*?)<\/a>.*?<p>(.*?)<\/p>/gs;
// 执行匹配
let result;
let data = [];
while ((result = reg.exec(str))) {
  data.push({ title: result[1], time: result[2] });
}
// 输出结果
console.log(data);

5.5 正则补充

img

img

六、ES10 新特性

6.1.Object.fromEntries

接收一个二维数组或者一个Map创建一个对象

与 Object.entries 将对象转换为二维数组

Object.entries 与 Object.fromEntries 互为逆运算

js
// 二维数组
const result = Object.fromEntries([
  ["name", "张三"],
  ["age", "18"],
  ["tel", "9892068902"],
]);
console.log(result);
// Map
const m = new Map([
  ["age", "18"],
  ["tel", "9892068902"],
]);
m.set("name", "张三");
const result1 = Object.fromEntries(m);
console.log(result1);

// Object.entries() 将对象转换为二维数组
const arr = Object.entries(result);
console.log(arr);

6.2.trimStart 和 trimEnd

ES5 trim:清除字符串两侧的空白字符

ES10 trimStart trimEnd 指定清除字符串左侧或右侧的空白字符

js
console.log(str);
console.log(str.trim()); // 清除字符串两侧空白
console.log(str.trimStart()); // 清除字符串左侧空白
console.log(str.trimEnd()); // 清除字符串右侧空白

6.3.Array.prototype.flat 与 flatMap

6.3.1 flat方法

将多维数组的维度降低

arr.flat([n])

可以传递参数,是一个数字,默认值为1

js
const arr = [
  [1, 2],
  [3, 4],
]; 
// 二维数组转一维数组
console.log(arr.flat()); // [1,2,3,4]

const arr2 = [
  [1, 2, [5, 6]],
  [3, 4],
];
// 三维数组转二维数组
console.log(arr2.flat()); //[1, 2, Array(2), 3, 4]
// 三维数组转一维数组
console.log(arr2.flat(2)); //[1, 2, 5, 6, 3, 4]
6.3.2 flatMap方法

将Map返回数组结果维度降低

js
const arr3 = [1, 2, 3, 4];
const result = arr3.flatMap((item) => item * 10);
console.log(result); //[10, 20, 30, 40]

6.4.Symbol.prototype.description

获取symbol的字符串描述

js
let s = Symbol('嘿嘿');
console.log(s.description); // 嘿嘿

七、 ES11 新特性

7.1.String.prototype.matchAll

用来获取正则批量匹配得到的结果

js
let str = `<ul>
        <li>
            <a>肖生克的救赎</a>
            <p>上映日期: 1994-09-10</p>
        </li>
        <li>
            <a>阿甘正传</a>
            <p>上映日期: 1994-07-06</p>
        </li>
    </ul>`;

// 需求:使用正则匹配获电影名称和上映时间
// 声明正则
const reg = /<li>.*?<a>(.*?)<\/a>.*?<p>(.*?)<\/p>/gs;
// 调用方法
const result = str.matchAll(reg);
console.log(result); // 返回结果为一个可迭代对象
for (let v of result) {
  console.log(v);
}

批量获取数据

7.2.类的私有属性

  1. 使用 # 声明私有属性
  2. 私有属性在类的外不能直接被访问
  3. 可以通过constructor进行赋值
  4. 私有属性不能被继承
js
class Person {
  // 公有属性
  name;
  // 私有属性
  #age;
  #weight;
  // 构造方法
  constructor(name, age, weight) {
    this.name = name;
    this.#age = age; // 为私有属性赋值
    this.#weight = weight; 
  }
  intro() {
    console.log(this.name); // 在类的内部访问私有属性
    console.log(this.#age);
    console.log(this.#weight);
  }
}

// 实例化
const girl = new Person("小红", 18, "50kg");
console.log(girl);

console.log(girl.name);
// console.log(girl.#age); 私有属性在类的外部能直接访问
girl.intro(); // 通过在类的内部封装方法进行访问

7.3.Promise.allSettled 和 Promise.all

7.3.1 Promise.allSettled
  1. 接收一个Promise数组返回一个Promise对象
  2. 返回结果永远为成功状态,成功的状态的值为每一个Promise对象状态和值

作用:批量处理Promise异步任务

js
// 声明两个Promise对象
const p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("商品数据 - 1 ");
  }, 1000);
});

const p2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    // resolve("商品数据 - 2 ");
    reject("出错了");
  }, 1000);
});

// 调用 allsettled方法
const reslut = Promise.allSettled([p1, p2]);
console.log(reslut);
// 无论接收的Promise对象是否为成功状态,返回的Promise都为成功
reslut.then((value) => {
  for (v of value) {
    console.log(v);
  }
});
7.3.2 Promise.all
  1. 接收一个Promise数组,返回一个Promise对象
  2. 当全部的Promise状态为成功返回状态为成功,有一个状态为失败,返回状态为失败
js
// 声明两个Promise对象
const p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("商品数据 - 1 ");
  }, 1000);
});

const p2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    // resolve("商品数据 - 2 ");
    reject("出错了");
  }, 1000);
});

const res = Promise.all([p1, p2]);
res
  .then((value) => {
    console.log(value);
  })
  .catch((err) => {
    console.log(err);
  });

7.4.可选链操作符

可选链操作符 ?.

使用可选链可避免对象参数层层判断

js
function main(config) {
  // 获取db的host属性
  // 先判断是否传入了值 避免报错
  // const dbHost = config && config.db && config.db.host;

  // 使用可选链操作符可以避免层层判断
  const dbHost = config?.db?.host;
  console.log(config.db.host);
}

main({
  db: {
    host: "192.168.1.1",
    username: "root",
  },
  cache: {
    host: "192.168.1.2",
    user: "admin",
  },
});

7.5.动态 import 导入

ES6的模块化是一个静态的模块化系统

动态 import 在需要时进行导入(用于路由懒加载)

语法:import(url)返回一个promise对象

js
// 静态引入
// import { hello } from "./hello.js";
// 获取元素
const btn = document.getElementById("btn");
btn.onclick = function () {
  // hello(); 使用
  // 动态引入  返回一个promise对象
  import('./hello.js').then(module => {
    console.log(module);
    module.hello();
  });
};

7.6 BigInt类型

  1. 大整型,在数字后面加上n
  2. 用于更大数的运算
  3. 只能和BigInt类型的数进行运算
js
// 大整型
let n = 123n;
console.log(n, typeof n); // 123n 'bigint'

// 函数  将普通的整数转换为大整型
let n1 = 123;
console.log(BigInt(n1)); //123n

// 作用:用于大数值的运算
let max = Number.MAX_SAFE_INTEGER; // 最大值
console.log(max);
console.log(max + 1);
console.log(max + 2); // 超出范围计算错误

console.log(BigInt(max)); // 借助BigInt计算
console.log(BigInt(max) + BigInt(1));
console.log(BigInt(max) + BigInt(10));

7.7 globalThis 对象

始终指向全局对象

在浏览器下的全局对象是window

在nodeJS下的全局对象是global