0%

ES5-6-7

ECMAScript

是一种由 ECMA 组织制定和发布的脚本语言规范

  • JavaScript 是 ECMA 的实现
  • JS 包含三个部分
    • ECMAScript (核心语法)
    • BOM (浏览器对象模型)
    • DOM (文档对象模型)
  • ES 的几个重要版本
    • ES5 —— 09年发布
    • ES6 (ES2015)
    • ES7 (ES2016)

ES5

严格模式

目的

消除 JavaScript 语法的一些不合理、不严谨之处, 减少一些怪异行为
消除代码的一些不安全之处, 为代码的安全运行保驾护航

  • 使用

    • 在全局或函数的第一条语句定义为 —— 'use strict'
  • 语法

    • 必须使用 var 声明变量

    • 禁止自定义函数中的 this 指向 window

      1
      2
      3
      4
      5
      6
      function Person (name, age) {
      this.name = name
      this.age = age
      }
      Person('zs', 20) // 报错
      new Person('zs', 20) // 正常
    • 创建 eval 作用域

      1
      2
      3
      4
      // eval() 是一个函数, 可以将字符串解析成 js 代码, 非严格模式下没有自己的作用域
      var str = 'NBA'
      eval('var str = "CBA"; alert(str);') // CBA
      alert(str) // 不使用严格模式 - CBA 使用严格模式 - NBA
    • 对象不能有重名属性

JSON 工具类

API 说明
JSON.stringify(obj / arr) js 对象(数组) json 字符串
JSON.parse(json) json 字符串 js 对象(数组)

Object 扩展

ES5 给 Object 扩展了一些静态方法

  • Object.create(prototype, [descriptors])

    • 作用 —— 以指定对象为原型创建新的对象
    • descriptors —— 为新的对象指定新的属性, 并对属性进行描述
      • value : 指定值
      • writable : 标识当前属性值是否是可修改的, 默认为 false
      • configurable: 标识当前属性是否可以被删除 默认为 false
    • enumerable: 标识当前属性是否能用 for in 枚举 默认为 false
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
        var obj = {name : 'curry', age : 29}
      var obj1 = {};
      obj1 = Object.create(obj, {
      sex : {
      value: '男',
      writable: true, // 设置可修改, 不设置则为不可修改
      enumerable: true // 设置可枚举
      }
      });
      obj1.sex = '女';
      console.log(obj1.sex); // '女'
  • Object.defineProperties(object, descriptors)

    • 作用 —— 为指定对象定义扩展多个属性
      • get —— 用来获取扩展属性值的回调函数
      • set —— 监听扩展属性值的变化自动调用, 并且实参即为修改的值
      • 存取器属性 —— setter / getter 一个用来存值, 一个用来取值
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        //Object.defineProperties(object, descriptors)
        var obj2 = {
        firstName : 'curry',
        lastName : 'stephen'
        };
        Object.defineProperties(obj2, {
        fullName: {
        get: function () {
        return this.firstName + '-' + this.lastName
        },
        set: function (data) {
        var names = data.split('-');
        this.firstName = names[0];
        this.lastName = names[1];
        }
        }
        });
        console.log(obj2.fullName); // curry-stephen
        obj2.firstName = 'tim';
        obj2.lastName = 'duncan';
        console.log(obj2.fullName); // tim-duncan
        obj2.fullName = 'kobe-bryant'; // 自动调用 set
        console.log(obj2.fullName); // kobe-bryant
  • 对象本身的两个方法

    • get propertyName() {} 获取当前属性
    • setpropertyName() {} 设置当前属性
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      var obj = {
      firstName : 'kobe',
      lastName : 'bryant',
      get fullName(){
      return this.firstName + ' ' + this.lastName
      },
      set fullName(data){
      var names = data.split(' ');
      this.firstName = names[0];
      this.lastName = names[1];
      }
      };
      console.log(obj.fullName);
      obj.fullName = 'curry stephen';
      console.log(obj.fullName);

