前端JavaScript前端

【前端 JavaScript 高级】 05 - ES6 - ES

2021-01-05  本文已影响0人  itlu

1. ECMASript 相关介绍

1.1 什么是 ECMA

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

1.2 什么是 ECMAScript

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

1.3 什么是 ECMA-262

  1. Ecma 国际制定了许多标准,而 ECMA-262 只是其中的一个, 所有标准列表查看

1.4 ECMA-262 历史

  1. ECMA-262(ECMAScript)历史版本查看网址
版 本 时 间 事 项
第 1 版 1997 年 制定了语言的基本语法
第 2 版 1998 年 较小改动
第 3 版 1999 年 引入正则、异常处理、格式化输出等。 IE 开始支持
第 4 版 2007 年 过于激进,未发布
第 5 版 2009 年 引入严格模式、 JSON,扩展对象、数组、原型、字符串、日期方法
第 6 版 2015 年 模块化、面向对象语法、Promise、箭头函数、 let、const、数组解构赋值等等
第 7 版 2016 年 幂运算符、数组扩展、Async/await 关键字
第 8 版 2017 年 Async/await、字符串扩展
第 9 版 2018 年 对象解构赋值、正则扩展
第 10 版 2019 年 扩展对象、数组方法
ES.next 动态指向下一个版本

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

1.5 谁在维护 ECMA-262

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

1.6 为什么要学习 ES6

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

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

  3. ES6 是前端发展趋势,就业必备技能。

1.7 ES6 兼容性

  1. 可查看兼容性

2. ECMASript 6 新特性

2.1 let关键字

let 关键字用来声明变量,使用 let 声明的变量有几个特点:
  1. 不允许重复声明变量;

  2. 有块儿级作用域;

  3. 不存在变量提升(预解析),如之前使用 var关键字声明变量时会将使用 var 声明 提到最前面。var num = 10; 变量提升之后 是将 var num; 提到最前面之后在进行赋值操作;

  4. 不影响作用域链。

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

  let a;
  let b, c, d;
  let e = 100;
  let f = 222, g = 'love you', h = [];
  
  // 1. 变量不能重复声明
  // let star = '李连杰';
  // let star = '吴京';

  // 2. 使用var声明的变量是可以重复的
  // var star = '李连杰';
  // var star = '吴京';

  // 2. 块级作用域全局 函数,eval

  {
    let boy = 'lyp';
  }

  console.log(boy); // 这里是访问不到的


  // 3. 不存在变量提升

  var song = '666';

  // 4. 不影响作用域链
  {
    let school = '王者学院';

    function fun() {
      console.log(school);
    }
  }

2.2 const 关键字

const 关键字用来声明常量,const 声明有以下特点:
  1. 声明必须赋初始值;

  2. 标识符一般为大写;

  3. 不允许重复声明;

  4. 值不允许修改;

  5. 块儿级作用域。

注意: 对象属性修改和数组元素变化不会触发 const 错误。 应用场景:声明对象类型使用 const,非对象类型声明选择 let

  // 声明常量
  const SCHOOL = '王者学院';

  // 1. 常量的声明一定要初始化

  const A;

  // 2. 常量的值不能修改
  SCHOOL = '666';

  // 3. const 有块级作用域
  {
    const UZI = '乌兹';
  }

  console.log(UZI);

  // 4. 一般常量标识符需要大写

  // 5. 对于数组和对象的元素修改,不算对常量的修改,不会报错

  const TEAM = ['王大', '王二', '王三'];

  TEAM.push('王四');

2.3 变量的解构赋值

  1. ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构赋值。
  // ES6允许按照一定的模式从数组或者对象中进行提取值,对变量进行赋值
  // 这就被称为解构赋值
  // 1. 数组的解构
  const F4 = ['宋小宝', '小沈阳', '刘能', '赵四'];

  let [song, xiao, liu, zhao] = F4;
  console.log(song);
  console.log(xiao);
  console.log(liu);
  console.log(zhao);

  // 2. 对象的解构
  const obj = {
    name: '赵本山',
    age: '66',
    xiaopin: function () {
      console.log('我会演小品');
    }
  }

  let {xiaopin} = obj;
  // 解构之后直接调用
  xiaopin();

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

2.4 模板字符串

