阅读 阮一峰 es6 部分记录

171 阅读37分钟

es6(2015)

let 和 const

  1. 块级作用域,不存在变量提升:只在声明所在的块级作用域内有效
  2. 存在暂时性死区:只要在块级作用域内,let和const 申明的变量就绑定在该区域,不受外界影响(即使块级作用域外用var定义了同名变量),在该块级域内,如果在let或者const之前使用了该变量,都会报错,这在语法上称为暂时性死区
  3. 相同作用域内不可以重复申明
  4. const 申明一个只读常量,申明后值不能改变。实质是指向内存地址的指针不能改变,至于指针所指向的内存地址存储的数据,没有限制。因此,对于基本类型数据,内存地址内就是变量的值,所以const申明的就是只读常量,值不可以改变; 对于引用类型,保存的只是一个指向实际数据的指针,const只能保证这个指针是固定的(即总是指向另一个固定的地址),至于它指向的数据结构是不是可变的,就完全不能控制了

变量的结构赋值

如果解构失败,变量的值等于undefined。

  1. 数组解构: 模式匹配, 按次序排列的,变量的取值由它的位置决定
    let [foo, [[bar], baz]] = [1, [[2], 3]];
    foo // 1
    bar // 2
    baz // 3

    let [ , , third] = ["foo", "bar", "baz"];
    third // "baz"

    let [head, ...tail] = [1, 2, 3, 4];
    head // 1
    tail // [2, 3, 4]

    let [x, y, ...z] = ['a'];
    x // "a"
    y // undefined
    z // []
    //给定默认值
    let [x, y = 'b'] = ['a', undefined]; // x='a', y='b'
    // 注意,ES6 内部使用严格相等运算符(===),判断一个位置是否有值。所以,只有当一个数组成员严格等于undefined,默认值才会生效。

    // 数组是特殊的对象,也可以按照对象去解构
    let arr = [1, 2, 3];
    let {0 : first, [arr.length - 1] : last} = arr;
    first // 1
    last // 3
  1. 对象解构: 变量必须与属性同名,才能取到正确的值
    let { bar, foo } = { foo: 'aaa', bar: 'bbb' };
    foo // "aaa"
    bar // "bbb"

    // 如果变量和属性名不一致,用一下写法; 这时foo是模式,不是变量
    let { foo: baz } = { foo: 'aaa', bar: 'bbb' };
    baz // "aaa"

    // 与数组一样,解构也可以用于嵌套结构的对象
    // 注意,这时p是模式,不是变量,因此不会被赋值。如果p也要作为变量赋值,可以写成下面这样
    let obj = {
        p: [
            'Hello',
            { y: 'World' }
        ]
    };

    let { p: [x, { y }] } = obj;
    x // "Hello"
    y // "World"

    // 给默认值
    var {x: y = 3} = {x: 5};
    y // 5
  1. 字符串的解构赋值,字符串当作类似数组处理
    const [a, b, c, d, e] = 'hello';
    a // "h"
    b // "e"
    c // "l"
    d // "l"
    e // "o"

    let {length : len} = 'hello';
    len // 5
  1. 函数参数也可以解构赋值
  2. 圆括号问题
    // 只有赋值语句的非模式部分可以加圆括号
    [(b)] = [3]; // 正确
    ({ p: (d) } = {}); // 正确
    // 1. let申明时解构不可以加圆括号
    // 全部报错
    let [(a)] = [1];

    let {x: (c)} = {};
    let ({x: c}) = {};
    let {(x: c)} = {};
    let {(x): c} = {};

    let { o: ({ p: p }) } = { o: { p: 2 } };
    // 2. 函数参数解构不可以加圆括号
    // 3. 赋值语句的模式不可以加圆括号
    // 全部报错
    ({ p: a }) = { p: 42 };
    ([a]) = [5];

模板字符串 ``

新增字符串方法

// 1. includes(), startsWith(), endsWith(); 第二个参数,表示开始搜索的位置
    let s = 'Hello world!';

    s.startsWith('world', 6) // true
    s.endsWith('Hello', 5) // true
    s.includes('Hello', 6) // false
// 2. repeat(n) 将字符串重复n次,
// n: 是小数,会取整
// n:是负数或者infinety会报错
// n:是NAN相当于0
// n:0~-1之间相当于0,会先取整
// n:是字符串的话会先转成数字
  let str = 'aaa';
  str.repeat(0) // 结果是 ''
// 3. padStart(),padEnd()  字符串补全长度的功能。如果某个字符串不够指定长度,会在头部或尾部补全。padStart()用于头部补全,padEnd()用于尾部补全
// 第一个参数是补全的最大长度,第二个是补全的参数
// 如果原字符串长度大于等于指定最大长度,则不做处理,返回源字符串
// 如果用来补全的字符串与原字符串,两者的长度之和超过了最大长度,则会截去超出位数的补全字符串
 'abc'.padStart(10, '0123456789') // '0123456abc
// 如果省略第二个参数,默认使用空格补全长度
// 4. trimStart(),trimEnd() 不会修改源字符串,返回新的字符串
// 5. replace只会替换第一个匹配到的; 新增replaceAll,可以替换所有
 'aabbcc'.replace('b', '_') // 'aa_bcc'
//  replaceAll(searchValue, replacement)
// searchValue: 可以是字符串,也可以是正则,但一定要带/g,否则会报错(如果是replace则不会)
// replacement: 可以是字符串或者函数,函数要返回字符串
// 6. at()方法接受一个整数作为参数,返回参数指定位置的字符,支持负索引(即倒数的位置; 如果参数超出字符串索引,会返回undefined
    const str = 'hello';
    str.at(1) // "e"
    str.at(-1) // "o"

新增数字方法

// 1. Number.isFinite(), Number.isNaN(), Number.isInteger()
// 2. Number.EPSILON 新增的极小常量,是 JavaScript 能够表示的最小精度。误差如果小于这个值,就可以认为已经没有意义了,即不存在误差了
// 2. 安全数: js能够准确表示的整数范围在-2^53到2^53之间(不含两个端点),超过这个范围,无法精确表示这个值。
// MAX_SAFE_INTEGER和MIN_SAFE_INTEGER是es6引入的常量,用来表示这个界限
    Number.MAX_SAFE_INTEGER === Math.pow(2, 53) - 1
    // true
    Number.MAX_SAFE_INTEGER === 9007199254740991
    // true

    Number.MIN_SAFE_INTEGER === -Number.MAX_SAFE_INTEGER
    // true
    Number.MIN_SAFE_INTEGER === -9007199254740991

Math对象扩展

// 1. Math.trunc方法用于去除一个数的小数部分,返回整数部分
    Math.trunc(-4.1) // -4