Array 的扩展

  • Array.prototype.indexOf(value)
    • 得到元素在数组中的第一个下标
  • Array.prototype.lastIndexOf(value)
    • 得到元素在数组中的最后一个下标
  • Array.prototype.forEach(function (item, index) {})
    • 遍历数组
  • Array.prototype.map(function (item, index) {})
    • 遍历数组, 返回一个新数组, 返回加工后的值组成的新数组
    • 新数组和原数组长度一致, 一对一的关系
  • Array.prototype.filter(function (item, index) {})
    • 遍历 过滤出一个新的子数组, 返回条件为 true 的值组成的新数组
    • 新数组是原数组的子数组, 长度可以比原数组小
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      /*
      需求:
      1. 输出第一个6的下标
      2. 输出最后一个6的下标
      3. 输出所有元素的值和下标
      4. 根据arr产生一个新数组,要求每个元素都比原来大10
      5. 根据arr产生一个新数组, 返回的每个元素要大于4
      */

      var arr = [1, 4, 6, 2, 5, 6];
      console.log(arr.indexOf(6));//2

      //Array.prototype.lastIndexOf(value) : 得到在数组中的最后值的下标
      console.log(arr.lastIndexOf(6));//5

      //Array.prototype.forEach(function(item, index){}) : 遍历数组
      arr.forEach(function (item, index) {
      console.log(item, index);
      });

      //Array.prototype.map(function(item, index){}) : 遍历数组返回一个新的数组,返回加工之后的值
      var arr1 = arr.map(function (item, index) {
      return item + 10
      });
      console.log(arr, arr1);

      //Array.prototype.filter(function(item, index){}) : 遍历过滤出一个新的子数组,返回条件为true 的值
      var arr2 = arr.filter(function (item, index) {
      return item > 4
      });
      console.log(arr, arr2);

Function 的扩展

  • Function.prototype.bind(obj)

    • 将函数内部的 this 绑定为 obj, 并将函数返回
  • 区别 bind()、call()、apply()?

    • 都能重新指定函数中的 this
    • call() 和 apply() 是 立即调用 函数
      • 不传参数的时候使用方式一样
      • 传参数的时候, call() 直接写参数, apply() 参数需要放在 数组内
    • bind() 是将函数返回
      • bind() 的特点 - 绑定完 不会立即调用 当前函数, 而是将函数返回
      • 传参的方式 - 同 call 一样
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        var obj = { name: 'zs'}
        function foo(data) {
        console.log(this, data)
        }

        // 传入参数的形式
        foo(11) // Window 11
        foo.call(obj, 11) // {name: 'zs'} 11
        foo.apply(obj, [11]) // {name: 'zs'} 11

        foo.bind(obj) // 没有输出
        foo.bind(obj, 11)() // {name: 'zs'} 11

        // 使用
        setInterval(function () {
        console.log(this) // {name: 'zs'}
        }.bind(obj), 1000)

ES6

参考文档

let / const 关键字

  • let

    • 作用

      • 与 var 类似, 用于声明一个变量
    • 特点

      • 块级作用域 内有效
      • 不能重复声明
      • 不会预处理, 不存在提升
    • 应用

      • 循环遍历加监听
      • let 取代 var 是趋势
        1
        2
        3
        4
        5
        for (let i = 0; i < btns.length; i++) {
        btns[i].onclick = function () {
        alert(i) // 0 1 2, let 有块级作用域
        }
        }
  • const

    • 作用

      • 定义一个 常量
    • 特点

      • 不能修改
      • 其他特点同 let
    • 应用

      • 保存不用改变的数据
        1
        const URL = '/login.html'

变量的解构赋值

理解 —— 从对象或数组中提取数据, 并赋值给多个变量

  • 用途
    • 多个 形参赋值

对象的解构赋值

要被赋值的变量名必须是 对象中已有的属性名

1
2
3
4
5
6
7
8
9
let { name, age, xxx } = { name: 'zs', age: 18 }
console.log(name, age, xxx) // 'zs' 18 undefined
console.log(age) // 18

function foo({ username, age }) {
console.log(username, age)
}
let obj = { username: 'zs', age: 18 }
foo(obj)

数组的解构赋值

要被赋值的变量是 按顺序赋值

1
2
3
4
5
let [a, b] = [1, 5, 9]
console.log(a, b) // 1 5

let [, a, b] = [1, 5, 9]
console.log(a, b) // 5 9

模版字符串

作用 —— 简化字符串的拼接

  • 模版字符串必须用 ``包含
  • 变化的部分使用 ${xxx} 定义
    1
    2
    3
    let obj = { name: 'zs', age:18 }
    let str = `我叫: ${obj.name}, 今年${obj.age}岁`
    console.log(str)

