ES6 万字极简总结,帮你快速过一遍知识点!

14,424 阅读24分钟

前言

由于常用es6语法不多,有时需要查阅一些不常用的函数或特性,需要重新查看阮一峰大佬的《ESCAScript 6 入门》,这段时间有空余,正好整理出来所有知识点,并附带解释。帮你快速唤醒es6的已经忘记的知识点!

来自阮一峰大佬《EMCAScript 6入门》总结精炼
部分解释取自MDN
内部包含部分ES2017ES2022标准知识点穿插

正文

let 和 const

let: 声明变量
const: 声明只读常量 声明必须赋初始值 常量值不可修改

共性特点

  • 没有变量提升(即:脚本开始运行时提前声明为unefined的变量)、拥有临时性死区(声明前调用会报错)
  • 不允许重复声明同名变量
  • 拥有块级作用域(即:超出当前变量声明作用域无法调用该变量)

解构赋值

数组:位置顺序解构
对象:数据结构相同且同名变量解构
字符串 :位置顺序解构,可解构length(let {length:len} = 'test')
函数:根据传入参数类型不同 按照以上规则解构

共性特点

  • 可指定默认值(即:let [f = 'ff'] = []
  • 可完全解构(即:等号两边数据结构完全相等)
  • 可不完全解构(即:等号两边数据结构相等,但等号左边只解构部分变量)

模板字符串

`` 使用模板字符串
${}在模板字符串中使用变量

  • 可多行使用(即``可换行)
  • 可使用反斜杠转义\(即 \ ` 使用)
  • 可通过${}调用函数(即${fn()})
  • 可嵌套模板字符串

字符串拓展方法

includes() :返回布尔值,表示是否找到了参数字符串。

let s = 'Hello world!';
s.includes('o') // true

startsWith() :返回布尔值,表示参数字符串是否在原字符串的头部。

s.startsWith('Hello') // true

endsWith() :返回布尔值,表示参数字符串是否在原字符串的尾部。

s.endsWith('!') // true

repeat() : 返回一个新字符串,表示将原字符串重复n次。

'hello'.repeat(2) // "hellohello"

padStart() : 字符串不够指定长度,将头部补全

'x'.padStart(5, 'ab') // 'ababx'
'x'.padStart(4, 'ab') // 'abax'

padEnd() : 字符串不够指定长度,将尾部补全。

'x'.padEnd(5, 'ab') // 'xabab'
'x'.padEnd(4, 'ab') // 'xaba'

trimStart() : 消除字符串头部的空格(返回的都是新字符串,不会修改原始字符串)

const s = '  abc  ';
s.trimStart() // "abc  "

trimEnd() : 消除尾部的空格 (返回的都是新字符串,不会修改原始字符串)

s.trimEnd() // "  abc"

matchAll() : 返回一个正则表达式在当前字符串的所有匹配
replaceAll() : 可以一次性替换所有匹配。

'aabbcc'.replaceAll('b', '_') // 'aa__cc'

at() :接受一个整数作为参数,返回参数指定位置的字符,支持负索引

const str = 'hello';
str.at(1) // "e"

数值拓展

二进制

  • 前缀用0b0b表示(即:0b111110111 === 503 // true

八进制

  • 前缀用 0o 标识(即:0o767 === 503 // true

数值分隔符

  • 为了增加数值可读性 使用下划线(_)作为分隔符 (即let budget = 1_000_000_000_000;)
  • 不能放在数值的最前面(leading)或最后面(trailing)。
  • 不能两个或两个以上的分隔符连在一起。
  • 小数点的前后不能有分隔符。
  • 科学计数法里面,表示指数的eE前后不能有分隔符。

BigInt 只用来表示整数,没有位数的限制,任何位数的整数都可以精确表示

  • 可以使用BigInt() 进行类型转换
  • BigInt 类型的数据必须添加后缀n
  • BigInt 不能与普通数值进行混合运算。
  • 可以使用Boolean()Number()String()这三个方法,将 BigInt 可以转为布尔值、数值和字符串类型,转为字符串时后缀n会消失。
  • BigInt 与字符串混合运算时,会先转为字符串,再进行运算。

实例方法

  • Number.isFinite() :用来检查一个数值是否为有限的(finite),即不是Infinity

  • Number.isNaN() :用来检查一个值是否为NaN

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

  • Number.parseInt:用来转换为正整数数值

  • Number.parseFloat:来转换为双浮点数值

  • Number.EPSILON:极小的常量,它表示 1 与大于 1 的最小浮点数之间的差

  • Number.MAX_SAFE_INTEGER: 表示javascript能精表示的整数最大值

  • Number.MIN_SAFE_INTEGER: 表示javascript能精表示的整数最小值

  • Number.isSafeInteger():用来判断一个整数是否落在javascript能表示最大值和最小值之内

  • Math.trunc():用于去除一个数的小数部分,返回整数部分

  • Math.sign():用来判断一个数到底是正数、负数、还是零。对于非数值,会先将其转换为数值

  • Math.cbrt():用于计算一个数的立方根

  • Math.clz32():将参数转为 32 位无符号整数的形式,然后返回这个 32 位值里面有多少个前导 0

  • Math.imul:返回两个数以 32 位带符号整数形式相乘的结果,返回的也是一个 32 位的带符号整数

  • Math.fround:返回一个数的32位单精度浮点数形式

  • Math.hypot:返回所有参数的平方和的平方根

  • Math.expm1(x):返回 ex - 1,即Math.exp(x) - 1

  • Math.log1p(x):返回1 + x的自然对数,即Math.log(1 + x)。如果x小于-1,返回NaN

  • Math.log10(x):返回以 10 为底的x的对数。如果x小于 0,则返回 NaN

  • Math.log2(x):返回以 2 为底的x的对数。如果x小于 0,则返回 NaN

  • Math.sinh(x) 返回x的双曲正弦

  • Math.cosh(x) 返回x的双曲余弦

  • Math.tanh(x) 返回x的双曲正切

  • Math.asinh(x) 返回x的反双曲正弦

  • Math.acosh(x) 返回x的反双曲余弦

  • Math.atanh(x) 返回x的反双曲正切

函数拓展

函数默认值

  • (即:函数的参数指定默认值)
    • 可以和解构一起使用(即:function foo({x, y = 5}){}
    • 通常参数默认值的位置在尾部,方便使用默认值忽略(即:function f(x = 1, y),只穿一个参数则x无法正常使用默认值忽略)
    • 使用默认值会导致函数了length属性失真(即:因为length属性的含义是,该函数预期传入的参数个数。某个参数指定默认值以后,预期传入的参数个数就不包括这个参数了)
    • 设置默认值会在函数初始化时产生单独作用域,初始化结束作用域消失 即:
      var x = 1;
      function f(x, y = x) {
        console.log(y);
      }
      f(2) // 2
    

rest参数

  • (即:...变量用于接受函数多余参数)

name属性

  • (即:返回该函数的函数名 函数名.name

箭头函数

  • => (即:function foo = () => {}
    • 没有自己的this对象,内部使用this会采用调用时上级作用域 this
    • 不可以对箭头函数使用new命令,否则会抛出错误
    • 不可以使用 arguments 对象,如果要用,可以用 rest 参数代替
    • 不可以使用yield命令,箭头函数不能用作 Generator 函数
  • Function.prototype.toString()(即:方法返回函数代码本身,以前会省略注释和空格)
  • catch 的参数省略(即:try {} catch {}

数组拓展

拓展运算符

  • ...将一个数组转为用逗号分隔的参数序列

数组空位

  • 数组的某一个位置没有任何值,ES6 则是明确将空位转为undefined

静态方法

  • Array.from() 用于将两类对象转为真正的数组: (array-like object)和(iterable)和 (Set) 和(Map) 即:
let s = {
    '0': 'a',
    '1': 'b',
    '2': 'c',
    length: 3
};
let arr2 = Array.from(s); // ['a', 'b', 'c']
  • Array.of() 用于将一组值,转换为数组 (即:Array.of(3, 11, 8) // [3,11,8])

实例方法

  • copyWithin() 在当前数组内部,将指定位置的成员复制到其他位置(会覆盖原数组),返回当前数组。会修改当前数组 (即:[1, 2, 3, 4, 5].copyWithin(0, 3) // [4, 5, 3, 4, 5]

  • find() 用于找出第一个符合条件的数组成员。然后返回该成员。如果没有符合条件的成员,则返回undefined

  • findIndex() 返回第一个符合条件的数组成员的位置,如果所有成员都不符合条件,则返回-1

  • fill() 使用给定值,填充一个数组 (即:['a', 'b', 'c'].fill(7) // [7, 7, 7])

  • includes() 返回一个布尔值,表示某个数组是否包含给定的值

  • flat() 将多维数组处理成一维数组,返回一个新数组

  • flatMap() 使用映射函数映射每个元素,然后将结果压缩成一个新数组。它与 map 连着深度值为 1 的 flat 几乎相同 (即:[1, 2, [3], [4, 5], 6].flatMap(num => num) // [1, 2, 3, 4, 5, 6]

  • ES2022新增 at() ,接受一个整数作为参数,返回对应位置的成员,并支持负索引。这个方法不仅可用于数组,也可用于字符串和类型数组 (即:[5, 12, 8, 130, 44].at(-2) // 130

对象拓展

属性简洁表示法

  • 允许在大括号里面,直接写入变量和函数,作为对象的属性和方法
    • (即:const foo = {name:name,method(){}})
  • 属性名表达式 字面量定义对象(即:obj['a'+'bc'] = 123;
  • 方法name属性 将对象方法函数,返回函数名 如果使用取值函数和存值函数则如下:
const obj = {
  get foo() {},
  set foo(x) {}
};
const descriptor = Object.getOwnPropertyDescriptor(obj, 'foo');
descriptor.get.name // "get foo"
descriptor.set.name // "set foo"

有两种特殊情况:bind方法创造的函数,name属性返回bound加上原函数的名字;Function构造函数创造的函数,name属性返回anonymous

super

  • 指向当前对象的原型对象。(即:super.xxx等同于Object.getPrototypeOf(this).xxx

静态方法

  • Object.getOwnPropertyNames()返回一个数组,包含对象自身的所有属性(不含 Symbol 属性,但是包括不可枚举属性)的键名。

  • Object.getOwnPropertySymbols()返回一个数组,包含对象自身的所有 Symbol 属性的键名。

  • Reflect.ownKeys() 返回一个数组,包含对象自身的(不含继承的)所有键名

Object.getOwnPropertyNames()Object.getOwnPropertySymbols()Reflect.ownKeys() 都遵守同样的属性遍历的次序规则

  • 首先遍历所有数值键,按照数值升序排列。
  • 其次遍历所有字符串键,按照加入时间升序排列。
  • 最后遍历所有 Symbol 键,按照加入时间升序排列。
  • Object.is()  方法判断两个值是否为同一个值

  • Object.assign() 忽略enumerablefalse的属性,属性从一个或多个源对象复制到目标对象,返回修改后的对象。

因为 Object.assign() 只复制属性值,假如源对象是一个对象的引用,它仅仅会复制其引用值。

  • Object.getOwnPropertyDescriptors() 所指定对象的所有自身属性的描述符,如果没有任何自身属性,则返回空对象。主要是为了解决Object.assign()无法正确拷贝get属性和set属性的问题。

  • __proto__属性 用来读取或设置当前对象的原型对象

  • Object.setPrototypeOf() 用来设置一个对象的原型对象(prototype),返回参数对象本身

  • Object.getPrototypeOf() 用于读取一个对象的原型对象

  • Object.entries() 返回数据的迭代对象,包含该对象的键值对数组

  • Object.keys() 返回数据的迭代对象,包含该对象的键名数组

  • Object.values() 返回数据的迭代对象,包含该对象的值名数组

  • Object.fromEntries()用于将一个键值对数组转为对象。

  • Object.hasOwn() 判断是否为自身的属性

JavaScript 对象的属性分成两种:自身的属性和继承的属性。对象实例有一个hasOwnProperty()方法,可以判断某个属性是否为原生属性。ES2022 在Object对象上面新增了一个静态方法Object.hasOwn(),也可以判断是否为自身的属性

运算符拓展

指数运算符

  • (即:**) 右结合,多个指数运算符连用时,是从最右边开始计算

链判断运算符

  • ES2020 引入了“链判断运算符” 判断对象是否存在。
    • 短路机制 ?.运算符相当于一种短路机制,只要不满足条件,就不再往下执行
    • 括号的影响 如果有圆括号包裹,只对圆括号颞部产生影响 (即(a?.b).c
    • 右侧不得为十进制数值 (即:foo?.3:0 会被解析成三元运算符进行处理)
  • obj?.prop // 对象属性是否存在
  • obj?.[expr] // 同上
  • func?.(...args) // 函数或对象方法是否存在

Null 判断运算符

  • ES2020  Null 判断运算符?? 只有运算符左侧的值为nullundefined时,才会返回右侧的值 (即:user.name ?? 'zhangsan'

逻辑赋值运算符

  • ES2021 引入 三个运算符||=&&=??=相当于先进行逻辑运算,然后根据运算结果,再视情况进行赋值运算。

x ||= y == x || (x = y)
x &&= y == x && (x = y)
x ??= y == x ?? (x = y)

正则拓展

  • RegExp 允许第二个参数添加修饰符(即:new RegExp(/abc/ig, 'i').flags
  • 字符串正则方法 (即:这 4 个方法,在语言内部全部调用RegExp的实例方法)
  • String.prototype.match 调用 RegExp.prototype[Symbol.match]
  • String.prototype.replace 调用 RegExp.prototype[Symbol.replace]
  • String.prototype.search 调用 RegExp.prototype[Symbol.search]
  • String.prototype.split 调用 RegExp.prototype[Symbol.split]

u修饰符

  • 含义为“Unicode 模式”,用来正确处理大于\uFFFF的 Unicode 字符 /^\uD83D/u.test('\uD83D\uDC2A') // false
    • 点字符 除了换行符以外的任意单个字符。对于码点大于0xFFFFUnicode 字符,点字符不能识别,必须加上u修饰符。(即:/^.$/u.test('𠮷') // true

    • Unicode字符表示法 使用大括号表示 Unicode 字符,在正则表达式中必须加上u修饰符,才能识别当中的大括号,否则会被解读为量词(即:/\u{20BB7}/u.test('𠮷') // true

    • 量词 使用u修饰符后,所有量词都会正确识别码点大于0xFFFFUnicode 字符(即:/𠮷{2}/.test('𠮷𠮷') // false

    • 预定义模式 u修饰符影响到预定义模式,能否正确识别码点大于0xFFFFUnicode 字符 \S是预定义模式 (即:/^\S$/u.test('𠮷') // true

    • i 修饰符 Unicode 字符的编码不同,但是字型很相近,加u修饰符 识别非规范的字符(即:/[a-z]/iu.test('\u212A') // true

    • 转义会在u模式下报错(即:/,/u // 报错

y修饰符

  • 全局匹配 确保匹配必须从剩余的第一个位置开始

实例属性

  • RegExp.prototype.unicode 属性 表示是否设置了u修饰符。
    const r2 = /hello/u;
    r2.unicode // true
  • RegExp.prototype.sticky属性 表示是否设置了y修饰符 (即:var r = /hello\d/y; r.sticky // true
  • RegExp.prototype.flags属性 会返回正则表达式的修饰符 (即:/abc/ig.flags)

Symbol

  • 表示独一无二的值 let s = Symbol();
  • 不能使用new命令 (即:因为生成的 Symbol 是一个原始类型的值)
  • 接受一个字符串作为参数
  • Symbol 值不能与其他类型的值进行运算
  • Symbol 值不能转换成数值 (即:Number(xxx) //typeError)
  • Symbol 作为属性名,遍历对象的时候,该属性不会出现在for...infor...of循环中,也不会被Object.keys()Object.getOwnPropertyNames()JSON.stringify()返回。 (可以通过Object.getOwnPropertySymbols()方法获取所有 Symbol 属性名)

实例属性

  • Symbol.prototype.description ES2019 提供了一个实例属性description,直接返回 Symbol 的描述。(即:const sym = Symbol('foo'); sym.description // "foo")

实例方法

  • Symbol.for() 传入字符串参数搜索是否有同名Symbol值,有返回Symbol值,没有就新建一个以该字符串为名称的Symbol值.
  • Symbol.keyFor()方法返回一个已登记的 Symbol 类型值的key

内部属性

  • Symbol.hasInstance 指向一个内部方法。当其他对象使用instanceof运算符,判断是否为该对象的实例时,会调用这个方法
  • Symbol.isConcatSpreadable 等于一个布尔值,表示该对象用于Array.prototype.concat()时,是否可以展开。
    arr2[Symbol.isConcatSpreadable] = false; 
    ['a', 'b'].concat(arr2, 'e') // ['a', 'b', ['c','d'], 'e']
  • Symbol.species 一个用于创建派生对象的构造器函数。创建衍生对象时,会使用该属性

  • Symbol.match 一个对字符串进行匹配的方法,也确定一个对象是否可以作为正则表达式使用。被String.prototype.match()使用。

  • Symbol.replace 一个替换匹配字符串的子串的方法。被 String.prototype.replace() 使用。

  • Symbol.search 一个返回一个字符串中与正则表达式相匹配的索引的方法。被 String.prototype.search() 使用

  • Symbol.split 一个在匹配正则表达式的索引处拆分一个字符串的方法.。被 String.prototype.split() 使用。

  • Symbol.iterator 一个返回一个对象默认迭代器的方法。被 for...of 使用。

  • Symbol.toPrimitive - 指向一个方法。该对象被转为原始类型的值时,会调用这个方法,返回该对象对应的原始类型值。会接受一个字符串参数,表示当前运算的模式,一共有三种模式。

    • Number:该场合需要转成数值
    • String:该场合需要转成字符串
    • Default:该场合可以转成数值,也可以转成字符串
  • Symbol.toStringTag 指向一个方法。在该对象上面调用Object.prototype.toString方法时,如果这个属性存在,它的返回值会出现在toString方法返回的字符串之中,表示对象的类型。

  • Symbol.unscopables 指向一个对象。该对象指定了使用with关键字时,哪些属性会被with环境排除。

Set 和 Map

Set

  • 存储任何类型的唯一值,无论是原始值或者是对象引用。
  • NaN 和 undefined 都可以被存储在 Set 中,NaN 之间被视为相同的值
  • Set 内部判断两个值是否不同,会采用精确相等运算符(===),因此可以成员去重,但是对于相同属性的对象成员,除非它们指向同一个对象,否则不会去重。

声明示例: const s = new Set([1,2,3]);

实例属性

  • Set.prototype.constructor:构造函数,默认就是Set函数。
  • Set.prototype.size:返回Set实例的成员总数。

实例方法

  • Set.prototype.add(value):添加某个值,返回 Set 结构本身。

  • Set.prototype.delete(value):删除某个值,返回一个布尔值,表示删除是否成功。

  • Set.prototype.has(value):返回一个布尔值,表示该值是否为Set的成员。

  • Set.prototype.clear():清除所有成员,没有返回值。

  • Set.prototype.keys():返回键名的遍历器

  • Set.prototype.values():返回键值的遍历器

  • Set.prototype.entries():返回键值对的遍历器

  • Set.prototype.forEach():使用回调函数遍历每个成员

WeakSet

  • 成员只能是对象
  • WeakSet 中的对象都是弱引用
  • 垃圾回收机制不考虑 WeakSet 对该对象的引用 (即:其他对象都不再引用该对象,垃圾回收机制会自动回收该对象所占用的内存,不考虑该对象还存在于 WeakSet 之中)
  • WeakSet 不可遍历
  • WeakSet 没有size属性
  • 其余规则和Set相同

声明示例:const ws = new WeakSet([[1, 2], [3, 4]]);
WeakSet 的一个用处,是储存 DOM 节点 而不用担心这些节点从文档移除时,会引发内存泄漏

实例方法

  • WeakSet.prototype.add(value) :向 WeakSet 实例添加一个新成员
  • WeakSet.prototype.delete(value) :清除 WeakSet 实例的指定成员
  • WeakSet.prototype.has(value) :返回一个布尔值,表示某个值是否在 WeakSet 实例之中

Map

  • 对象保存键值对,并且能够记住键的原始插入顺序。任何值(对象或者基本类型)都可以作为一个键或一个值。
  • Map 键名相等判断规则
    • NaN 是与 NaN 相等的(虽然 NaN !== NaN),剩下所有其它的值是根据 === 运算符的结果判断是否相等。
    • Map 的键是跟内存地址绑定的,只要内存地址不一样,就视为两个键

声明示例:new Map([['baz', 3]])

实例属性

  • size 属性 返回 Map 结构的成员总数。

实例方法

  • Map.prototype.set(key, value) 设置键名key对应的键值为value,然后返回整个 Map 结构。如果key已经有值,则键值会被更新,否则就新生成该键。可以使用链式写法

  • Map.prototype.get(key) 读取key对应的键值,如果找不到key,返回undefined

  • Map.prototype.has(key) 返回一个布尔值,表示某个键是否在当前 Map 对象之中

  • Map.prototype.delete(key) 删除某个键值,返回true。如果删除失败,返回false

  • Map.prototype.clear() 清除所有成员,没有返回值。

  • Map.prototype.keys():返回键名的遍历器。

  • Map.prototype.values():返回键值的遍历器。

  • Map.prototype.entries():返回所有成员的遍历器。

  • Map.prototype.forEach():遍历 Map 的所有成员。

WeakMap

  • WeakMap只接受对象作为键名(null除外)
  • WeakMap的键名所指向的对象,不计入垃圾回收机制
  • WeakMap 不可遍历
  • WeakMap 没有size属性
  • 不支持clear方法
  • 其余规则和Map相同

声明示例: new WeakMap([[k1, 'foo'], [k2, 'bar']])

实例方法

  • WeakMap.prototype.delete(key) 删除 WeakMap 中与 key 相关联的值。删除之后,返回 false
  • WeakMap.prototype.get(key) 返回 WeakMap 中与 key 相关联的值,如果 key 不存在则返回 undefined
  • WeakMap.prototype.has(key) 返回一个布尔值,断言一个值是否已经与 WeakMap 对象中的 key 关联。
  • WeakMap.prototype.set(key, value)给  WeakMap 中的 key 设置一个 value。该方法返回一个 WeakMap 对象。

Proxy

  • Proxy 对象用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)。
  • Proxy 目标对象的透明代理 即不做任何拦截的情况下,也无法保证与目标对象的行为一致 ,因为 Proxy 代理目标对象内部的this关键字会指向 Proxy 代理

支持的拦截方法

  • get(target, propKey, receiver) 拦截对象属性的读取 (即:proxy.fooproxy['foo'])

  • set(target, propKey, value, receiver) 拦截对象属性的设置,返回一个布尔值(即:proxy.foo = vproxy['foo'] = v

  • has(target, propKey) 拦截 in 操作符的捕捉器。(即:propKey in proxy

  • deleteProperty(target, propKey) 拦截delete 操作符的捕捉器。(即: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)

Proxy.revicable()

  • 返回一个可取消的 Proxy 实例。
  • revoke属性是一个函数,可以取消Proxy实例。当执行revoke函数之后,再访问Proxy实例,会抛出错误。(即:let {proxy, revoke} = Proxy.revocable(target, handler)

Reflect

  • Reflect 是一个内置的对象,它提供拦截 JavaScript 操作的方法

  • 将某些 Object 方法 移植到Reflect并修改的更合理 (即:修改属性添加返回值等)

  • Reflect并非一个构造函数,所以不能通过new 运算符对其进行调用

  • Reflect的所有属性和方法都是静态的

  • Reflect对象的方法与Proxy对象的方法一一对应,通常可以调用Reflect方法,完成默认行为 (即:不管Proxy怎么修改默认行为,你总可以在Reflect上获取默认行为。)

静态方法

  • Reflect.apply(func, thisArg, args) 对一个函数进行调用操作,同时可以传入一个数组作为调用参数。和 Function.prototype.apply() 功能类似。

  • Reflect.get(target, name, receiver) 查找并返回target对象的name属性,如果没有该属性,则返回undefined

  • Reflect.set(target, name, value, receiver) 设置target对象的name属性等于value

  • Reflect.has(obj, name) 判断一个对象是否存在某个属性,和 in 运算符的功能完全相同。

  • Reflect.deleteProperty(obj, name) 等同于delete obj[name],用于删除对象的属性

  • Reflect.construct(target, args) 对构造函数进行 new操作,相当于执行 new target(...args)

  • Reflect.getPrototypeOf(obj) 用于读取对象的__proto__属性,对应Object.getPrototypeOf(obj)

  • Reflect.setPrototypeOf(obj, newProto) 用于设置目标对象的原型(prototype),对应Object.setPrototypeOf(obj, newProto)方法。它返回一个布尔值,表示是否设置成功。

  • Reflect.defineProperty(target, propertyKey, attributes) 基本等同于Object.defineProperty,用来为对象定义属性,如果设置成功就会返回 true

  • Reflect.getOwnPropertyDescriptor(target, propertyKey) 等同于Object.getOwnPropertyDescriptor,用于得到指定属性的描述对象,如果对象中存在该属性,则返回对应的属性描述符,否则返回 undefined

  • Reflect.isExtensible (target) 对应Object.isExtensible,返回一个布尔值,表示当前对象是否可扩展

  • Reflect.preventExtensions(target) 对应Object.preventExtensions方法,用于让一个对象变为不可扩展。它返回一个布尔值,表示是否操作成功

  • Reflect.ownKeys (target) 返回一个包含所有自身属性(不包含继承属性)的数组。(类似于 Object.keys(), 但不会受enumerable 影响)

Promise

  • 是异步编程的一种解决方案,用于解决回调地狱问题
  • 对象的状态不受外界影响
    • pending 初始状态,待处理
    • fulfilled 意味着操作成功完成,已完成
    • rejected 意味着操作失败,已拒绝
  • 一旦状态改变,就不会再变,任何时候都可以得到这个结果
  • 无法取消Promise,一旦新建它就会立即执行
  • 将异步操作最终的成功返回值或者失败原因和相应的处理程序关联起来做到可以像同步方法一样返回值
  • 不会立即返回最终的值,而是会返回一个 Promise实例

实例方法

  • Promise.prototype.then() 是为 Promise 实例添加状态改变时的回调函数,返回的是一个新的Promise实例

  • Promise.prototype.catch() 用于指定发生错误时的回调函数,若回调函数被调用,则兑现其返回值,否则兑现原来的 Promise 兑现的值。

  • Promise.prototype.finally() 指定不管 Promise 对象最后状态如何,都会执行的操作, ES2018 引入标准

静态方法

  • Promise.all() 将多个 Promise 实例,包装成一个新的 Promise 实例
    • 参数可以不是数组,但必须具有 Iterator 接口,且返回的每个成员都是 Promise 实例

    • 执行多个Promise,虽然按顺序执行,但是由于异步回调时间不固定的情况下并不能保证执行顺序。

    • 不会阻塞线程,只会在合适的时机调用整体fulfilledrejected的回调函数

    • 遇到执行回调中第一个失败。会立刻执行自身的rejected的回调函数,并且只会抛出第一个失败rejected,后续遇到rejected均不执行

    • 不会因为内部异步函数的失败,而中断后续所有的异步函数执行

    • 可以更快的捕获异常问题。 详情参见: 关于 Promise.all 和 async await 这档子事儿

    let p1 = new Promise(()=>{});
    let p2 = new Promise(()=>{});
    const p = Promise.all([p1, p2]);
  • Promise.race() 同时接受多个 Promise 实例,包装成一个新的 Promise 实例。一旦迭代器中的某个 Promise 解决或拒绝,返回的 Promise 就会解决或拒绝。
    • 传的参数迭代是空的,则返回的 Promise 将永远等待

    • 如果迭代包含一个或多个非承诺值和/或已解决/拒绝的承诺,则 Promise.race 将解析为迭代中找到的第一个值。

        var p1 = new Promise(function(resolve, reject) {
            setTimeout(resolve, 500, "one");
        });
        var p2 = new Promise(function(resolve, reject) {
            setTimeout(resolve, 100, "two");
        });

        Promise.race([p1, p2]).then(function(value) {
          console.log(value); // "two"
          // 两个都完成,但 p2 更快
        });
  • Promise.allSettled()接受多个 Promise 实例, ES2020 引入了Promise.allSettled()方法,用来确定一组异步操作是否都结束了(不管成功或失败)
    • 返回一个在所有给定的 Promise 都已经fulfilledrejected后的 Promise,并带有一个对象数组,每个对象表示对应的 Promise 结果

    • 接受一个数组作为参数,数组的每个成员都是一个 Promise 对象

const promise1 = Promise.resolve(3);
const promise2 = new Promise((resolve, reject) => setTimeout(reject, 100, 'foo'));
const promises = [promise1, promise2];

Promise.allSettled(promises).
  then((results) => results.forEach((result) => console.log(result.status)));
  • Promise.any() ES2021 引入了Promise.any()接受一组 Promise 实例作为参数,包装成一个新的 Promise 实例返回。如果有一个成功就返回第一个,后续不返回,如果失败需要等到所有失败才会返回失败Promise
    • 接收一个Promise可迭代对象,只要其中的一个 Promise 成功,就返回那个已经成功的 Promise 。如果可迭代对象中没有一个 Promise 成功(即所有的 Promises 都失败/拒绝),就返回一个失败的 Promise

    • 抛出的错误是一个 AggregateError 实例 , 这个 AggregateError 实例对象的errors属性是一个数组,包含了所有成员的错误

  • Promise.resolve() 接受一个对象参数,返回一个以给定值解析后的 Promise 对象
    • 如果参数是 Promise 实例,将不做任何修改、原封不动地返回这个实例

    • 参数是一个thenable对象(即:对象指的是具有then方法的对象),将这个对象转为 Promise 对象,然后就立即执行 then()方法

    不要在解析为自身的 thenable 上调用Promise.resolve。这将导致无限递归,因为它试图展平无限嵌套的 promise

        let thenable = {
          then: (resolve, reject) => {
            resolve(thenable)
          }
        }
        Promise.resolve(thenable) //这会造成一个死循环
    
    • 如果参数是一个原始值,返回一个新的 Promise 对象,状态为resolved

    • 不带参数,直接返回一个resolved状态的 Promise 对象。

  • Promise.reject() 返回一个带有拒绝原因的 Promise 对象

Iterator 和 for...of

Iterator 迭代器

  • 它是一种接口,为各种不同的数据结构提供统一的访问机制
  • 使得数据结构的成员能够按某种次序排列
  • Iterator 接口主要供for...of操作
  • 迭代器对象可以通过重复调用显式迭代next()。迭代一个迭代器被称为消耗迭代器,因为它通常只能做一次。在产生终止值后,next()应继续返回额外的调用{done: true}

Iterator遍历过程

  • 创建一个指针对象,指向当前数据结构的起始位置。也就是说,迭代器对象本质上,就是一个指针对象。
  • 第一次调用指针对象的next方法,可以将指针指向数据结构的第一个成员。
  • 第二次调用指针对象的next方法,指针就指向数据结构的第二个成员。
  • 不断调用指针对象的next方法,直到它指向数据结构的结束位置。
let arr = ['a', 'b', 'c'];
let iter = arr[Symbol.iterator]();

iter.next() // { value: 'a', done: false }
iter.next() // { value: 'b', done: false }
iter.next() // { value: 'c', done: false }
iter.next() // { value: undefined, done: true }

具备 Iterator 接口的数据结构

  • Array
  • Map
  • Set
  • String
  • TypedArray
  • 函数的 arguments 对象
  • NodeList 对象

会默认调用 Iterator 接口的场合

  • 对数组和 Set 结构进行解构赋值时,会默认调用Symbol.iterator方法。
  • 扩展运算符
  • yield*后面跟的是一个可遍历的结构,它会调用该结构的遍历器接口
  • for...of
  • Array.from()
  • Map(), Set(), WeakMap(), WeakSet()
  • Promise.all()
  • Promise.race()

for...of

  • 一个数据结构只要部署了Symbol.iterator属性,就被视为具有 iterator 接口,就可以用for...of循环遍历它的成员
  • 它可以由 breakthrow 或 return 终止,迭代器关闭
  • 提供了遍历所有数据结构的统一操作接口。

Generator

  • 异步编程解决方案,执行 Generator 函数会返回一个迭代器对象

  • Generator 可以暂停函数执行,返回任意表达式的值

  • 对象的属性是 Generator 函数,可简写为 * myGeneratorMethod() {···}

  • function关键字与函数名之间有一个星号 (即:function* helloWorldGenerator(){}) 星号不验证位置。

  • 函数体内部使用yield表达式,定义不同的内部状态

  • 可以调用迭代器对象的next方法,使得指针移向下一个状态

  • yield表达式是暂停执行的标记,而next方法可以恢复执行

  • for...of可以自动遍历 Generator 函数运行时生成的Iterator对象,并且不需要调用next方法

  • 可以把Generator 赋值给对象的Symbol.iterator属性,从而使得该对象具有 Iterator 接口,并支持扩展运算符遍历

    myIterable[Symbol.iterator] = function* () {
      yield 1;
      yield 2;
      yield 3;
    };
    [...myIterable] // [1, 2, 3]

通常Generator函数用于解决异步任务,或者存放异步任务,可以通过yield,交出函数的执行权。

yield

  • yield表达式本身没有返回值,默认返回undefined

  • 迭代器对象的next方法的运行逻辑如下。

    • 遇到yield表达式,就暂停执行后面的操作,并将紧跟在yield后面的那个表达式的值,作为返回的对象的value属性值。

    • 下一次调用next方法时,再继续往下执行,直到遇到下一个yield表达式。

    • 如果没有再遇到新的yield表达式,就一直运行到函数结束,直到return语句为止,并将return语句后面的表达式的值,作为返回的对象的value属性值。

    • 如果该函数没有return语句,则返回的对象的value属性值为undefined

yield* 表达式

  • 一个 Generator 函数里面执行另一个 Generator 函数,可返回一个可迭代对象的表达式
    (即yield* foo();

  • 在有return语句时,需要用var value = yield* iterator的形式获取return语句的值

  • 任何数据结构只要有 Iterator 接口,就可以被yield*遍历,换种说话,yield*可以很方便地取出嵌Iterator数据下的所有成员

方便查看运行逻辑的 demo

next

  • next 传参会被当作上一个yield表达式的返回值。

实例方法

  • Generator.prototype.throw() 可以在函数体外抛出错误,然后在 Generator 函数体内捕获,返回带有 done 及 value 两个属性的对象
    即:g.throw(new Error("Something went wrong"))

    • 如果 Generator 函数内部没有部署try...catch代码块,那么throw方法抛出的错误,将被外部try...catch代码块捕获

    • 只要函数内部部署了try...catch代码块,那么迭代器的throw方法抛出的错误,不影响下一次遍历

  • Generator.prototype.return() 返回给定的值并结束迭代器 (即:g.return('foo')

    • 如果函数内部有try...finally代码块,且正在执行try代码块,那么return()方法会导致立刻进入finally代码块,执行完后,整个函数才会结束

async 函数

ES2017 引入了 async 函数,异步操作变得更加方便

  • async 函数 相当于 PromiseGenerator 组合成的语法糖

  • await 表达式会暂停整个 async 函数的执行进程并出让其控制权,只有当其等待的基于 promise 的异步操作被兑现或被拒绝之后才会恢复进程

  • await 返回值如果不是Promise 则会被隐式包装成一个Promise

  • await关键字只在 async 函数内有效

  • async函数会返回一个 Promise 对象,return命令返回的值,会被then方法回调函数接收到,async函数内部抛出错误,会导致返回的 Promise 对象变为reject状态

  • 同步执行异步任务,按顺序执行,并阻塞线程保证执行顺序。

  • 会阻塞线程

  • 遇到执行回调中第一个失败,报错如果不加try...catch会直接中断后续代码执行

  • 依次执行保证指定顺序调用异步函数

  • 简洁的使用语法糖
    详情参见 关于 Promise.all 和 async await 这档子事儿

例子

    function timeout(ms) {
      return new Promise((resolve) => {
        setTimeout(resolve, ms);
      });
    }
    async function asyncPrint(value, ms) {
      await timeout(ms);
      console.log(value);
    }
    asyncPrint('hello world', 50);

Class

  • class写法可以让对象原型的写法更加清晰、更像面向对象编程的语法
  • 类必须使用new调用,否则会报错
  • 类的属性和方法,除非显式定义在其本身(即定义在this对象上),否则都是定义在原型上
  • 类的属性名,可以采用表达式 (即:[methodName]() {}
  • Class表达式 可以定义一个只在类内部使用的类名指代当前类
    (即:const MyClass = class Me {}
  • 类不存在变量提升
  • 类和模块的内部,默认就是严格模式
  • ES2022,有一个提案,为class加了私有属性。方法是在属性名之前,使用#表示。
  • ES2022 类的实例属性,可以定义在类内部的最顶层
    (即:class IncreasingCounter { _count = 0; })
  • ES2022 改进了in运算符,使它可以用来判断是否有私有属性
  • ES2022 引入了静态块(static block),允许在类的内部设置一个代码块,在类生成时运行一次,主要作用是对静态属性进行初始化

constructor()

  • 是类的默认方法,创建对象实例时,自动调用该方法。一个类必须有constructor()方法,如果没有显式定义,一个空的constructor()方法会被默认添加
  • constructor()方法默认返回实例对象(即this),可以重新指定返回另外一个对象

getter 和 setter

  • class的内部可以使用getset关键字,对某个属性的存值和取值,进行自定义行为。
  • 使用Object.getOwnPropertyDescriptor() 可以获取到类,属性是否设置了getset

静态方法

  • 使用 static 声明静态方法 ,该方法不会被实例继承,而是直接通过类来调用
  • 静态方法包含this关键字,指代的是类,而非实例
  • 静态方法可以被继承,子类可以通过super调用父类静态方法

静态属性

  • class Foo {} Foo.prop = 1; 可通过此方式定义静态属性 或者使用提案提供了类的静态属性,写法是在实例属性的前面,加上static关键字
  • 其余规则与静态方法一致

new.target

  • 该属性一般用在构造函数之中,返回new命令作用于的那个构造函数。如果构造函数不是通过new命令或Reflect.construct()调用的,new.target会返回undefined
  • 在函数外部,使用new.target会报错

继承

  • Class 可以通过extends关键字实现继承,让子类继承父类的属性和方法
  • 子类必须在constructor()方法中调用super(),否则就会报错 (即:先将父类的属性和方法,加到一个空的对象上面,然后再将该对象作为子类的实例)
  • Object.getPrototypeOf()方法可以用来从子类上获取父类

super

  • 可以通过 super 调用父类的静态方法
  • 不能使用 delete 操作符 加 super.prop 或者 super[expr] 去删除父类的属性,这样做会抛出 ReferenceError
  • 当使用 Object.defineProperty 定义一个属性为不可写时,super将不能重写这个属性的值
  • 使用super的时候,必须显式指定是作为函数、还是作为对象使用,否则会报错
    (即:console.log(super)

Module

  • ES6 模块是编译时加载
  • ES6 的模块自动采用严格模式

严格模式限制如下:

  • 变量必须声明后再使用
  • 函数的参数不能有同名属性,否则报错
  • 不能使用with语句
  • 不能对只读属性赋值,否则报错
  • 不能使用前缀 0 表示八进制数,否则报错
  • 不能删除不可删除的属性,否则报错
  • 不能删除变量delete prop,会报错,只能删除属性delete global[prop]
  • eval不会在它的外层作用域引入变量
  • evalarguments不能被重新赋值
  • arguments不会自动反映函数参数的变化
  • 不能使用arguments.callee
  • 不能使用arguments.caller
  • 禁止this指向全局对象
  • 不能使用fn.callerfn.arguments获取函数调用的堆栈
  • 增加了保留字(比如protectedstaticinterface
  • export用于规定模块的对外接口,import用于输入其他模块提供的功能
  • exportimport 处于块级作用域内,就会报错

export

  • 于从模块中导出实时绑定的函数、对象或原始值,以便其他程序可以通过 import 语句使用它们
  • 导出单个变量
export let name1, name2, …, nameN;
  • 导出对象
export { name1, name2, …, nameN }; 
  • 重命名导出
export { variable1 as name1, variable2 as name2, …, nameN };
  • 默认导出
export default expression;
  • 导出模块合集
export * from …;

import

  • 用于导入由另一个模块导出的绑定
  • import命令会被 `JavaScript· 引擎静态分析,先于模块内的其他语句执行
  • 导入整个模块
import * as myModule from '/modules/my-module.js';
  • 导入多个变量
import {foo, bar} from '/modules/my-module.js';
  • 导入带别名的变量
import {reallyReallyLongModuleExportName as shortName}
  from '/modules/my-module.js';
  • 导入默认值
import myDefault from '/modules/my-module.js';

import()

  • ES2020提案 引入import()函数,支持动态加载模块。
  • import()函数可以用在任何地方,非模块的脚本也可以使用。它是运行时执行
  • import()返回 Promise 对象
  • import()允许模块路径动态生成
import('/modules/my-module.js')
  .then((module) => {});

写在最后

量变产生质变!共勉!