//    对于非数值,内部会优先用Number转
    Math.trunc(true) //1
//    对于空值和无法截取整数的值,返回NAN
    Math.trunc(NaN);      // NaN
    Math.trunc('foo');    // NaN
    Math.trunc();         // NaN
    Math.trunc(undefined) // NaN
// 2. Math.sign方法用来判断一个数到底是正数、负数、还是零。对于非数值,会先将其转换为数值。
// 它会返回五种值。
// 参数为正数,返回+1;
// 参数为负数,返回-1;
// 参数为 0,返回0;
// 参数为-0,返回-0;
// 其他值,返回NaN。
// 3.   Math.cbrt()方法用于计算一个数的立方根,
// 4. Math.clz32()方法将参数转为 32 位无符号整数的形式,然后返回这个 32 位值里面有多少个前导 0。  对于小数,只考虑整数部分
// 5. Math.imul方法返回两个数以 32 位带符号整数形式相乘的结果,返回的也是一个 32 位的带符号整数。
// 6. Math.fround方法返回一个数的32位单精度浮点数形式。
// 7. Math.hypot方法返回所有参数的平方和的平方根。
// 8. Math.expm1(x)返回 e^x - 1,即Math.exp(x) - 1
// 9. Math.log1p(x)方法返回1 + x的自然对数,即Math.log(1 + x)。如果x小于-1,返回NaN
// 10. Math.log10(x)返回以 10 为底的x的对数。如果x小于 0,则返回 NaN
// 11. Math.log2(x)返回以 2 为底的x的对数。如果x小于 0,则返回 NaN

bigint 数据类型

函数的扩展

  1. 给默认值,在结合变量的解构赋值给参数给默认值
    function foo({x, y = 5}) {
    console.log(x, y);
    }

    foo({}) // undefined 5
    foo({x: 1}) // 1 5
    foo({x: 1, y: 2}) // 1 2
    foo() // TypeError: Cannot read property 'x' of undefined
  1. 函数参数给默认值时,给默认值的参数一般放在末尾,若不放在末尾,则不可以只是省略它,而不省略它后面的参数,会报错,除非手动给undefined
    function f(x = 1, y) {
    return [x, y];
    }

    f() // [1, undefined]
    f(2) // [2, undefined]
    f(, 1) // 报错
    f(undefined, 1) // [1, 1]
  1. 作用域

一旦设置了参数的默认值,函数进行声明初始化时,参数会形成一个单独的作用域(context)。等到初始化结束,这个作用域就会消失。这种语法行为,在不设置参数默认值时,是不会出现的

    var x = 1;
    function f(x, y = x) {
    console.log(y);
    }
    f(2) // 2
    // 调用f函数时,传参给第一个参数值2,第二个参数有默认值,参数初始化形成单独的作用域,y的默认值是x,在这个单独作用域中有x,所以取这个作用域内x值2; 如果该作用域内没有x,则取外部变量x,如下面这个
    let x = 1;
    function f(y = x) {
    let x = 2;
    console.log(y);
    }

    f() // 1
  1. rest参数

ES6 引入 rest 参数(形式为...变量名),用于获取函数的多余参数,这样就不需要使用arguments对象了。rest 参数搭配的变量是一个数组,该变量将多余的参数放入数组中

    function add(...values) {
    let sum = 0;

    for (var val of values) {
        sum += val;
    }

    return sum;
    } 

    add(2, 5, 3) // 10
  1. 严格模式: use strict。 从 es5开始,函数内部可以开启严格模式
    function doSomething(a, b) {
        'use strict';
        // code
    }

注: 在参数有默认值、解构赋值、扩展运算符(...)时,函数内部使用严格模式会报错;可以将严格模式定义在全局 6. 箭头函数 (1)箭头函数没有自己的this对象(详见下文)。

(2)不可以当作构造函数,也就是说,不可以对箭头函数使用new命令,否则会抛出一个错误。

(3)不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替。

(4)不可以使用yield命令,因此箭头函数不能用作 Generator 函数。

数组的扩展

  1. 扩展运算符: ...

    1. 可以展开数组
    2. 可以展开字符串
    3. 可以展开任何定义了遍历器(Iterator)的类数组,展开成为真正的数组
  2. Array.from(arrayLike,callback)

    将类数组和可遍历的对象(包括Set和Map数据类型)转换为数组 可以添加第二个参数callback,将每个元素处理后放入要返回的数组中

  3. Array.of()

Array.of()总是返回参数值组成的数组。如果扩展运算符没有参数,就返回一个空数组

    Array.of() // []
    Array.of(undefined) // [undefined]
    Array.of(1) // [1]
    Array.of(1, 2) // [1, 2]
    // 主要用来弥补 new Array
    Array() // []
    Array(3) // [, , ,]
    Array(3, 11, 8) // [3, 11, 8]
  1. copyWithin(target, start = 0, end = this.length)

    数组内部,把某个位置的数据复制到另一个位置,会改变原数组 target: 开始替换数据的位置 start: 开始读取数据的位置 end: 读取数据结束的位置

  2. 实例方法:find(),findIndex(),findLast(),findLastIndex()

    1. find(),findIndex(): 从数组起始位置找第一个符合条件的元素
    2. findLast(),findLastIndex(): 从数组末尾开始找 ( ES2022新增)
  3. fill(p1,p2,p3)

    1. 通过给定值来填充数组,p1是填充的数值,p2是填充的起始位置,p3是结束位置,填充时不包括结束位置
    2. 可用于数组初始化
    3. 若填充数p1是对象,则是填充对象引用位置,即浅拷贝而非深拷贝
    ['a', 'b', 'c'].fill(7)  // [7, 7, 7]
    new Array(3).fill(7)  // [7, 7, 7]  
    ['a', 'b', 'c'].fill(7, 1, 2) // ['a', 7, 'c']
  1. 实例方法:entries(),keys() 和 values()

    都返回一个遍历器对象,可以用for...of循环进行遍历,唯一的区别是keys()是对键名的遍历、values()是对键值的遍历,entries()是对键值对的遍历。

    for (let index of ['a', 'b'].keys()) {
    console.log(index);
    }
    // 0
    // 1

    for (let elem of ['a', 'b'].values()) {
    console.log(elem);
    }
    // 'a'
    // 'b'

    for (let [index, elem] of ['a', 'b'].entries()) {
    console.log(index, elem);
    }
    // 0 "a"
    // 1 "b"
  1. 实例方法:includes()
  2. flat(n),flatMap(callback)
    1. flat(n): 多维数组扁平化方法,n 是要扁平化的层数
    2. flatMap(callback)方法对原数组的每个成员执行一个函数(相当于执行Array.prototype.map()),然后对 返回值 组成 的数组执行flat()方法。该方法返回一个新数组,不改变原数组, 只能拉平一层
  3. 实例方法:at()
    1. 接受一个整数作为参数,返回对应位置的成员,并支持负索引。这个方法不仅可用于数组,也可用于字符串和类型数组
    2. 如果参数位置超出了数组范围,at()返回undefined。
    const arr = [5, 12, 8, 130, 44];
    arr.at(2) // 8
    arr.at(-2) // 130
    arr.at(100) // undefined
  1. 实例方法:toReversed(),toSorted(),toSpliced(),with() 提案

    它们分别对应数组的原有方法。

    • toReversed()对应reverse(),用来颠倒数组成员的位置。
    • toSorted()对应sort(),用来对数组成员排序。
    • toSpliced()对应splice(),用来在指定位置,删除指定数量的成员,并插入新成员。
    • with(index, value)对应splice(index, 1, value),用来将指定位置的成员替换为新的值。 上面是这四个新方法对应的原有方法,含义和用法完全一样,唯一不同的是不会改变原数组,而是返回原数组操作后的拷贝
  2. 实例方法:group(),groupToMap() 提案

    根据分组函数的运行结果,将数组成员分组 按照字符串分组就使用group(),按照对象分组就使用groupToMap()

    const array = [1, 2, 3, 4, 5];

    array.group((num, index, array) => {
        return num % 2 === 0 ? 'even': 'odd';
    }); // { even: [2,4], odd:[1,3,5]}

    const odd  = { odd: true };
    const even = { even: true };
    array.groupToMap((num, index, array) => {
        return num % 2 === 0 ? even: odd;
    });
    //  Map { {odd: true}: [1, 3, 5], {even: true}: [2, 4] }
  1. 数组的空位

    es5中对于空位处理:

    • forEach(), filter(), reduce(), every() 和some()都会跳过空位。
    • map()会跳过空位,但会保留这个值
    • join()和toString()会将空位视为undefined,而undefined和null会被处理成空字符串。 ES6 则是明确将空位转为undefined
  2. Array.prototype.sort() 的排序稳定性