简化的对象写法

  1. 与属性名相同的属性值可以省略不写
  2. 可以省略方法里的 :function
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    let name = 'zs'
    let age = 18
    let obj = {
    name: name,
    age: age,
    getName: function () {
    return this.name
    }

    let obj = {
    name,
    age,
    getName() {
    return this.name
    }
    }

箭头函数

作用 —— 定义匿名函数, 回调函数中常用

  • 基本语法

    • 没有参数的情况下, 小括号不能省

      1
      () => console.log('xx')
    • 一个参数的情况下, 小括号能省

      1
      i => i+2
    • 大于一个参数的情况下, 小括号不能省

      1
      (i, j) => i + j
    • 函数体只有一条语句, 可以不用大括号, 默认返回该条语句的执行结果或 undefined

    • 函数体如果有多条语句, 需要用 {}; 如果有需要返回的内容, 需要 手动返回

      1
      2
      3
      4
      5
      6
      7
      8
      let fun = (x, y) => x + y
      console.log(fun(20, 30)) // 50

      let fun = (x, y) => { x + y }
      console.log(fun(20, 30)) // undefined

      let fun = (x, y) => { return x + y }
      console.log(fun(20, 30)) // 50
  • 使用场景

    • 多用于定义回调函数
  • 特点

    • 简洁
    • 箭头函数没有自己的 this, 箭头函数的 this 不是调用的时候决定的,
      而是 在定义的时候处在的位置处的对象 就是它的 this

箭头函数的 this

箭头函数的 this 看 其所处的外层函数

  • 如果有, 外层函数的 this 就是箭头函数的 this
  • 如果没有, 则箭头函数的 this 是 window
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    btn.onclick = () => {
    console.log(this) // window
    }

    let obj = {
    name: 'zs',
    fn: function () {
    btn.onclick = () => {
    console.log(this)
    }
    }
    }
    obj.fn() // obj

    let obj = {
    name: 'zs',
    fn: () => {
    btn.onclick = () => {
    console.log(this)
    }
    }
    }
    obj.fn() // Window

… 运算符

作为参数使用

  • rest(可变)参数
    • 用来取代 arguments, 但比 arguments 灵活, …rest 只能放在实参最后
    • 定义的时候要加 … , 使用的时候直接使用 rest
      1
      2
      3
      4
      function fun(a, ...rest) {
      console.log(rest)
      }
      fun(1, 2, 3) // [2, 3] 拿到的是真数组, 可以使用数组的方法

作为扩展运算符使用

  • 扩展运算符
    • 可以解构数组
    • 也可以解构伪数组
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      let arr = [1, 6]
      let arr1 = [2, 3, 4, 5]
      arr = [1, ...arr1, 6]
      console.log(arr) // [1, 2, 3, 4, 5, 6]

      [...divs] // 伪数组转数组

      let obj = { name: 'zs' }
      let obj2 = { ...obj }
      console.log(obj2) // { name: 'zs' }

      // 直接 ...obj 会报错,...arr 不会报错

形参默认值

当不传参时, 默认使用形参里的默认值

1
2
3
4
5
6
function Point(x = 1, y = 2) {
this.x = x
this.y = y
}
let point = new Point()
console.log(point) //{ x: 1, y: 2 }

Module 的语法

历史上,JavaScript 一直没有模块(module)体系,无法将一个大程序拆分成 互相依赖 的小文件,再用简单的方法拼装起来

ES6 在语言标准的层面上,实现了模块功能,而且实现得相当简单,完全可以取代 CommonJS 和 AMD 规范,成为浏览器和服务器通用的模块解决方案

export 命令

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

    • export 命令用于向外暴露接口,
    • import 命令用于引入其他模块提供的功能
  • 一个模块就是一个独立的文件

    • 该文件内部的所有变量,外部无法获取
    • 如果你希望外部能够读取模块内部的某个变量,就必须使用 export 关键字输出该变量
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    export var firstName = 'Michael'
    export var lastName = 'Jackson'
    export var year = 1958

    // 或者
    var firstName = 'Michael'
    var lastName = 'Jackson'
    var year = 1958

    export { firstName, lastName, year }

    // 对外暴露一个函数
    export function multiply(x, y) {
    return x * y
    }
  • export 命令后面,使用大括号指定所要输出的一组变量或函数或类

  • 通常情况下,export 输出的变量就是本来的名字,但是可以使用 as 关键字重命名

    1
    2
    3
    4
    5
    6
    7
    8
    function v1() { ... }
    function v2() { ... }

    export {
    v1 as streamV1,
    v2 as streamV2,
    v2 as streamLatestVersion
    }

⚠️ export 命令规定的是对外的接口,必须与模块 (js 文件) 内部的变量建立一一对应关系

  • export 向外暴露成员只能有两种写法

    • 写法一

      1
      export var m = 1
    • 写法二

      1
      2
      var m = 1
      export { m }
  • export 向外暴露的成员可以 整体加载 (import * as xxx from ‘./module’)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    // circle.js
    export function area(radius) {
    return Math.PI * radius * radius;
    }

    export function circumference(radius) {
    return 2 * Math.PI * radius;
    }

    // main.js
    import { area, circumference } from './circle'

    console.log('圆面积:' + area(4))
    console.log('圆周长:' + circumference(14))

    // 整体加载
    import * as circle from './circle'

    console.log('圆面积:' + circle.area(4))
    console.log('圆周长:' + circle.circumference(14))

export default 命令

  • export default命令,为模块指定默认输出

    • 其他模块加载该模块时,import命令可以为其指定任意名字; 这时import命令后面,不使用大括号
    1
    2
    3
    4
    5
    6
    7
    8
    // export-default.js
    export default function () {
    console.log('foo');
    }

    // import-default.js
    import customName from './export-default';
    customName(); // 'foo'
  • export default 命令用在非匿名函数前,也是可以的

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    // export-default.js
    export default function foo() {
    console.log('foo');
    }

    // 或者写成

    function foo() {
    console.log('foo');
    }

    export default foo;
  • 本质上,export default 就是输出一个叫做 default 的变量或方法,然后系统 允许你为它取任意名字

  • export default也可以用来输出类

    1
    2
    3
    4
    5
    6
    // MyClass.js
    export default class { ... }

    // main.js
    import MyClass from 'MyClass';
    let o = new MyClass();

比较 export 和 export default

  1. 一个 js 模块只能有一个 export default 命令, 却可以有多个 export 命令
  2. export 和 export default 可以共存
  3. export 和 export default 在导出 命名函数、命名类 时写法是一样的
  4. export 向外暴露的成员必须带有名字, export 命令规定的是对外的接口,必须与模块内部的变量建立一一对应关系
  5. export 向外暴露的成员在 import 时必须加大括号; export default 向外暴露的成员在 import 时不加大括号
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
export default function crc32() { // 输出
// ...
}

import crc32 from 'crc32' // 输入

export function crc32() { // 输出
// ...
};

import { crc32 } from 'crc32' // 输入

// 一个模块同时使用 export default 和 export
export default obj

export var obj2 = {}

import obj, { obj2 } from '...'

import

使用exportexport default命令定义了模块的对外接口以后,其他 JS 文件就可以通过import命令加载这个模块

1
2
3
4
5
6
// main.js
import { firstName, lastName, year } from './profile.js';

function setName(element) {
element.textContent = firstName + ' ' + lastName;
}
  • import 命令接受一对大括号,里面指定要从其他模块导入的变量名。大括号里面的变量名,必须与被导入模块(profile.js)对外接口的名称相同

  • 如果想为输入的变量重新取一个名字,import命令要使用as关键字,将输入的变量重命名

    1
    import { lastName as surname } from './profile.js'
  • import命令具有提升效果,会提升到整个模块的头部,首先执行

    1
    2
    3
    foo()

    import { foo } from 'my_module'

⚠️

  1. 如果多次重复执行同一句import语句,那么只会执行一次,而不会执行多次
  2. import 命令导入的变量都是只读的,因为它的本质是引入接口
  3. import 命令可以引入静态资源文件
1
2
3
4
5
6
7
8
// 1. 直接引入自定义的静态文件
import './css/index.css'

// 2. 引入 node_modules 中的静态文件,可以省略 node_modules 这一层, 直接从包名开始写
import 'bootstrap/dist/css/bootstrap.css'

// 3. 引入静态文件并拿到其中的数据
import data from './data.json'

Promise 对象

  • 理解

    • Promise 对象代表了未来某个将要发生的事件 (通常是一个 异步操作)

    • 有了 promise 对象, 可以将异步操作以同步的流程表达出来, 避免了 “回调地狱” 问题

    • ES6 的 Promise 是一个构造函数, 用来生成 promise 实例

    • promise 一旦创建, 括号内的函数就立即调用

      • 如果不想让它立即执行, 应该把它放到一个函数中
    • promise 对象上有两个函数 resolve 和 reject

      • resolve 和 reject 从哪来?

        在 new 出来的 promise 实例上调用 .then() , 预先为这个 promise 异步操作指定成功的回调 resolve 和失败的回调 reject

      • promise 对象的 resolve() 方法只能接收一个参数,如果需要传递多个参数可以传递一个对象
    • promise 构造函数的 prototype 上有 then 方法

promise 对象的 then()

then() 之后可以链式调用 then()

  • 后续的每一个 then 中指定的回调处理函数都会被执行

  • 后续的每一个 then 中指定的回调处理函数可以接收上一个 then 中第一个函数的返回值

    • 如果上一个 then 中第一个函数没有指定返回值,后续的 then 接收的就是 undefined

    • 如果上一个 then 中第一个函数指定返回值是普通数据类型,后续的 then 接收的就是该普通类型的返回值

    • 如果上一个 then 中第一个函数指定返回值是 promise 对象,后续的 then 的第一个参数就是该 promise 对象的 resolve() 函数

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      new Promise((resolve, reject) => {
      fs.readFile('a.txt', 'utf8', (err, data) => {
      if (err) {
      return reject(err)
      }
      resolve(data)
      })
      })
      .then((data) => {
      console.log(data) // a.txt 的内容
      })
      .then((data) => {
      console.log(data) // undefined, 上一个 then() 没有指定返回值
      })

      // 返回普通数据
      ......
      .then((data) => {
      console.log(data) // a.txt 的内容
      return 123
      })
      .then((data) => {
      console.log(data) // 123, 上一个 then() 的返回值
      })

      // 返回 promise 对象
      ......
      .then((data) => {
      console.log(data) // a.txt 的内容
      return promise对象
      })
      .then((data) => {
      console.log(data) // 上一个 then() 返回的 promise 对象的 resole(data) 中的 data
      })

promise 应用场景

使用 promise 基本步骤

  1. 将异步操作封装成 promise 对象
    异步操作成功, 调用 resolve()
    操作失败, 调用reject()
  2. promise 实例调用 then (function () {}[, function () {}])
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    // 应用场景一
    // 按顺序读多个文件
    var fs = require('fs')

    function pReadFile(filePath) {
    return new Promise(function(resolve, reject) {
    fs.readFile(filePath, 'utf8', function(err, data) {
    if (err) {
    reject(err)
    } else {
    resolve(data)
    }
    })
    })
    }

    pReadFile('./data/a.txt')
    .then(function(data) { // 通过 .then 指定回调的时候, 可以只传成功的回调
    console.log(data)
    return pReadFile('./data/b.txt') // 返回一个新的 promsie 实例, 可以继续用 .then 来处理
    // 如果返回一个普通值,就直接将这个普通值传递给后续的 then
    })
    .then(function(data) {
    console.log(data)
    return pReadFile('./data/c.txt')
    })
    .then(function(data) {
    console.log(data)
    })


    // 应用场景二
    // 请求多个借口才能拿到综合的数据
    var data = {}
    $.get(借口地址1)
    .then(function (userData) {
    data.user = userData
    return $.get(借口地址2)
    })
    .then(function (jobsData) {
    data.jobs = jobsData
    var htmlStr = template('tpl', data)
    $('form').html(htmlStr)
    })

promise 对象的三个状态

  • pending —— 初始化状态

  • fullfilled —— 成功状态

  • rejected —— 失败状态

  • 应用

    • 使用 promise 实现超时处理
    • 使用 promise 封装 ajax 请求
  • ⚠️

    • promise 本身不是异步, 但其内部通常封装一个异步操作

promise 替代回调

只需要把之前传给回调函数的异步结果传给 promise 对象的 resolve 函数就可以了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// 回调
fucntion get (url, callback) {
var xml = new XMLHttpRequest()
xml.onload = function () {
callback(this.responseText)
}
xml.open('GET', url)
xml.send()
}
get(url, function (data) {
console.log(data)
})

// 改造成 promise 形式
function get(url) {
return new Promise((resolve, reject) => {
var xml = new XMLHttpRequest()
xml.onload = function () {
resolve(this.responseText)
}
xml.open('GET', url)
xml.send()
})
}
get(url)
.then(function (data) {
console.log(data)
})

promise 捕获异常

  • 方式一

    • 手动指定回调, 就是定义 .then 的第二个参数(失败的回调)
    • 并且在失败的回调里也 return 新的 promise , 以保证前边的执行失败不影响后边的执行
  • 方式二

    • 如果后边的 promise 的执行依赖前面 promise 的结果; 前边的执行失败, 应该终止后边的执行

    • 在最后面 .catch

    • ⚠️ 如果前边某个操作添加了单独的 reject(), 则该操作的错误不会进入 catch()

      1
      2
      3
      .catch(err => { // 如果前面有任何的错误发生, 立即终止所有 then() 的执行, 进入 catch 的执行
      console.log(err.message)
      })

jquery 中的 ajax 天生支持 promise

$.get(url) 默认返回一个 promise 对象, 就是说可以调用 .then() 的方式执行后续操作

1
2
3
4
$.get(url)
.then(data => {
console.log(data)
})
1
2
3
4
5
6
7
8
9
10
11
$.ajax({
url: url,
type: 'post',
data: $("#fm").serialize(),
dataType: 'json'
})
.then(function(data){
console.log(" 提交成功", "操作成功")
}, function(){
console.log("提交失败", "操作失败")
})

async 函数

真正意义上去解决 异步回调 的问题 —— 同步流程表达异步操作
简化 promise 的操作 —— 不再需要去调用.then() 来指定成功/失败的回调

  • 本质

    • Generator 的语法糖
  • 语法

    1
    2
    3
    4
    async function foo() {
    await 异步操作
    await 异步操作
    }
  • 返回值

    • async 函数的返回值是一个新的 promsie 对象,promsie 成功回调的结果由 async 的返回值决定
  • 特点

    • 不需要像 Generator 去调用 next 方法, 遇到 await 等待, 当前的异步操作完成就往下执行
    • async 取代 Generator 函数的 * , await 取代 Generator 的 yield
    • async 函数通常返回的总是 promise 对象, 可以调用 then 方法进行下一步操作
    • 语义上更为明确, 使用简单
    • await 后边如果是普通函数, 得到的就是普通函数的返回值
    • await 后边如果是 promise 对象, 得到的就是 promise 对象的 resolve 函数被调用时所传递的参数
      • 如果异步请求失败, 会报错
  • 可以通过 try catch 来捕获异常

    -  await 操作写在 try 里边,捕获异常写在 catch(err) 里
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    function getNews(url) {
    return new Promise((reslove, reject) => {
    $.ajax({
    type: 'get',
    url,
    success: data => reslove(data),
    error: error => reject(error)
    // 如果前边的请求出错了,下边的 result 没有值, 如何提示用户?
    // 解决办法 —— error: error => resolve(false)
    })
    })
    }
    async function send() {
    // await 后边通常都是跟一个 promise 对象
    let result = await getNews('http://localhost:3000/')
    // 请求出错的提示
    // if (!result) { alert('暂时没有数据') return }
    console.log(result)
    result = await getNews('http://localhost:3000/' + result.commentUrl)
    console.log(result)
    }
    send()

多个请求(await)的情况

如果在 async 函数中有多个请求,需要写多个 await ,这样后边的 await 会等待前面的执行完才执行

  • 造成等待时间过长

  • 解决 —— Promise.all([promise1, promise2….])

    1
    2
    3
    4
    5
    6
    7
    8
    async componentDidMount() {
    // Promsie.all() 接收一个数组,数组的元素是每一个 pormise 对象
    // 返回一个数组,数组的每个元素是对应的 请求的返回结果
    const results = Promise.all([reqCategory(pCategroryId), reqCategory(categoryId)])
    const cName1 = results[0].data.name
    const cName2 = results[1].data.name
    ......
    }

总结 Promise 和 async 函数

基本操作都是 先将异步操作封装成一个返回值为 promise 对象的函数

  • 然后
    • Promise 方式是使用封装函数得到的 promsie 实例直接去 调用 then() 方法
      • then() 的第一个参数为 promise 对象的 resolve 方法,第二个参数为 reject 方法
    • async 函数是 在调用封装的函数前加上 await
      • await 封装函数的返回值为 promise 对象的 resolve 方法的参数

Symbol

ES5 中对象的属性名都是字符串, 容易造成重名, 污染环境

  • ES6 中添加了一种 原始数据类型 symbol

  • 特点

    • symbol 属性对应的值是唯一的, 解决命名冲突问题
    • symbol 值不能与其他数据进行运算, 包括同字符串拼接
    • for in for of 遍历时不会遍历 symbol 属性
  • 使用

    • 调用 Symbol() 函数得到 symbol 值 (每次得到的值都不一样)
      1
      2
      3
      4
      let symbol = Symbol()
      let obj = {}
      obj[symbol] = 'hello' // 不能 obj.symbol
      console.log(obj) // { Symbol(): 'hello' }
  • 传参标识

    1
    2
    3
    let symbol = Symbol('one')
    let symbo2 = Symbol('two')
    console.log(symbol, symbo2) // Symbol(one) Symbol(two)
  • 内置 Symbol 值

    • 除了自定义的 symbol 值以外, ES6 还提供了 11 个内置的 symbol 值
    • Symbol.iterator
      • 对象的 Symbol.iterator 属性, 指向该对象的默认遍历器方法

Iterator 遍历器

  • iterator 是一种接口机制, 为各自不同的数据结构提供统一的访问机制

  • 作用

    • 为各种数据结构, 提供一个统一的、简便的访问接口
    • 是的数据结构的成员可以按某种次数排列
    • ES6 创造一种新的遍历, for of 循环, Iterator 接口主要供 for of 消费
  • 工作原理

    • 创建一个指针对象 (遍历器对象), 指向数据结构的起始位置
    • 第一次调用 next() 方法, 指针自动指向数据结构的第一个成员
    • 接下来不断调用 next 方法, 指针会一直往后移动, 直到指向最后一个成员
    • 每次调用 next 方法返回的是一个包含 value 和 done 的对象, { value: 当前成员的值, done:布尔值}
      value 标售当前成员的值, done 对应的布尔值表示当前的数据的结构是否遍历结束
      当遍历结束的时候返回 value 的值是 undefined, done 值为 true
  • 原生具备 iterator 接口的数据 (可用 for of 遍历)

    • 当数据结构上都部署了 Symbol, iterator 接口, 该数据就是可以用 for of 遍历

    • 数组、字符串、arguments、set 容器、map 容器

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      // 遍历数组
      for (let i of arr) {
      console.log(i)
      }

      // 遍历字符串
      for (let i of str) {
      console.log(i)
      }

      // 遍历 arguments
      function fun() {
      for (let i of arguments) {
      console.log(i)
      }
      }
      fun(1, 4, 9)
    • ⚠️ 对象不能用 for of 遍历

Generator 函数

  • ES6 提供的解决异步编程的方案之一
  • Generator 函数是一个状态机, 内部封装了不同状态的数据
  • 用来生成遍历器对象
  • 可暂停函数(惰性请求), yield 可暂停, next 方法可启动; 每次返回的是 yield 后的表达式的结果
  • 特点
    • function 与函数名之间有一个星号
    • 内部用 yield 表达式来定义不同的状态
    • generator 函数返回的是指针对象, 而不会执行函数内部的逻辑
    • 调用 next 方法函数内部逻辑开始执行, 遇到 yield 表达式停止, 返回 { value: yield 后的表达式的结果 / undefined, done:}
    • 再次调用 next 方法会从上一次停止时的 yield 处开始, 直到最后

class 类

通过 class 定义类, 实现类的继承

  • 在每个 class 内部, 有一个 constructor 构造器, 如果没有显示定义, 那么类内部默认有个看不见的 constructor
  • constructor 的作用
    • 好比之前的 function Person() {} 构造函数
  1. 每当通过 new 来创建类的实例, 必然会调用 constructor()
  2. class 作用域里边, 只能写方法和静态属性(static)
    • 静态属性和方法挂载在 构造器 上, 一般的方法挂载在 类实例的原型
  3. 通过 extends 来实现类的继承, 不仅可以继承属性, 还可以继承父类的原型方法
    • 继承的本质是 —— 原型式继承(子类的原型指向了父类的原型)
  4. 通过 super 调用父类的构造方法
    • 如果在子类中没有写 constructor(), 不用调
    • 如果写了 constructor(), 必须调用 super()
  5. 可以在子类中重写从父类继承的一般方法

class 类内部的语法

  1. class 作用域里只能写静态属性和方法, 没有其他语句

  2. 声明一般方法只能是以下两种方式

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    class Person {
    ...
    // 方式1
    show() {
    console.log(this.name, this.age)
    }

    // 方式2
    show = () => {
    console.log(this.name, this.age)
    }
    ...
    }
  3. 每个 } 之后 没有逗号
    注意与对象语法的区分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class Person {
// 定义构造方法
constructor(name, age) {
this.name = name
this.age = age
}
// 定义一般的方法
show() {
console.log(this.name, this.age)
}
// 或者写成箭头函数
// show = () => {
// console.log(this.name, this.age)
// }
}
let per = new Person('zs', 18)