模板字符串(template string)是增强版的字符串, 用反引号(`)标识,特点:
  1. 字符串中可以出现换行符;

  2. 可以使用 ${xxx}形式输出变量。

  // ES6中引入新的声明字符串的方式 ``   '' ""

  // 1. 声明
  let str = `我是一个字符串`;
  console.log(str, typeof str);

  // 2. 使用模板字符串之后可以直接出现换行符
  let star = `
              <h1>张三</h1>
              <h1>李四</h1>
              <h1>王五</h1>
              <h1>赵六</h1>
            `;
  console.log(star);

  // 3. 模板字符串中的字符串拼接
  let lovest = '欢欢'
  let starest = `${lovest}是我最喜欢的人`;
  console.log(starest);

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

2.5 简化对象写法

  1. ES6 允许在大括号里面,直接写入变量和函数,作为对象的属性和方法。这样的书写更加简洁。
let name = '王者学院';
let slogon = '666';
let improve = function () {
    console.log('可以提高你的技能');
}
//属性和方法简写
let atguigu = {
  name,
  slogon,
  improve,
  change() {
      console.log('可以改变你')
  }
};

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

2.6 箭头函数

  1. ES6 中的箭头函数 ()=> 定义函数。
/*******************************************************************************************/
  // ES6中的箭头函数 ()=> 定义函数
  /**
   * 传统的声明函数的方式
   */
  let fn = function (a, b) {
    return a + b;
  };

  console.log(fn(12, 32));

  /**
   * 使用箭头函数的方式声明一个函数
   */
  let fun = (a, b) => {
    return a + b;
  };

  // 调用函数
  console.log(fun(1, 3));

  /*******************************************************************************************/

  /**
   * 箭头函数 和 传统函数声明的区别
   *
   * 1. this是静态的。 this始终是指向函数 声明时 所在作用域 的 this 的值 。无论怎么改变其this都是不会改变的
   *
   */
  function getName() {
    console.log(this.name); //
  }

  let getNameTo = () => {
    console.log(this.name);//
  };

  // 设置 widow  对象的name属性
  window.name = 'windowyo';
  const SCHOOL = {
    name: 'ATLYP'
  };

  // 使用call调用改变它的this指向
  getName.call(SCHOOL); // 调用传统方式声明的函数并改变其this指向  ATLYP  指向改变了
  getNameTo.call(SCHOOL); // 调用箭头函数方式声明的函数并改变其this指向 windowyo 指向未改变

  /*******************************************************************************************/

  /**
   * 箭头函数不能作为构造函数创建对象的
   * @param name
   * @param age
   * @constructor
   */
  let Person = (name, age) => {
    this.name = name;
    this.age = age;
  };
  // let p = new Person('张三', 23); // Person is not a constructor 箭头函数声明的构造函数不能创建对象
  /*******************************************************************************************/

  /**
   * 箭头函数中是不能使用arguments变量
   */
  function useArguments(a, b) {
    console.log(arguments);
  }

  useArguments(12, 34);

  let notArguments = (a, b) => {
    // console.log(arguments);
  }
  notArguments(23, 56); // arguments is not defined
  /*******************************************************************************************/

  /**
   * 箭头函数的简写
   *
   *  1. 省略小括号,当形参有且只有一个的时候
   *
   *  2. 可以省略花括号 , 当代码体中只有一条语句的时候,此时return也必须省略,而且函数的执行结果就是函数的返回值
   */

    // 1. 省略小括号
  let add = n => {
      return n + n;
    };

  console.log(add(20)); // 40

  // 2. 省略花括号
  let pow = n => n * n;
  console.log(pow(20)); // 400
  /*******************************************************************************************/
箭头函数的注意点:
  1. 如果形参只有一个,则小括号可以省略;

  2. 函数体如果只有一条语句,则花括号可以省略,函数的返回值为该条语句的执行结果;

  3. 箭头函数 this 指向声明时所在作用域下 this 的值;;

  4. 箭头函数不能作为构造函数实例化;

  5. 不能使用 arguments

  6. 箭头函数中this是静态的。 this始终是指向函数 声明时 所在作用域 的 this 的值 。无论怎么改变其this都是不会改变的

箭头函数和实际的应用场景
  1. 箭头函数中this是静态的。 this始终是指向函数 声明时 所在作用域 的 this 的值 。无论怎么改变其this都是不会改变的

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

  3. 箭头函数不适合与this有关的回调,如 事件回调、对象的方法。

// 获取dom元素
  let ad = document.querySelector('.ad');

  /**
   * 箭头函数不适合作为事件回调 ,但是适合定时器,数组方法等
   */
  ad.addEventListener('click', function () {
    // 事先保存一个 this
    let _this = this;
    // 在点击盒子之后2秒改变盒子的颜色
    setTimeout(() => {
      // 注意这里的this 因为这里是一个箭头函数,箭头函数中的this是静态的,其是始终指向声明时所在作用域的this的值
      // _this.style.backgroundColor = 'pink';
      this.style.backgroundColor = 'pink';
    }, 2000)
  });

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

  // 过滤数组中的元素,找出其中的偶数
  let arr = [1, 2, 3, 9, 90, 78];

  /**
   *
   * 使用传统function声明函数的方式
   * */
  let newArrFunction = arr.filter(function (item) {
    return item % 2 === 0;
  });
  console.log(newArrFunction);

  /**
   * 使用箭头函数的方式
   * @type {number[]}
   */
  let newArr = arr.filter(item => item % 2 === 0);
  console.log(newArr);

  // 箭头函数不适合与this有关的回调,如事件回调、对象的方法

注意:箭头函数不会更改 this 指向,用来指定回调函数会非常合适。

2.7 rest 参数

  1. ES6 引入 rest 参数,用于获取函数的实参,用来代替 arguments
  // ES6引入rest参数用于获取函数的实参,用来代替arguments
  // ES5中获取实参的方式
  function es5Arguments(a, b, c) {
    console.log(arguments); // 这是一个对象 {1, 2, 3}
  }

  es5Arguments(1, 2, 3);

  // ES6 中的rest参数
  /**
   * rest参数必须放在参数的最后
   */
  function es6Rest(a, b, ...args) {
    console.log(a);
    console.log(b);
    console.log(args); // 这是一个数组 [56, 89, 784, 12, 456, 456]
  }

  es6Rest(12, 23, 56, 89, 784, 12, 456, 456);

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

ES6中函数参数的默认值
  1. ES6中为形参赋初始值 , 具有默认值的形参的位置一般靠后

  2. 参数默认值可以与解构赋值结合使用。

  /**
   * ES6中为形参赋初始值 , 具有默认值的形参的位置一般靠后
   * @param a
   * @param b
   * @param c
   */
  function add(a = 10, b = 20, c = 30) {
    return a + b + c;
  }


  // 2. 参数默认值可以与解构赋值结合使用
  function connect({host = '45.56.47.48', username = 'root', password = '123456', port = '3306'}) {
    console.log(host);
    console.log(username);
    console.log(password);
    console.log(port);
  }


  connect({
    host: '127.0.0.1',
    username: 'root',
    password: 'root',
    port: '3306'
  });

2.8 spread 扩展运算符

  1. 扩展运算符(spread)也是三个点(...)。它好比 rest 参数的逆运算,将一个数组转为用逗号分隔的参数序列,对数组进行解包。
 // 扩展运算符将数组转换为逗号分隔的参数序列
  const tfBoy = ['易烊千玺', '王源', '张三'];

  function shaBi() {
    console.log(arguments);
  }
  shaBi(...tfBoy);
// 展开运算符 里面有引用数据类型的话也是一个浅拷贝
  let oldArr = ['j', 'f', 'r', 'p'];

  let newArr = ['j', 'a', 'v', 'a'];

  newArr = oldArr.concat(newArr);

  console.log(newArr);

  let conCatArr = [...oldArr, ...newArr];
  console.log(conCatArr);

  // 将伪数组转换为真正的数组
  let divs = document.querySelectorAll('div'); // 这是一个伪数组
  // 将伪数组转换为真实的数组
  console.log(divs);
  let newDivs = [...divs];
  console.log(newDivs);

2.9 Symbol

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

  2. s string symbol

  3. o Object

  4. n null number

  5. b boolean

Symbol 特点
  1. Symbol 的值是唯一的,用来解决命名冲突的问题。

  2. Symbol 值不能与其他数据进行运算。

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

// 向对象中添加属性和方法 up down
  let game = {
    name: '张三',
    up: function () {
      console.log('我是up');
    },
    down: function () {
      console.log('我是down');
    }
  };

  let methods = {
    up: Symbol(),
    down: Symbol()
  };

  game[methods.up] = function () {
    console.log('我可以向上');
  };

  game[methods.down] = function () {
    console.log('我可以向下');
  };

  console.log(game);

/****************************************************************************************/
  // 第二种方式

  let obj = {
    name: '張三',
    up: function () {
      console.log('上');
    },
    [Symbol('say')]: function () {
      console.log('说话');
    },
    [Symbol('sing')]: function () {
      console.log('唱歌');
    }
  }

  console.log(obj);

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

Symbol 内置值
  1. 除了定义自己使用的 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.isConcatSpreadable设置数组是否可以进行拆解。
  // Symbol 内置了 11 个属性
  // [Symbol.hasInstance]
  class Person {
    static [Symbol.hasInstance](param) {
      console.log(param);
      console.log('我是用来检测类型的');
      // 根据return 返回确定 false 或者 true
    }
  }

  let obj = {};

  console.log(obj instanceof Person);

  // 数组是否拆解合并
  let oldArr = [1, 2, 3, 4];
  let newArr = [4, 5, 6, 7];
  newArr[Symbol.isConcatSpreadable] = false; // 设置是否拆解进行连接
  newArr = oldArr.concat(newArr);
  console.log(newArr);

2.10 迭代器

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

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

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

Array
Arguments
Set
Map
String
TypedArray
NodeList

工作原理
  1. 创建一个指针对象,指向当前数据结构的起始位置;

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

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

  4. 每调用 next方法返回一个包含 valuedone属性的对象。

自定义遍历数据
// 声明一个对象
  const banji = {
    name: '终极一班',

    stu: [
      'zhansgan',
      'lisi',
      'wangwu',
      'zhaoliu'
    ],
    /**
     *
     * 重写迭代器遍历对象中的数组
     * */
    [Symbol.iterator]() {
      let index = 0;

      // 保存 this
      let _this = this;

      return {
        next: function () {
          if (index < _this.stu.length) {
            // 这是迭代器中返回的结果想
            let result = {value: _this.stu[index], done: false};
            index++;
            return result;
          } else {
            return {value: undefined, done: true};
          }
        }
      }

    }
  };

  /**
   * 使用for ... of ... 遍历
   */
  for (let item of banji) {
    console.log(item);
  }

2.11 生成器

  1. 生成器函数是ES6 提供的一种异步编程解决方案,语法行为与传统函数完全不同。
 // 生成器是一个函数 是一个异步的编程解决方案
  // 异步编程使用的是纯回调函数
  // yield 是函数代码的分割符
  function* gen() {
    console.log('hello generator');
    console.log('111');
    yield '你是小猪猪';
    console.log('222');
    yield '你的小尾巴';
    console.log('333');
    yield '我你';
    console.log('444');
  }

  let iterator = gen();

  console.log(iterator);

  // 调用生成器函数
  // console.log(iterator.next());
  // console.log(iterator.next());
  // console.log(iterator.next());
  // console.log(iterator.next());

  // 可以使用 for...of... 进行遍历
  for (let item of gen()) {
    console.log(item);
  }
代码说明:
  1. * 的位置没有限制;

  2. 生成器函数返回的结果是迭代器对象,调用迭代器对象的 next 方法可以得到yield 语句后的值;

  3. yield相当于函数的暂停标记,也可以认为是函数的分隔符,每调用一次 next方法,执行一段代码;

  4. next 方法可以传递实参,作为 yield 语句的返回值。

生成器函数的参数
 /**
   * 生成器函数
   * @param arg
   */
  function* gen(arg) {
    console.log(arg); // 666
    let one = yield '1111';
    console.log(one); // 第一个参数
    let two = yield '2222';
    console.log(two); // 第二个参数
    let three = yield '3333';
    console.log(three); // 第三个参数
  }

  // next中可以传递参数,参数将作为yield的返回值返回。
  let iterator = gen('666');
  iterator.next();
  iterator.next('第一个参数');
  iterator.next('第二个参数');
  iterator.next('第三个参数');
案例 :1s 以后在控制台输出 111 , 2s之后输出 222 3s之后输出333
1s111 00_00_00-00_00_30.gif
  1. 传统的定时器嵌套调用的方式 :容易形成回调地狱
  // 下面这是回调地狱
  setTimeout(() => {
    console.log(111);
    setTimeout(() => {
      console.log(222);
      setTimeout(() => {
        console.log(333);
      }, 3000);
    }, 2000);
  }, 1000);
  1. 使用生成器函数解决回调地狱问题
/**
   * 使用生成器函数解决回调地狱问题
   */
  function one() {
    setTimeout(() => {
      console.log('444');
      // 执行完成调用下一次
      iterator.next();
    }, 1000);
  }


  function two() {
    setTimeout(() => {
      console.log('555');
      // 执行完成调用下一次
      iterator.next();
    }, 2000);
  }

  function three() {
    setTimeout(() => {
      console.log('666');
      // 执行完成调用下一次
      iterator.next();
    }, 3000);
  }

  let iterator = gen();

  /**
   * 生成器函数
   */
  function* gen() {
    yield one();

    yield two();

    yield three();
  }

  iterator.next();
案例 :分时段(先后)请求数据 ,先请求用户数据,在请求订单数据,最后请求商品数据
异步获取数据 00_00_00-00_00_30.gif
function getUsers() {
    setTimeout(() => {
      let data = '用户数据';
      iterator.next(data); // 通过next 将参数传递过去
    }, 1000);
  }

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

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

  /**
   * 生成器函数
   */
  function* gen() {
    // 接收第二个next传递过来的参数
    let user = yield getUsers();
    console.log(user);

    let order = yield getOrders();
    console.log(order);

    let good = yield getGoods();
    console.log(good);
  }

  let iterator = gen();
  // 第一次调用
  iterator.next();

2.12 Promise

  1. Promise 是 ES6 引入的异步编程的新解决方案。语法上 Promise 是一个构造函数,用来封装异步操作并可以获取其成功或失败的结果。
Promise 解决回调地狱问题
  /**
   * promise解决回调地狱的问题
   * @type {Promise<any>}
   */
    // 实例化 Promise的对象
  const p = new Promise(function (resolve, reject) {
      setTimeout(function () {
        /*// 成功的情况
        let data = '数据库中的用户数据';
        // resolve 调用它代表成功
        resolve(data);*/
        /*************************************************************/
        /*// 失败的情况*/
        let error = '获取数据库数据失败';
        reject(error); // reject 代表失败
      }, 5000);
    });

  // 调用promise对象的then方法
  p.then(function (value) {
    // 成功
    console.log(value);
  }, function (reason) {
    // 失败
    console.error(reason);
  });
Promise + Node 实现简单的文件读取
  1. 使用传统的方式 :
//1. 引入fs模块
const fs = require('fs'); // 需要node环境的支持

// 2. 调用方法读取文件
/**
 * 读取文件内容 data中是一个· buffer
 */
fs.readFile('../resources/666.md', (error, data) => {
  // 如果失败将会抛出异常
  if (error) {
    throw error;
  }
  console.log(data.toString());
});
  1. 使用Promise的方式 :
//1. 引入fs模块
const fs = require('fs'); // 需要node环境的支持
// 3. 使用 promise 修改读取文件的操作
const p = new Promise(function (resolve, reject) {
  fs.readFile('../resources/666.md', (err, data) => {
    if (err) {
      // reject 是拒绝的意思
      reject(err);
    }
    // resolve 是解决的意思
    resolve(data);
  })
});
p.then(function (value) {
  console.log(value.toString());
}, function (reason) {
  console.log(reason);
});
使用Promise发送一个Ajax请求
  1. 如果有一种情况是需要先发送一个请求之后再发送另一个请求,如果请求多的情况下可能出现回调地狱的情况。
const p = new Promise((resolve, reject) => {
  // 1. 创建一个对象
  const xhr = new XMLHttpRequest();
// 2. 初始化
  xhr.open('GET', 'url');
// 3. 发送
  xhr.send();
// 4. 绑定事件
  xhr.onreadystatechange = function () {
    // 判断
    if (xhr.readyState === 4) {
      // 判断状态码 处于 200 - 299 之间
      if (xhr.status >= 200 && xhr.status <= 299) {
        // 表示成功
        // console.log(xhr.response);
        // 表示成功
        resolve(xhr.response);
      } else {
        // console.log(xhr.status);
        reject(xhr.status);
      }
    }
  };
});

/**
 * 使用then处理结果,解决回调地狱的问题
 */
p.then((value) => {
  console.log(value);
}, (reason) => {
  console.log(reason);
})
Promise.prototype.then 调用then方法then方法的返回结果是 Promise对象,对象状态由回调函数的执行结果决定。
  1. 如果回调函数中返回的结果是非Promise 类型的属性状态为成功,返回值为对象成功的值。

  2. 如果没有return返回结果,也是一个非Promise对象 ,此时Promise类型属性状态为成功。

  3. 如果回调函数中返回的结果是一个Promise类型的对象。

  4. 如果抛出错误也是失败的Promise的错误。

 const p = new Promise((resolve, reject) => {
   setTimeout(() => {
     resolve('用户数据');
   }, 1000)
});
 const result = p.then((value) => {
    console.log(value);
    // return 123; // fulfilled 是完成的意思
    // 如果抛出异常
    // throw new Error('发生了错误'); // rejected
    // 如果回调函数中返回的是一个 Promise 对象
    return new Promise((resolve, reject) => {
      resolve('解决'); // fulfilled
    });
  }, reason => {
    console.log(reason);
  });

 console.log(result);
Promise.prototype.catch 方法类似一个语法糖
 const p = new Promise(
    (resolve, reject) => {
      setTimeout(() => {
        reject('出错了!');
      }, 1000)
    }
  );

  /**
   * catch类似一个语法糖
   */
  p.catch((reason) => {
    console.warn(reason);
  });

2.13 Set

  1. ES6 提供了新的数据结构Set(集合) 。它类似于数组,但成员的值都是唯一的,集合实现了iterator 接口,所以可以使用『扩展运算符』和『for…of…』进行遍历,集合的属性和方法:
  1. size 返回集合的元素个数;
  2. add 增加一个新元素,返回当前集合;
  3. delete 删除元素,返回 boolean 值;
  4. has 检测集合中是否包含某个元素,返回 boolean 值;
  5. clear 清空集合,返回undefined
  let s = new Set();
  console.log(typeof s);
  let s2 = new Set(['张三', '李四', '王五', '赵六']);
  console.log(s2.size);// 4
  // add
  s2.add('田七');
  console.log(s2); //
  // delete
  s2.delete('张三');
  // has
  console.log(s2.has('田七')); // true
  // clear
  // s2.clear();
  console.log(s2);
  // Set集合实现了 iterator 接口
  // 打印集合
  for (let item of s2) {
    console.log(item); // 李四 王五 赵六 田七
  }
Set 集合实践:数组去重 + 求交集、并集、差集
  1. 交集:两个数组中同时拥有的元素。

  2. 并集:在数组一中有,在数组二中也有。

  3. 差集:求数组一和数组二的差集。在数组一中存在,但是在数组二中不存在的元素。

 let arr = [1,2,3,4,5,4,3,2,1];
  // 1. 数组去重
  let result = [...new Set(arr)]; // 元素唯一的数组
  console.log(result); // [1,2,3,4,5]
  // 2. 交集,在数组1 和数组2 中同时存在的元素
  let array = [1,2,6,8,9,5,4];
  // 首先将数组转换为一个集合再判断 arr 中的元素是否在 array 中存在
  let publicRes = arr.filter(item => new Set(array).has(item));
  // 将得到的数组通过集合去重
  console.log([...new Set(publicRes)]);// 去重 [1,2,4,5]
  // 3. 并集 在数组1中有在数组2中也有
  let union = [...new Set([...array, ...arr])];
  console.log(union); // [1,2,6,8,9,5,4,3]
  // 4. 差集 arr 中存在的元素在 array 中不存在的
  let diff = array.filter(item => !(new Set(arr).has(item)));
  console.log(diff); // [6,8,9]
集合与数组之间的转换
 let oldArr = [3,2,1,5,6,89,5];
  // 数组转集合
  let oldSet = new Set(oldArr);
  console.log(oldSet); // {3, 2, 1, 5, 6, 89,5}
  // 集合转数组
  let newArr = [...oldSet];
  console.log(newArr); // [3, 2, 1, 5, 6, 89]

2.14 Map

  1. ES6 提供了 Map 数据结构。它类似于对象,也是键值对的集合。 但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键Map 也实现了iterator 接口,所以可以使用『扩展运算符』和『for…of…』进行遍历。 Map 的属性和方法:
  1. size 返回 Map 的元素个数;
  2. set 增加一个新元素,返回当前 Map;
  3. get 返回键名对象的键值;
  4. has 检测 Map 中是否包含某个元素,返回boolean 值;
  5. clear 清空集合,返回 undefined;
  // 声明Map
  let m = new Map();

  // 添加元素
  m.set('name', '仙桃');

  m.set('change', function () {
    console.log('我发生了改变');
  });

  // 键是一个对象
  let key = {
    school: 'XT'
  };

  m.set(key, ['张三', '李四', '王五']);

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

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

  console.log(m); //
  // 获取
  console.log(m.get(key)); // ['張三','李四','王五']
  console.log(m.get('change')); // f() {console.log('我发生了改变');}

  // 清空
  m.clear();

  console.log(m); // Map(0)

2.15 class 类

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

  2. constructor 定义构造函数初始化;

  3. extends 继承父类;

  4. super 调用父级构造方法;

  5. static 定义静态方法和属性;

  6. 父类方法可以重写。

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

  // 添加方法 , 使用原型对象添加实现共享
  Phone.prototype.call = function () {
    console.log('我可以打电话');
  }

  // 实例化对象
  let huawei = new Phone('荣耀', 3999);

  huawei.call();
  console.log(huawei);


  class Ps5 {
    constructor(brand, price) {
      this.brand = brand;
      this.price = price;
    }

    play() {
      console.log('我可以打游戏');
    }
  }

  // 创建对象实例
  let ps = new Ps5('任天堂',2999);
  ps.play();
  console.log(ps);
类里面的静态成员
  /**
   * 在ES5中*************************************************
   */
  function Phone() {

  }

  Phone.name = '我是静态成员'; // 属于函数对象的 不属于实例对象 为静态成员
  Phone.change = function () {
    console.log('我可以改变世界');
  }; // 属于函数对象的 不属于实例对象 为静态成员
  let nokia = new Phone();
  console.log(nokia.name); // undefined
  Phone.prototype.size = '5.5';
  console.log(nokia.size);

  /**
   * 在ES6中************************************************
   */
  class Person {
    // 在类中声明一个静态成员
    static name = '王五';
    age = 23;
  }

  console.log(Person.name);
  console.log(Person.age); // undefined
  let p = new Person();

  console.log(p.age);
  console.log(p.name); // undefined
ES5中实现继承
  /**
   * ES5中实现类的继承
   */
  function Phone(brand, price) {
    this.brand = brand;
    this.price = price;
  }

  function OnePlus(brand, price, color, size) {
    Phone.call(this, brand, price);
    this.color = color;
    this.size = size;
  }

  // 设置子级构造函数的原型
  OnePlus.prototype = new Phone();
  OnePlus.prototype.constructor = OnePlus;

  // 声明子类方法
  OnePlus.prototype.photo = function () {
    console.log('我可以打电话');
  }

  OnePlus.prototype.playGame = function () {
    console.log('我可以玩游戏');
  }

  // 创建onPlus 实例
  let onePlus = new OnePlus('1+', 2999, '红色', 5.5);
  console.log(onePlus);
  onePlus.photo();
  onePlus.playGame();

  // 原型对象 和 对象原型
  console.log(OnePlus.prototype);
  console.log(onePlus);
ES6 中类的继承
  1. 也可以对继承的方法进行重写,但是在重写的方法中不能通过super关键字调用父类的该方法。
class Phone {
    constructor(brand, price) {
      this.brand = brand;
      this.price = price;
    }

    call() {
      console.log('我可以打电话');
    }

  }

  class OnePlus extends Phone {

    /**
     * 构造函数
     */
    constructor(brand, price, color, size) {
      super(brand, price); // Phone(this,brand,price);
      this.color = color;
      this.size = size;
    }

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

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

  let onePLus = new OnePlus('1+', 3999, '绿色', 6.6);
  onePLus.photo(); // 拍照
  onePLus.playGame(); // 玩游戏
  onePLus.call(); // 我可以打电话
setter 和 getter 方法
 // get 和 set
  class Phone {
    get price() {
      console.log('获取价格');
      return '2999';
    }

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

  let s = new Phone();
  console.log(s.price);
  s.price = '3999';
  console.log(s.price);

2.16 ES6中数值的扩展

二进制和八进制
  1. ES6 提供了二进制和八进制数值的新的写法,分别用前缀 0b 和 0o 表示。
Number.isFinite() 与 Number.isNaN()
  1. Number.isFinite() 用来检查一个数值是否为有限的;
  1. Number.isNaN() 用来检查一个值是否为 NaN
Number.parseInt() 与 Number.parseFloat()
  1. ES6 将全局方法 parseInt 和 parseFloat,移植到 Number 对象上面,使用不变。
Math.trunc
  1. 用于去除一个数的小数部分,返回整数部分。
Math.sign
  1. 判断一个数到底是 正数 还是负数 还是零。正数返回 1 ,负数返回 -1 零返回 0
代码演示
// 1. Number.EPSILON 是JavaScript表示的最小精度
  console.log((0.1 + 0.2)); // 0.30000000000000004

  // 2. 二进制和八进制

  // 二进制
  let b = 0b0101;
  console.log(b); // 5

  // 八进制
  let o = 0o777;
  console.log(o); // 511

  // 十进制
  let d = 10106;
  console.log(d); // 10106

  // 十六进制
  let x = 0xfff;
  console.log(x);// 4095

  // 3. Number.isFinite() 检测一个数值是否是一个有限数
  console.log(Number.isFinite(10)); // true
  console.log(Number.isFinite(100 / 0)); // false
  console.log(Number.isFinite(Infinity)); // false

  // 4. Number.isNaN() 检测一个数值是否为NaN
  console.log(Number.isNaN(123));// false
  console.log(Number.isNaN(Number.parseInt('kk456')));// true

  // 5. Number.parseInt Number.parseFloat字符串转整数或者浮点数
  let str = '123456';
  console.log(str); // 黑色字符串
  let num = Number.parseFloat(str);
  console.log(num);// 蓝色数字

  str = 'kk456';
  num = Number.parseInt(str);
  console.log(num);

  // 6. Number.isInteger 判断一个数是否为整数
  let intNum = 123;
  console.log(Number.isInteger(intNum)); // true

  // 7. Math.trunc() 将数字的小数部分抹掉
  let floatNum = 123.365;
  console.log(Math.trunc(floatNum)); // 123

  // 8. Math.sign 判断一个数到底是 正数 还是负数 还是零
  console.log(Math.sign(10)); // 1
  console.log(Math.sign(0)); // 0
  console.log(Math.sign(-10)); // -1
Number.isInteger
  1. Number.isInteger() 用来判断一个数值是否为整数。

2.17 对象扩展

ES6 新增了一些 Object 对象的方法
  1. Object.is 比较两个值是否严格相等,与『===』行为基本一致(+0 与 NaN); 在其他情况下的比较情况下NaN和谁比较都是false。但是在这里 NaNNaN 比较就是true

  2. Object.assign 对象的合并,将源对象的所有可枚举属性,复制到目标对象;

  3. __proto__setPrototypeOfsetPrototypeOf 可以直接设置对象的原型。

// 1. Object.is 判断两个值是否完全相等
  console.log(Object.is(120, 120)); // true
  console.log(Object.is(NaN, NaN)); // true
  console.log((NaN === NaN));// false
  // NaN 不管和谁使用 === 做比较都是false

  // 2.Object.assign 对象合并
  const configA = {
    driver: 'jdbc.mysql.DataManagerDriver',
    url: 'localhost',
    username: 'root',
    password: 'root',
    test: 'test'
  };

  const configB = {
    driver: 'jdbc.mysql.DataManagerDriver',
    url: '127.0.0.1',
    username: 'admin',
    password: '12345'
  }
  console.log(Object.assign(configA, configB));
  console.log(configA);// 已经合并
  console.log(Object.assign(configB, configA));

  // 3. 设置原型对象 Object.setPrototypeOf = 设置原型对象
  const school = {
    school: '仙桃大数据学院'
  };

  const area = {
    area: '渝北'
  };

  Object.setPrototypeOf(school, area);

  console.log(school);

  // 4. 获取原型 Object.getPrototypeOf = 获取原型对象
  console.log(Object.getPrototypeOf(school));

2.18 模块化

  1. 模块化是指将一个大的程序文件,拆分成许多小的文件,然后将小文件组合起来。
模块化的好处
  1. 防止命名冲突;

  2. 代码复用;

  3. 高维护性。

模块化规范产品
ES6 之前的模块化规范有:
  1. CommonJS => NodeJS、 Browserify

  2. AMD => requireJS

  3. CMD => seaJS

ES6 模块化语法
模块功能主要由两个命令构成: exportimport
  1. export 命令用于规定模块的对外接口;

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

export的三种暴露方式
(一) export 分别暴露的方式
/**
 * 下面的属于分别暴露
 * @type {string}
 */
export let school = '仙桃大数据学院';

export function teach() {
  console.log('传授大数据开发经验');
}
(二)export统一暴露的方式
/**
 * 统一暴露
 * @type {string}
 */
let school = '仙桃大数据学校';

function stuData() {
  console.log('学习大数据');
}

/**
 * 统一暴露
 */
export {
  school,
  stuData
}
(三)export 默认暴露的方式
export default {
  school:'仙桃大数据学院',
  change:function () {
    console.log('为技术而改变');
  }
}
import 的三种引入方式
(一) 使用解构赋值的方式引入
 // m1
  import {school,teach} from '../js/m1.js';
  console.log(school);
  teach();
(二) 如果变量中出现了变量名重复的情况使用as进行重命名
// m2
  import {school as xt,stuData} from '../js/m2.js';

  console.log(xt);
  stuData();
(三) 导入默认暴露方式有两种方式
// m3 导入默认暴露
  import {default as m3} from '../js/m3.js';

  console.log(m3);

  // 简写形式只针对默认暴露
  import m3Default from '../js/m3.js';
  console.log(m3Default);
  m3Default.change();
浏览器中使用模块化的方式
  1. 将模块的引入统一写到一个JS文件中 app.js
// 入口文件

// 模块引入
import * as m1 from '../js/m1.js';
import * as m2 from '../js/m2.js';
import * as m3 from '../js/m3.js';
  1. 在页面文件引入,app.js并将script的类型设置为module
<!--类型需要设置成module-->
<script  src="../js/app.js" type="module"></script>
ES6 中使用babel对ES6模块化代码进行转换使其通用
步骤
  1. 安装工具:babel-clibabel-preset-env (预设包)、browserify (webpack)(打包工具)。

  2. 使用该安装命令 : npm i babel-cli babel-preset-env browserify -D 进行安装。

  3. 执行babel 命令(非全局安装使用npx): npx babel js -d dist/js --presets=babel-preset-env

  4. 打包: npx browserify dist/js/app.js -o dist/bundle.js

ES6模块化规范和NPM包的使用
  1. 使用npm 命令安装 jquery: npm i jquery ;

3. ES7 新特性

3.1 Array.prototype.includes

  1. Includes 方法用来检测数组中是否包含某个元素,返回布尔类型值。和原来数组中的 indexOf() 方法进行区分。indexOf() 返回的值是元素的位置,而includes方法返回的是true 或者false
 // includes
  const four = ['西游记','红楼梦','三国演义','水浒传'];

  // 判断
  console.log(four.includes('西游记')); // true
  console.log(four.includes('金画画')); // false

  // indexOf
  console.log(four.indexOf('西游记')); // 0
  console.log(four.indexOf('水浒传')); // 3
  console.log(four.indexOf('金画画')); // -1

3.2 指数操作符(幂运算)

  1. ES7 中引入指数运算符「**」,用来实现幂运算,功能与Math.pow 结果相同如210
// 幂运算 **
  console.log((2 ** 10)); // 1024
  console.log(Math.pow(2, 10)); // 1024

4. ECMASript 8 新特性

4.1 async 和 await

  1. asyncawait两种语法结合可以让异步代码像同步代码一样。
async 函数
  1. async 函数的返回值为promise 对象;

  2. promise 对象的结果由async 函数执行的返回值决定。

/**
   * async(异步的) 修饰的函数返回的是一个 promise 对象
   *  1. 返回的结果不是一个Promise类型的对象 就是一个成功的Promise;
   *  2. 抛出错误 返回的结果是一个失败的Promise;
   *  3. 如果返回的结果是一个Promise对象 ;
   */
  async function fun() {
    // 1. 返回的结果不是一个Promise对象
    // return '我是字符串';

    // 2. 返回的结果中抛出异常
    // throw new Error('这是一个错误!');

    // 3. 返回的结果是一个Promise对象
    return new Promise((resolve, reject) => {
      // resolve('成功!');
      reject('拒绝!');
    })
  }
  const result = fun();
  console.log(result);
  /**
   * 处理结果
   */
  result.then(
    value => {
      console.log(value);
    },
    reason => {
      console.log(reason);
    });

4.2 await 表达式

  1. await 必须写在 async 函数中;

  2. await 右侧的表达式一般为 promise 对象;

  3. await 返回的是 promise 成功的值;

  4. awaitpromise 失败了, 就会抛出异常, 需要通过 try...catch 捕获处理。

 const p = new Promise((resolve, reject) => {
    // resolve('成功的值!');
    reject('失败了!'); // 如果这里失败 在async 中的await需要使用 try...catch() 捕获
  });
  /**
   * await 需要放在 async 函数中
   */
  async function mainFun() {
    try {
      let result = await p;
      console.log(result);
    } catch (e) {
      console.log(e); // 失败了! 是这里打印的哦
    }
  }
  // 调用函数
  mainFun();
async 和 await 读取多个文件
  1. 需要node环境的支持。

  2. 运行时只需要在命令行 node 文件名.js 即可。

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

// 读取文件
/**
 * 读取文件 666.md
 */
function rf666() {
  return new Promise((resolve, reject) => {
    fs.readFile('../resources/666.md', (err, data) => {
      if (err) reject(err);
      resolve(data);
    });
  });
}


function rf888() {
  return new Promise((resolve, reject) => {
    fs.readFile('../resources/888.md', (err, data) => {
      if (err) reject(err);
      resolve(data);
    });
  });
}

function rf999() {
  return new Promise((resolve, reject) => {
    fs.readFile('../resources/999.md', (err, data) => {
      if (err) reject(err);
      resolve(data);
    });
  });
}

async function main() {
 try {
   let f666 = await rf666();
   let f888 = await rf888();
   let f999 = await rf999();
   console.log(f666.toString());
   console.log(f888.toString());
   console.log(f999.toString());
 } catch (e) {
   console.log(e);
 }
}
main();
async 和 await 封装ajax请求
/**
   * 发送ajax请求返回的结果是一个 Promise 对象
   */
  function sendAjax(url) {
    return new Promise((resolve, reject) => {
      // 1. 创建对象
      const xhr = new XMLHttpRequest();
      // 2. 初始化
      xhr.open('GET', url);
      // 3. 发送请求
      xhr.send();
      // 4. 绑定事件
      xhr.onreadystatechange = function () {
        // 判断
        if (xhr.readyState === 4) { // 需要加上这句状态的判断否则请求不到数据
          // 判断状态码 处于 200 - 299 之间
          if (xhr.status >= 200 && xhr.status <= 299) {
            // 表示成功
            // console.log(xhr.response);
            // 表示成功
            resolve(xhr.response);
          } else {
            // console.log(xhr.status);
            reject(xhr.status);
          }
        }
      };
    });
  }

  console.log(sendAjax('https://api.apiopen.top/getJoke'));
  /**
   * 使用then处理
   */
  sendAjax('https://api.apiopen.top/getJoke').then(value => {
    // console.log(value);
  }, reason => {
    console.log(reason);
  });

  // 使用 async 和 await 进行处理
 async function main() {
   let joke = await sendAjax('https://api.apiopen.top/getJoke');
   console.log(joke);
 }
  main();

4.2 Object.values 和 Object.entries 、Object.keys()对象方法扩展

  1. Object.values()方法返回一个给定对象的所有可枚举属性值的数组;
  1. Object.entries()方法返回一个给定对象自身可遍历属性 [key,value]的数组;
  1. Object.keys() 方法返回一个给定对象的所有可枚举的属性。

4.3 Object.getOwnPropertyDescriptors

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


5. ECMASript 9 新特性

5.1 Rest/Spread 属性

  1. Rest 参数与 spread 扩展运算符在 ES6 中已经引入,不过 ES6 中只针对于数组,
    ES9 中为对象提供了像数组一样的 rest 参数和扩展运算符
/**
   * 对象的扩展运算符
   */
  const connectCfg = {
    host: '127.0.0.1',
    port: '3306',
    username: 'root',
    password: 'root',
    type: 'db'
  }

  /**
   * 对象的展开
   * @param host
   * @param port
   * @param user
   */
  function connect({host, port, ...user}) {
    console.log(host);
    console.log(port);
    console.log(user);
  }

  connect(connectCfg);

  const skillOne = {
    q: '天音波'
  };

  const skillTwo = {
    w: '金钟罩'
  };

  const skillThree = {
    e: '天雷破'
  };

  const skillFour = {
    r: '猛龙摆尾'
  };

  // 将对象拆分为 数组
  const ms = {...skillOne, ...skillTwo, ...skillThree, ...skillFour};
  console.log(ms);

5.2 正则扩展-命名捕获分组

  1. ES9 允许命名捕获组使用符号『?<name>』 ,这样获取捕获结果可读性更强
// 声明一个字符串
  let str = '<a href="https://www.baidu.com">百度一下</a>'

  // 编写正则
  let reg = /<a href="(?<url>.*)">(?<text>.*)<\/a>/;
  const result = reg.exec(str);
  console.log(result);
  console.log(result.groups.url);
  console.log(result.groups.text);

5.3 正则扩展-反向断言

  1. ES9 支持反向断言,通过对匹配结果前面的内容进行判断,对匹配进行筛选。
 /**
   * 1. 正向断言
   *
   * 2. 反向断言
   *
   */
    // 正向断言
  let str = 'zhangsan1235656哈哈哈哈'
  const reg = /\d+(?=哈)/;
  const result = reg.exec(str);
  console.log(result);

  // 反向断言 : 可以根据前边的内容做判断
  const rg = /(?<=n)\d+/;
  console.log(rg.exec(str));

5.4 正则扩展dot-All

  1. 正则表达式中点.匹配除回车外的任何单字符,标记『s』 改变这种行为,允许行终止符出现。
let str =
    '<ul>
      <li>
        <a>肖申克的救赎</a>
        <p>上映时间:2019</p>
      </li>
      <li>
        <a>阿甘正传</a>
        <p>上映时间:2018</p>
      </li>
    </ul>';
  /**
   * dot . 元字符 除换行符以外的任意单个字符
   *
   * 多了一个模式修正符 s
   *
   * g 全局修饰符
   */

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

6. ES10 新特性

6.1 Object.fromEntries 和 Object.entries

  1. Object.fromEntries 将数组转换为对象。
  1. Object.entries 将对象转换为数组。
// 二维数组
  const result = Object.fromEntries([
      ['name', '仙桃大数据学院'],
      ['subject', 'Java、前端']
    ]);

  let map = new Map();
  map = Object.fromEntries(result);
  console.log(map);

6.1 trimEnd() 和 trimStart()

  1. trimEnd() 清除尾部的空白。
  1. trimStart() 清除首部的空白。

6.2 flat 和 flatMap

  1. flat 将多维数组转换为低维数组 ,参数传递的是代表 深度。
  1. flatMap 类似于数组的 map 方法。map()方法创建一个新数组,其结果是该数组中的每个元素是调用一次提供的函数后的返回值。参考网址

7. ES11 新特性 未完待续....

上一篇下一篇

猜你喜欢

热点阅读