对象的扩展

  1. 属性的简洁表示法

允许在大括号里面,直接写入变量和函数,作为对象的属性和方法

    const foo = 'bar';
    const baz = {
        foo,
        say(){
          console.log('===');
        }
    };
  1. 属性名表达式
    // 方法一
    obj.foo = true;
    // 方法二
    obj['a' + 'bc'] = 123;
    // 使用自面量定义对象
    let propKey = 'foo';
    let obj = {
        [propKey]: true,
        ['a' + 'bc']: 123,
        ['h' + 'ello']() {
            return 'hi';
        }
    };
    // 表达式变量是个对象时, 会自动转为 [object object]
    const obj1 = {name: 'aaa'};
    const obj2 = {name: 'bbb'};
    let obj =  {
        [obj1]:'ccc',
        [obj2]: 'ddd'
    } // 最终结果 obj = {[object object]: 'ddd'}
  1. 属性的可枚举性和遍历
//  可枚举
    let obj = { foo: 123 };
    Object.getOwnPropertyDescriptor(obj, 'foo')
    //  {
    //    value: 123,
    //    writable: true,
    //    enumerable: true,
    //    configurable: true
    //  }
//  遍历
    for...in //遍历对象自身的和继承的可枚举属性(不含 Symbol 属性
    Object.keys(obj)//返回一个数组,包括对象自身的(不含继承的)所有可枚举属性(不含 Symbol 属性)的键名
    Object.getOwnPropertyNames(obj)//返回一个数组,包含对象自身的所有属性(不含 Symbol 属性,但是包括不可枚举属性)的键名
    Object.getOwnPropertySymbols(obj) //返回一个数组,包含对象自身的所有 Symbol 属性的键名
    Reflect.ownKeys(obj) //返回一个数组,包含对象自身的(不含继承的)所有键名,不管键名是 Symbol 或字符串,也不管是否可枚举。
//  遍历规则
    // 首先遍历所有数值键,按照数值升序排列。
    // 其次遍历所有字符串键,按照加入时间升序排列。
    // 最后遍历所有 Symbol 键,按照加入时间升序排列
  1. super 关键字

super 关键字表示原型对象,只能用在对象的方法之中,用在其他地方都会报错。

// 可以使用super的对象方法写法
    let obj = {
        say(){
           return super.foo
        }
    }
// 错误的,将函数赋值给属性的形式
    let obj = {
        say: ()=>super.foo
    }
    let obj = {
        say: function(){
            return super.foo
        }
    }
  1. 对象的扩展运算符: ...
   let params = {
     foo: 'as',
     ...obj
   }
  1. 新增方法
    • Object.is(): 用来比较两个值是否相等,只要两个值是一样的,它们就应该相等,与 === 类似,但有两处不同

      • ===: 认为+0和-0 相等,且NAN 不等于自身
      • Object.is(): 认为+0和-0 不相等,且NAN 等于自身
    • Object.assign():合并对象,将元对象所有可枚举属性包括属性名是symbol值的属性,都会复制到目标对象给

      • 是浅拷贝
      • 同名属性替换
      • 可以处理数组,但会将数组当作对象处理
       Object.assign([1, 2, 3], [4, 5]) // [4,5,3]
      
      • 只能进行值的复制,如果要复制的值是一个取值函数,那么将求值后再复制。
      const source = {
          get foo() { return 1 }
      };
      const target = {};
      
      Object.assign(target, source) // target = {foo: 1}    
      
    • Object.getOwnPropertyDescriptors()

      const target = {
          foo:'aaa',
          name:'bbb'
      }
      Object.getOwnPropertyDescriptors(target)
      /** 
       * {
              foo: {
                  value: 'aaa',
                  writable: true,
                  enumerable: true,
                  configurable: true
              },
              name:{
                  value: 'bbb',
                  writable: true,
                  enumerable: true,
                  configurable: true
              }
         }
       */
      Object.getOwnPropertyDescriptor(target, 'foo')
      /**
       *  {
              value: 'aaa',
              writable: true,
              enumerable: true,
              configurable: true
          }
       */
      
    • __proto__属性,Object.setPrototypeOf(),Object.getPrototypeOf()

      • Object.setPrototypeOf(object, prototype): 设置一个对象的原型对象
      • Object.getPrototypeOf(obj):读取一个对象的原型对象
    • Object.keys(),Object.values(),Object.entries()

      const obj = { foo: 'bar', baz: 42 };
      Object.keys(obj) //['foo','baz']
      Object.values(obj) // ['bar',42]
      Object.entries(obj) // [['foo','bar'],['baz',42]]
      
    • Object.fromEntries(): 是Object.entries()的逆操作,用于将一个键值对数组转为对象。

          Object.fromEntries([
              ['foo', 'bar'],
              ['baz', 42]
          ])
          // { foo: "bar", baz: 42 }
      
    • Object.hasOwn(): 判断某个属性是否是某个对象自身的属性 ES2022

      js对象属性分自身和继承的两种:方法hasOwnProperty可以判断是否是自身属性。es2022在Object上新增静态方法 hasOwn 去判断

        let foo = {
            b: 456
        }
       Object.hasOwn(foo, 'b') // true
      