class Star extends Person {
constructor(name, age, salary) {
// 调用父类的构造方法
super(name, age)
this.salary = salary
}
// 重写父类的方法
show() {
console.log('子类的方法')
console.log(this.name, this.age, this.salary)
}
}
let star = new Star('kobe', 38, 10000000)
star.show()

super

super

super这个关键字,既可以当作函数使用,也可以当作对象使用

  • 作为函数时

    • 代表父类的构造函数
    • 调用 super 相当于调用父类的构造函数
      • 但 super 内部的 this 指向的是子类的实例,即
        super() 在这里相当于 A.prototype.constructor.call(this)
    • 作为函数时,super()只能用在子类的构造函数之中
  • 作为对象时

    • 在普通方法中,指向父类的原型对象;在静态方法中,指向父类

    • 在子类普通方法中通过super调用父类的方法时,方法内部的this指向当前的子类实例

    • 如果通过super对某个属性赋值,这时super就是this,赋值的属性会变成子类实例的属性

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      class A {
      constructor() {
      this.x = 1;
      }
      }

      class B extends A {
      constructor() {
      super();
      this.x = 2;
      super.x = 3;
      console.log(super.x); // undefined
      console.log(this.x); // 3
      }
      }

      let b = new B();
      // super.x赋值为3,这时等同于对this.x赋值为3。而当读取super.x的时候,读的是A.prototype.x,所以返回undefined