运算符扩展

  1. 指数运算符 (**) ES2016

右结合

    2**3 = 8;
    2**3**2 = 512; // 相当于 2**(3**2)
  1. 链判断运算符 (?.) ES2020
    • 左侧的对象是否为null或undefined。如果是的,就不再往下运算,而是返回undefined
    • 如果属性链有圆括号,链判断运算符对圆括号外部没有影响,只对圆括号内部有影响。
    const firstName = message?.body?.user?.firstName || 'default';
    obj?.prop // 对象属性是否存在
    obj?.[expr] // 同上
    func?.(...args) // 函数或对象方法是否存在, 存在则调用,否则返回undefined
  1. Null 判断运算符 (??) ES2020

?? 本质是逻辑运算,和 && 、||,多个逻辑运算符放一起时要用括号表明优先级,否则会报错

    // 通常给默认值
    const headerText = response.settings.headerText || 'Hello, world!';
    const animationDuration = response.settings.animationDuration || 300;
    const showSplashScreen = response.settings.showSplashScreen || true;
    // 但是这种,在值空字符串或者false或者0时,也会使用默认值,为避免这种情况:用 ??
    // ??  作用类似 || 但只有运算符左侧的值为null或undefined时,才会返回右侧的值
    const headerText = response.settings.headerText ?? 'Hello, world!';
    const animationDuration = response.settings.animationDuration ?? 300;
    const showSplashScreen = response.settings.showSplashScreen ?? true;
  1. 逻辑赋值运算符 &&=、 ||=、 ??= ES2021

相当于先进行逻辑运算,然后根据运算结果,再视情况进行赋值运算。

    // 或赋值运算符
    x ||= y
    // 等同于 只有x是false的时候才执行右边的; 和 x=x||y 一个结果 (逻辑运算符优先级 大于 赋值运算符)
    x || (x = y)

    // 与赋值运算符
    x &&= y
    // 等同于
    x && (x = y)

    // Null 赋值运算符
    x ??= y
    // 等同于
    x ?? (x = y)

Symbol

es6引入的新的原始数据类型,表示独一无二的值

  1. 注:
    • Symbol()函数前不能使用new命令,否则会报错,它是原始数据类型,不是对象
    • Symbol不是对象,所以不可以添加属性,是类似字符串的数据
    let s = Symbol();
    typeof s  // "symbol"

    // 1. 可以传参数去描述这个值
    let s1 = Symbol('foo');
    let s2 = Symbol('bar');

    s1 // Symbol(foo)
    s2 // Symbol(bar)

    s1.toString() // "Symbol(foo)"
    s2.toString() // "Symbol(bar)"

    // 2. Symbol的参数只表示对当前值的描述,因此就算相同参数的Symbol函数的返回值是不相等的。
    // 没有参数的情况
    let s1 = Symbol();
    let s2 = Symbol();

    s1 === s2 // false

    // 有参数的情况
    let s1 = Symbol('foo');
    let s2 = Symbol('foo');

    s1 === s2 // false
    // 3. Symbol 值不能与其他类型的值进行运算,会报错
    let sym = Symbol('My symbol');
    "your symbol is " + sym
    // TypeError: can't convert symbol to string
    `your symbol is ${sym}`
    // TypeError: can't convert symbol to string
    // 4. Symbol 值可以显式转为字符串,也可以转换为bolean值,但不可转为数字
    let sym = Symbol('My symbol');
    String(sym) // 'Symbol(My symbol)'
    sym.toString() // 'Symbol(My symbol)'

    Boolean(sym) // true
    !sym  // false
    if (sym) {
    // ...
    }

    Number(sym) // TypeError
    sym + 2 // TypeError
  1. Symbol.prototype.description ES2019
//  提供属性-description:获取symbol的描述文字 
    let sym = Symbol('My symbol');
    sym.description = 'My symbol'
  1. 作为属性名: symbol都不相等,可以作为唯一标识符,做属性名
    let mySymbol = Symbol();

    // 第一种写法
    let a = {};
    a[mySymbol] = 'Hello!';

    // 第二种写法
    let a = {
    [mySymbol]: 'Hello!'
    };

    // 第三种写法
    let a = {};
    Object.defineProperty(a, mySymbol, { value: 'Hello!' });

    // 以上写法都得到同样结果
    a[mySymbol] // "Hello!"

注意点

    // 1.Symbol 值作为对象属性名时,不能用点运算符,因为点运算符后面总是字符串,所以不会读取mySymbol作为标识名所指代的那个值,导致a的属性名实际上是一个字符串,而不是一个 Symbol 值
    const mySymbol = Symbol();
    const a = {};
    a.mySymbol = 'Hello!';
    a[mySymbol] // undefined
    a['mySymbol'] // "Hello!"
    // 2.Symbol 值作为属性名,遍历对象的时候,该属性不会出现在for...in、for...of循环中,也不会被Object.keys()、Object.getOwnPropertyNames()、JSON.stringify()返回。但是 Object.getOwnPropertySymbols(obj),Reflect.ownKeys(obj)返回的属性数组中包含symbol属性

  1. Symbol.for(key),Symbol.keyFor(key)
  • Symbol.for(key): 接受一个字符串做参数,在全局搜索有没有已经有该参数做描述的symbol值,有就返回该值,没有就新建一个返回,并登记到全局供搜索,不管运行环境是不是全局的
  • Symbol.keyFor(key): 返回一个已登记的 Symbol 类型值的描述key

: Symbol.for()与Symbol()这两种写法,都会生成新的 Symbol,区别:前者会在全局搜索,全局不存在才会返回新的;而后者不会去搜,每次调用都会返回一个新值

    let s1 = Symbol.for('foo');
    let s2 = Symbol.for('foo');
    s1 === s2 // true

    Symbol.for("bar") === Symbol.for("bar")
    // true
    Symbol("bar") === Symbol("bar")
    // false

    let s1 = Symbol.for("foo");
    Symbol.keyFor(s1) // "foo"
    let s2 = Symbol("foo");
    Symbol.keyFor(s2) // undefined

Set

es6新增的数据结构,类似数组,但其内每个成员都是唯一的

    const s = new Set();
    // 1. Set函数可以接受一个数组(或者具有 iterable 接口的其他数据结构)作为参数
    const set = new Set([1, 2, 3, 4, 4]);
    [...set]
    // [1, 2, 3, 4]
    //  2.去除数组的重复成员
    [...new Set(array)]
    // 3.去除字符串里面的重复字符
    [...new Set('ababbc')].join('')
    // 4. 向 Set 加入值的时候,不会发生类型转换,所以5和"5"是两个不同的值. set内部判断两个值是否相等,用的算法是Same-value-zero equality,类似于 === ,区别在于:1)===认为NaN和本身是不下跪等的,但Same-value-zero equality认为是相等的。 2)===认为+0===-0, 而Same-value-zero equality认为它们不相等
  1. 属性
  • Set.prototype.constructor:构造函数,默认就是Set函数。
  • Set.prototype.size:返回Set实例的成员总数。
  1. 操作方法:用于操作数据
  • Set.prototype.add(value):添加某个值,返回 Set 结构本身。
  • Set.prototype.delete(value):删除某个值,返回一个布尔值,表示删除是否成功。
  • Set.prototype.has(value):返回一个布尔值,表示该值是否为Set的成员。
  • Set.prototype.clear():清除所有成员,没有返回值。
  1. 遍历方法:用于遍历数据 Set的遍历顺序是按插入顺序遍历的
  • Set.prototype.keys():返回键名的遍历器
  • Set.prototype.values():返回键值的遍历器
  • Set.prototype.entries():返回键值对的遍历器
  • Set.prototype.forEach():使用回调函数遍历每个成员 set.keys()和set.values()返回是一样的,set没有键名,或者说set键名和键值一样
  1. 可以直接用 for...of 遍历
    let set = new Set(['red', 'green', 'blue']);
    for (let x of set) {
    console.log(x);
    }
    // red
    // green
    // blue
    for (let x of set) {
     console.log(x);
    }
    // red
    // green
    // blue

    let set = new Set(['red', 'green', 'blue']);

    for (let item of set.keys()) {
    console.log(item);
    }
    // red
    // green
    // blue

    for (let item of set.values()) {
    console.log(item);
    }
    // red
    // green
    // blue

    for (let item of set.entries()) {
    console.log(item);
    }
    // ["red", "red"]
    // ["green", "green"]
    // ["blue", "blue"]

WeakSet

和Set类似,是不重复值的集合, 与Set的区别在于两点:

  • WeakSet中只能存放对象,不可以放其他值
  • 而且WeakSet对对象是弱引用,即:WeakSet中存储的对象,若是外界其他地方对它的引用都取消了,那么垃圾回收机制会直接回收该对象的内存,不考虑它还存在于WeakSet中。 WeakSet 的成员是不适合引用的,因为它会随时消失
    const ws = new WeakSet();
    // 1. WeakSet接收一个数组或类数组,或任何具有Iterable接口的对象做参数,该数组的所有成员都会自动成为WeakSet的成员
    const a = [[1, 2], [3, 4]];
    const ws = new WeakSet(a); // WeakSet {[1, 2], [3, 4]} 
  1. 方法
  • WeakSet.prototype.add(value):向 WeakSet 实例添加一个新成员。
  • WeakSet.prototype.delete(value):清除 WeakSet 实例的指定成员。
  • WeakSet.prototype.has(value):返回一个布尔值,表示某个值是否在 WeakSet 实例之中
  1. WeakSet没有size属性,而且也不可以forEach遍历,因为它中对象都是弱引用,随时可能消失

Map

  • 键值对集合,键不局限于字符串,可以是其他任何类型。
  • 原始js对象提供 '字符串 - 值', map提供 '值 - 值' 对应
    // 1. 
    const m = new Map();
    const o = {p: 'Hello World'};
    m.set(o, 'content')
    m.get(o) // "content"
    // 2. 可以接收 一个二维数组做参数
    // 不仅仅是数组,任何具有 Iterator 接口、且每个成员都是一个双元素的数组的数据结构
    const map = new Map([
        ['name', '张三'],
        ['title', 'Author']
    ]);
    map.size // 2
    map.has('name') // true
    map.get('name') // "张三"
    map.has('title') // true
    map.get('title') // "Author"
    // 3. Map接收数组做参数,实际执行的算法是
    const items = [
        ['name', '张三'],
        ['title', 'Author']
    ];
    const map = new Map();
    items.forEach(
    ([key, value]) => map.set(key, value)
    );
    // 4. 对同一个键多次赋值,后面的会覆盖前面的
    const map = new Map();
        map
        .set(1, 'aaa')
        .set(1, 'bbb');
        map.get(1) // "bbb"
    // 5. get方法读取一个不存在的键,返回undefined
    new Map().get('asfddfsasadf')  // undefined
    // 6. 注意: 键为引用类型时,只有是对同一个对象的引用,map才视其为同一个键; 
    const map = new Map();
    map.set(['a'], 555);
    map.get(['a']) // undefined
    // 上面set和get方法,表面是针对同一个键,但实际上这是两个不同的数组实例,内存地址是不一样的
    // 7. 注意:  相同值的两个引用类型实例,在 Map 结构中被视为两个键。
    const map = new Map();
    const k1 = ['a'];
    const k2 = ['a'];
    map
    .set(k1, 111)
    .set(k2, 222);
    map.get(k1) // 111
    map.get(k2) // 222
    // 8. 注意: 键为简单类型时(boolean,string,number),只要严格相等,map就认为是同一个键
    // 1)0和-0就是一个键,
    // 2)布尔值true和字符串true则是两个不同的键。
    // 3)另外,undefined和null也是两个不同的键。
    // 4)虽然NaN不严格相等于自身,但 Map 将其视为同一个键
  1. 属性和方法
  • size属性, 返回map成员总数
  • Map.prototype.set(key, value):设置键名key对应的键值为value,然后返回当前整个 Map 结构
  • Map.prototype.get(key): 读取key对应的键值,如果找不到key,返回undefined
  • Map.prototype.has(key): 返回一个布尔值,表示某个键是否在当前 Map 对象之中
  • Map.prototype.delete(key): 删除某个键,返回Boolean值,表示是否删除成功
  • Map.prototype.clear(): 清空所有成员
    const map = new Map();
     map.set(1, 'a')
        .set(2, 'b')
        .set(3, 'c');
    map.get(1) // 'a'
    map.has(2) // true
    map.delete(3) // true
    map.get(3)  // undefined
  1. 遍历
  • Map.prototype.keys():返回键名的遍历器。
  • Map.prototype.values():返回键值的遍历器。
  • Map.prototype.entries():返回所有成员的遍历器。
  • Map.prototype.forEach():遍历 Map 的所有成员。
    // 等同于使用map.entries()
    for (let [key, value] of map) {
    console.log(key, value);
    }
    // "F" "no"
    // "T" "yes"

    const map = new Map([
        [1, 'one'],
        [2, 'two'],
        [3, 'three'],
    ]);

    [...map.keys()]
    // [1, 2, 3]

    [...map.values()]
    // ['one', 'two', 'three']

    [...map.entries()]
    // [[1,'one'], [2, 'two'], [3, 'three']]

    [...map]
    // [[1,'one'], [2, 'two'], [3, 'three']]