其他扩展

字符串扩展

  • includes(str)
    • 判断是否包含指定的字符串, 返回 true / false
  • startsWith(str)
    • 判断是否以指定的字符串开头, 返回 true / false
  • endsWith(str)
    • 判断是否以指定的字符串结尾, 返回 true / false
  • repeat(count)
    • 重复指定次数, 返回重复 几次 后的字符串

数值扩展

  • 二进制用 0b开头, 八进制用 0o开头

    1
    console.log(0b1010) // 10
  • Number.isFinite(i)

    • 判断是否是有限大的数
  • Number.isNaN(i)

    • 判断是否是 NaN
  • Number.isInteger(i)

    • 判断是否是整数
  • Number.parseInt(str)

    • 将字符串转换为对应的数值
  • Math.trunc(i)

    • 直接去除小数部分

数组方法的扩展

  • Array.from(v)

    • 将伪数组对象或可遍历的对象转换为真数组

      1
      2
      3
      4
      let btns = document.getElementsByTagName('button')
      Array.from(btns).forEach(function (item, index) {
      console.log(item)
      })
  • Array.of(v1, v2, v3)

    • 将一系列值转换为数组

      1
      2
      let arr = Array.of(1, 2, 6)
      console.log(arr) // [1, 2, 6]
  • find(function (item, index) { return true })

    • 找出第一个满足条件的值

      1
      2
      3
      let result = arr.find(function (item, index) {
      return item > 4
      })
  • findIndex(function (item, index) { return true })

    • 找出第一个满足条件的值的索引

      1
      2
      3
      let resultIndex = arr.findIndex(function (item, index) {
      return item > 4
      })