WeakMap

和Map类似,键值对集合,区别在于:

  • WeakMap只接受对象作为键名(null除外),不接受其他类型的值作为键名
  • WeakMap的键名所指向的对象为弱引用,不计入垃圾回收机制。
  • WeakMap和Map在API上的区别: WeakMap没有遍历操作(即没有keys()、values()和entries()方法,forEach),也没有size属性,也没有clear()方法清空
    const wm = new WeakMap();

    // size、forEach、clear 方法都不存在
    wm.size // undefined
    wm.forEach // undefined
    wm.clear // undefined

WeakRef ES2021

WeakSet和WeakMap都是基于弱引用的数据结构, es2021新增WeakRef用于创建对原始对象的弱引用

    let target = {};
    let wr = new WeakRef(target);

    let obj = wr.deref();
    if (obj) { // target 未被垃圾回收机制清除
    // ...
    }
    // target是原始对象,wr是WeakRef的实例,是对target的弱引用,不计入垃圾回收机制,wr的引用不会妨碍原始对象target被垃圾回收机制清除。

    // 、、deref方法: 如果源对象没有被来及回收机制清楚,则返回原始对象, 否则返回undefined

Proxy

对一些默认操作进行改写,理解: 在目标对象之前架设一层 ‘拦截’, 外界对目标的访问都需要通过这层拦截,可以在拦截里对外界的访问进行过滤和修改

    // target: 要代理的目标对象; handler:是一个配置对象,用来定制拦截行为
    var proxy = new Proxy(target, handler);
    var proxy = new Proxy({name:'aaa'}, {
        get: function(target, propKey) {
            return 35;
        },
        apply: function(target, thisBinding, args) {
            return args[0];
        },
    });
    // 如果handler没有设置任何拦截,那就等同于直接通向原对象

Proxy 支持的拦截操作:

  • get(target, propKey, receiver):拦截对象属性的读取,比如proxy.foo和proxy['foo']。
  • set(target, propKey, value, receiver):拦截对象属性的设置,比如proxy.foo = v或proxy['foo'] = v,返回一个布尔值。
  • has(target, propKey):拦截propKey in proxy的操作,返回一个布尔值。
  • deleteProperty(target, propKey):拦截delete proxy[propKey]的操作,返回一个布尔值。
  • ownKeys(target):拦截Object.getOwnPropertyNames(proxy)、Object.getOwnPropertySymbols(proxy)、Object.keys(proxy)、for...in循环,返回一个数组。该方法返回目标对象所有自身的属性的属性名,而Object.keys()的返回结果仅包括目标对象自身的可遍历属性。
  • getOwnPropertyDescriptor(target, propKey):拦截Object.getOwnPropertyDescriptor(proxy, propKey),返回属性的描述对象。
  • defineProperty(target, propKey, propDesc):拦截Object.defineProperty(proxy, propKey, propDesc)、Object.defineProperties(proxy, propDescs),返回一个布尔值。
  • preventExtensions(target):拦截Object.preventExtensions(proxy),返回一个布尔值。
  • getPrototypeOf(target):拦截Object.getPrototypeOf(proxy),返回一个对象。
  • isExtensible(target):拦截Object.isExtensible(proxy),返回一个布尔值。
  • setPrototypeOf(target, proto):拦截Object.setPrototypeOf(proxy, proto),返回一个布尔值。如果目标对象是函数,那么还有两种额外操作可以拦截。
  • apply(target, object, args):拦截 Proxy 实例作为函数调用的操作,比如proxy(...args)、proxy.call(object, ...args)、proxy.apply(...)。
  • construct(target, args):拦截 Proxy 实例作为构造函数调用的操作,比如new proxy(...args)。

Reflect

Reflect对象与Proxy对象一样,也是 ES6 为了操作对象而提供的新 API Reflect设计的目的:

  1. 将Object上的一些内部方法(如:Object.defineProperty),放在Reflect上。即从Reflect上可以拿到语言内部的方法
  2. 修改某些Object方法的返回结果,让其变得更合理。比如,Object.defineProperty(obj, name, desc)在无法定义属性时,会抛出一个错误,而Reflect.defineProperty(obj, name, desc)则会返回false
  3. 让Object操作都变成函数行为。某些Object操作是命令式,比如name in obj和delete obj[name],而Reflect.has(obj, name)和Reflect.deleteProperty(obj, name)让它们变成了函数行为
  4. Reflect对象一共有 13 个静态方法。 大部分与Object对象的同名方法的作用都是相同的,而且它与Proxy对象的方法(Proxy的handler配置中方法)是一一对应的
  • Reflect.apply(target, thisArg, args)
  • Reflect.construct(target, args)
  • Reflect.get(target, name, receiver)
  • Reflect.set(target, name, value, receiver)
  • Reflect.defineProperty(target, name, desc)
  • Reflect.deleteProperty(target, name)
  • Reflect.has(target, name)
  • Reflect.ownKeys(target)
  • Reflect.isExtensible(target)
  • Reflect.preventExtensions(target)
  • Reflect.getOwnPropertyDescriptor(target, name)
  • Reflect.getPrototypeOf(target)
  • Reflect.setPrototypeOf(target, prototype)

Promise

异步编程的一种解决方案,优于之前的回调函数和事件。

  • 有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。它的状态不受外界影响,且状态一旦改变不会再变回去。
  • Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected
  • 使用: Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolve和reject
    • resolve: 将Promise对象的状态从“未完成”变为“成功”(即从 pending 变为 resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去
    • reject: 将Promise对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。
   const promise = new Promise(function(resolve, reject) {
        if (/* 异步操作成功 */){
            resolve(value);
        } else {
            reject(error);
        }
   }

   promise.then(function(value) {
    // success
    }, function(error) {
    // failure
    });
  1. 方法
  • Promise.prototype.then(cb1,cb2): 状态改变时的回调函数。第一个参数是resolved状态的回调函数,第二个参数是rejected状态的回调函数 then方法返回的是一个新的Promise实例(注意,不是原来那个Promise实例)。因此可以采用链式写法,即then方法后面再调用另一个then方法
  • Promise.prototype.catch(error=>{}):是 .then(null, rejection)或.then(undefined, rejection)的别名,用于指定发生错误时的回调函数
  • Promise.prototype.finally() ES2018: 用于指定不管 Promise 对象最后状态如何,都会执行的操作
  • Promise.all(): 用于将多个 Promise 实例,包装成一个新的 Promise 实例
// 接受一个数组作为参数,p1、p2、p3都是 Promise 实例,是异步方法,如果是async await 就调用一下
// 参数可以不是数组,但必须具有 Iterator 接口,且返回的每个成员都是 Promise 实例
    const p = Promise.all([p1, p2, p3]);

p的状态由p1、p2、p3决定,分成两种情况 (1)只有p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数。 (2)只要p1、p2、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。 注意: 1)如果作为参数的 Promise 实例,自己定义了catch方法,那么它一旦被rejected,并不会触发Promise.all()的catch方法。作为参数的promise实例,有自定义的catch方法时,它实际是自己catch方法返回的promise,当自己的catch方法执行后,也就是resoved状态, 导致promise.all都执行的是.then 2) 如果 作为参数的 Promise 实例 没有自己的catch方法,就会调用Promise.all()的catch方法

  • Promise.race(): 同样是将多个 Promise 实例,包装成一个新的 Promise 实例。 参数与Promise.all()方法一样
    const p = Promise.race([p1, p2, p3]);
    // 只要p1,p2,p3有一个改变状态,p的状态就会改变,那个率先改变状态的promise实例的返回值会传给p
  • Promise.allSettled() ES2020 : 用来确定一组异步操作是否都结束了(不管成功或失败) 接受一个数组作为参数,数组的每个成员都是一个 Promise 对象,并返回一个新的 Promise 对象
    const promises = [
        fetch('/api-1'),
        fetch('/api-2')
    ];

    const allSettledPromise = await Promise.allSettled(promises);
    allSettledPromise.then(function (results) {
      console.log(results);
    });
    // [
    //    { status: 'fulfilled', value: 42 }, 成功
    //    { status: 'rejected', reason: -1 }   失败
    // ]

    // 1.Promise.allSettled()的返回值allSettledPromise,状态只可能变成fulfilled,也就是总会执行 .then。它的回调函数接收到的参数是数组results,和参数数组对应
    // 2. results数组成员格式固定,如上,成功返回的value,是返回值
  • Promise.any() ES2021: 接收一组promise实例做参数 有一个成功,即p会变fulfilled,只有所有都rejected时,p会rejected。和promise.all相反
   const  p = Promise.any([
        fetch('https://v8.dev/').then(() => 'home'),
        fetch('https://v8.dev/blog').then(() => 'blog'),
        fetch('https://v8.dev/docs').then(() => 'docs')
    ]).then((first) => {  // 只要有一个 fetch() 请求成功
        console.log(first);
    }).catch((error) => { // 所有三个 fetch() 全部请求失败
        console.log(error);
    });
  • Promise.resolve(): 将现有对象转为 Promise 对象
    Promise.resolve('foo')
    // 等价于
    new Promise(resolve => resolve('foo'))

Promise.resolve()方法的参数分成四种情况:

  • 参数是一个 Promise 实例: 什么都不做,返回原实例
  • 参数是一个 具有then方法的对象:将这个对象转为 Promise 对象,然后就立即执行该对象的then()方法
  • 数不是具有then()方法的对象,或根本就不是对象: 返回新的promise对象,并将状态变为resolved
  const p = Promise.resolve('Hello');
      p.then(function (s) {
      console.log(s)
  });
  // Hello
  • 不带有任何参数: 直接返回一个resolved状态的 Promise 对象
  const p = Promise.resolve();
  p.then(function () {
  // ...
  });
  • Promise.reject():会返回一个新的 Promise 实例,该实例的状态为rejected。 它的参数是一个字符串,会作为rejected的理由,后续catch方法的参数就是这个字符串
    const p = Promise.reject('出错了');
    // 等同于
    const p = new Promise((resolve, reject) => reject('出错了'))

    Promise.reject('出错了')
    .catch(e => {
        console.log(e === '出错了')
    })
  • Promise.try() 提案: 让同步函数同步执行,异步函数异步执行,并且让它们具有统一的 API,可以统一用 .then 来管理执行流程。Promise.try就是模拟try代码块
    const f = () => console.log('now');
    Promise.try(f);
    console.log('next');
    // now
    // next

Iterator

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

Iterator 的遍历过程是这样的: (1)创建一个指针对象,指向当前数据结构的起始位置。也就是说,遍历器对象本质上,就是一个指针对象。

(2)第一次调用指针对象的next方法,可以将指针指向数据结构的第一个成员。

(3)第二次调用指针对象的next方法,指针就指向数据结构的第二个成员。

(4)不断调用指针对象的next方法,直到它指向数据结构的结束位置。

每一次调用next方法,都会返回数据结构的当前成员的信息。具体来说,就是返回一个包含value和done两个属性的对象。其中,value属性是当前成员的值,done属性是一个布尔值,表示遍历是否结束。

Generator

ES6 提供的一种异步编程解决方案

  • 可以把它理解成,Generator 函数是一个状态机,封装了多个内部状态。执行 Generator 函数会返回一个遍历器对象,可以依次遍历 Generator 函数内部的每一个状态。
  • Generator 函数是一个普通函数,但是有两个特征。一是,function关键字与函数名之间有一个星号;二是,函数体内部使用yield表达式,定义不同的内部状态(yield在英语里的意思就是“产出”)。
        function* helloWorldGenerator() {
            yield 'hello';
            yield 'world';
            return 'ending';
        }

        var hw = helloWorldGenerator();
        hw.next()
        // { value: 'hello', done: false }

        hw.next()
        // { value: 'world', done: false }

        hw.next()
        // { value: 'ending', done: true }

        hw.next()
        // { value: undefined, done: true }
  • yield 表达式是暂停标志, yield表达式后面的表达式,只有当调用next方法、内部指针指向该语句时才会执行

async ES2017

是 Generator 函数的语法糖, async函数就是将 Generator 函数的星号(*)替换成async,将yield替换成await

es7 (2016)

  • 数组includes()方法,用来判断一个数组是否包含一个指定的值,根据情况,如果包含则返回true,否则返回false。
  • a ** b指数运算符,它与 Math.pow(a, b)相同。