对象扩展

  • Object.is(v1, v2)

    • 判断两个数据是否完全相等 (底层比较的是字符串)

      1
      2
      3
      4
      5
      6
      7
      // 默认关系运算符会将其转为 number 进行比较(两个字符串比较除外)
      console.log(0 == -0) // true
      console.log(NaN == NaN) // false

      // 将其转为字符串再比较
      console.log(Object.is(0, -0)) // false
      console.log(Object.is(NaN, NaN)) // true
  • Object.assign(target, source1, source2)

    • 将源对象的属性复制到目标对象上

      1
      2
      3
      4
      5
      let obj = {}
      let obj1 = { name: 'sz' }
      let obj2 = { sex: '女' }
      Object.assign(obj, obj1, obj2)
      console.log(obj) // { name: 'sz', sex: '女' }
  • 直接操作 __ proto__ 属性

    1
    2
    3
    4
    5
    var obj2 = {}
    var obj = { money: 1000 }
    obj2.__proto__ = obj
    // obj2 可以访问 obj 上的所有属性, 打印自身是空, 但是可以通过原型链拿到 obj 上的属性
    console.log(obj2, obj2.money) // {} 1000

set 和 map 容器

  • set 容器

    • 无序 不可重复 的多个 value 的集合体 (就是要传个数组)
    • Set()
    • Set(array)
    • set 实例的方法
      • add(value)
      • delete(value)
      • has(value)
      • clear()
      • size
  • map 容器

    • 无序的 key 不重复 的多个 key-value 的集合体 (数组内嵌套数组)

    • Map()

    • Map(array)

    • Map 实例的方法

      • set(key, value)
      • get(key)
      • delete(key)
      • has(key)
      • clear()
      • size
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      let set = new Set([1, 2, 3, 4, 2]) // 传递的是数组
      console.log(set.size, set) // 4 Set(4) {1, 2, 3, 4}
      set.add(7)
      console.log(set.size) // 5
      console.log(set.has(8)) // false

      let map = new Map([['a', 'b'],['age', 36]])
      console.log(map) // Map(2) {'a' => 'b', 'age' => 36}
      map.set('name', 'zs')
      console.log(map) // Map(2) {'a' => 'b', 'age' => 36, 'name' => 'zs'}
      map.delete('age')

for of 循环遍历

  • 遍历数组
  • 遍历 Set
  • 遍历 Map
  • 遍历字符串
  • 遍历伪数组
  • 不能遍历对象
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    let set = new Set([1, 2, 3, 4])
    for (let i of set) {
    console.log(i)
    }

    // 数组去重
    let arr = [1, 2, 5, 5, 6, 1]
    let arr1 = []

    let set = new Set(arr)
    for (let i of set) {
    arr1.push(i)
    }
    arr = arr1

ES7

  • 指数运算符(幂)

    • **
      1
      console.log(3 ** 3) // 27
  • Array.prototype.includes(value)

    • 判断数组里是否包含指定的 value
      1
      2
      let arr = [1, 2, 3]
      console.log(arr.includes(2)) // true