es8 (2017)

  • async/await: 异步终极解决方案。详细解说参考这里
  • Object.values()
  • Object.entries()
  • String padding:String.prototype.padStart、String.prototype.padEnd。 允许将空字符串或其他字符串添加到原始字符串的开头或结尾
    String.padStart(targetLength,[padString])
    console.log('0.0'.padStart(4,'10')) //10.0

    String.padEnd(targetLength,padString])
    console.log('0.0'.padEnd(4,'0')) //0.00 
  • 函数参数列表结尾允许逗号
  • Object.getOwnPropertyDescriptors(targetObj): 获取一个对象的所有自身属性的描述符,如果没有任何自身属性,则返回空对象
  • SharedArrayBuffer 对象:用来表示一个通用的,固定长度的原始二进制数据缓冲区
/**
 * 
 * @param {*} length 所创建的数组缓冲区的大小,以字节(byte)为单位。  
 * @returns {SharedArrayBuffer} 一个大小指定的新 SharedArrayBuffer 对象。其内容被初始化为 0。
 */
new SharedArrayBuffer(length)

  • Atomics 对象:提供了一组静态方法用来对 SharedArrayBuffer 对象进行原子操作. Atomics 模块。与一般的全局对象不同,Atomics 不是构造函数,因此不能使用 new 操作符调用,也不能将其当作函数直接调用。Atomics 的所有属性和方法都是静态的 (与 Math 对象一样)

    • Atomics.add(): 将指定位置上的数组元素与给定的值相加,并返回相加前该元素的值。
    • Atomics.compareExchange(): 如果数组中指定的元素与给定的值相等,则将其更新为新的值,并返回该元素原先的值。
    • Atomics.exchange(): 将数组中指定的元素更新为给定的值,并返回该元素更新前的值。
    • Atomics.load(): 返回数组中指定元素的值
    • Atomics.or(): 将指定位置上的数组元素与给定的值相或,并返回或操作前该元素的值。
    • Atomics.store(): 将数组中指定的元素设置为给定的值,并返回该值
    • Atomics.sub(): 将指定位置上的数组元素与给定的值相减,并返回相减前该元素的值
    • Atomics.xor():将指定位置上的数组元素与给定的值相异或,并返回异或操作前该元素的值。
    • Atomics.wait():检测数组中某个指定位置上的值是否仍然是给定值,是则保持挂起直到被唤醒或超时。返回值为 "ok"、"not-equal" 或 "time-out"。调用时,如果当前线程不允许阻塞,则会抛出异常(大多数浏览器都不允许在主线程中调用 wait())。
    • Atomics.wake():唤醒等待队列中正在数组指定位置的元素上等待的线程。返回值为成功唤醒的线程数量。
    • Atomics.isLockFree(size):可以用来检测当前系统是否支持硬件级的原子操作。对于指定大小的数组,如果当前系统支持硬件级的原子操作,则返回 true;否则就意味着对于该数组,Atomics 对象中的各原子操作都只能用锁来实现。此函数面向的是技术专家。

es9 (2018)

  • 异步迭代:await可以和for...of循环一起使用,以串行的方式运行异步操作
  • Promise.finally()
  • Rest/Spread 属性:ES2015引入了Rest参数和扩展运算符。三个点(...)仅用于数组。ES2018为对象解构提供了和数组一样的Rest参数()和展开操作符。 Rest参数只能用于结尾
  • 正则表达式命名捕获组:允许命名捕获组使用符号?
  • 正则表达式反向断言(lookbehind)
  • 正则表达式dotAll模式:正则表达式中点.匹配除回车外的任何单字符,标记s改变这种行为,允许行终止符的出现
  • 正则表达式 Unicode 转义: Unicode 属性转义形式为\p{...}和\P{...}
  • 非转义序列的模板字符串:移除对 ECMAScript 在带标签的模版字符串中转义序列的语法限制

es10 (2019)

  • Array.flat()和Array.flatMap():数组展平
  • String.trimStart()和String.trimEnd():去掉开头结尾空格文本
  • String.prototype.matchAll:为所有匹配的匹配对象返回一个迭代器
    const raw_arr = 'test1  test2  test3'.matchAll((/t(e)(st(\d?))/g));
    // raw_arr [Array(4),Array(4),Array(4)]
  • Symbol.prototype.description:只读属性,回 Symbol 对象的可选描述的字符串
  • Object.fromEntries()
  • 可选 Catch: try...catch错误处理过程中,如果没有给catch传参数的话,代码就会报错。在新规范中,我们可以省略catch绑定的参数和括号
  • JSON Superset 超集: 之前如果JSON字符串中包含有行分隔符(\u2028) 和段落分隔符(\u2029),那么在解析过程中会报错。现在ES2019对它们提供了支持
  • JSON.stringify() 加强格式转化: emoji表情的字符长度是 2
  • Array.prototype.sort() 更加稳定, 之前允许不稳定排序算法,现在大都用的稳定排序算法
  • Function.prototype.toString() 重新修订: 现在toString(),将从头到尾返回源代码中的实际文本片段。 包括注释、空格和语法详细信息。
function /* a comment */ foo() {}

// 之前
foo.toString(); // 'function foo() {}'
// 现在
foo.toString(); // 'function /* a comment  */ foo () {}'

es11 (2020)

  • 动态 import ():它接收一个字符串作为模块标识符,并返回一个 promise,主要用于动态按需导入
  • 空值合并运算符:表达式在 ?? 的左侧 z只有运算符求值为undefined或null,才返回其右侧
  • 可选链接:?.用户检测不确定的中间节点,其前为null时,中断后续
  • BigInt:新基本数据类型,表示任意精度的整数
  • globalThis:浏览器:window、worker:self、node:global
  • Promise.allSettled:返回一个在所有给定的promise已被决议或被拒绝后决议的promise,并带有一个对- 象数组,每个对象表示对应的promise结果
  • for-in 结构:用于规范for-in语句的遍历顺序

es12 (2021)

  • String.prototype.replaceAll :有了这个 API,替换字符不用写正则了 模式的所有匹配都会被替代项替换。模式可以是字符串或正则表达式,而替换项可以是字符串或针对每次匹配执行的函数。并返回一个全新的字符串
    const str = "student is a real student";
    const newStr = str.replace(/student/g, "hahaha");
    const newStr = str.replaceAll('student', "hahaha");
    // 结果都是hahaha is a real hahaha
  • Promise.any() :有一个成功,即p会变fulfilled,只有所有都rejected时,p会rejected。和promise.all相反。
  • 新增逻辑赋值操作符: ??=、&&=、 ||=
  • WeakRef
  • 下划线 (_) 分隔符:使用 _ 分隔数字字面量以方便阅读
let x = 2_3333_3333
// x 的值等同于 233333333,只是这样可读性更强,不用一位一位数了
  • Intl.ListFormat :用来处理和多语言相关的对象格式化操作
  • Intl.DateTimeFormat API 中的 dateStyle 和 timeStyle 的配置项:用来处理多语言下的时间日期格式化的函数