【笔记】ES6随手记(持续)
Sep 24, 2023笔记jses6ES6 随手记(持续)
(包含 ES7 及以上,2023 年最新为 ES14)
- update date: 2023-09-24 10:12:30
- start date: 2018-08-27 13:00:45
ES7 ~ ES13 说明
ECMA-262
Ecma 国际 (一个标准化组织)创建了 ECMA-262 规范,这个规范就是 ECMAScript 语言的官方标准。
Ecma 第 39 号技术委员会 (TC39)
是一组开发 ECMA-262 标准规范的人(Brendan Eich 和其他一些人)。
ECMA 规范最终由 TC39 敲定。TC39 由包括浏览器厂商在内的各方组成,他们开会推动 JavaScript 提案沿着一条严格的发展道路前进。
从提案到入选 ECMA 规范主要有以下几个阶段:
Stage 0
: strawman——最初想法的提交。Stage 1
: proposal(提案)——由 TC39 至少一名成员倡导的正式提案文件,该文件包括 API 事例。Stage 2
: draft(草案)——功能规范的初始版本,该版本包含功能规范的两个实验实现。Stage 3
: candidate(候选)——提案规范通过审查并从厂商那里收集反馈Stage 4
: finished(完成)——提案准备加入 ECMAScript,但是到浏览器或者 Nodejs 中可能需要更长的时间。
1 对象设置变量键值
难以形容,直接上代码:
1 | const key = 'testkey'; |
babel 编译(ES5):
1 | var _obj; |
应用场景
对象的 key 设置有限制且希望更加语义化、或 key 是动态的,比如一些接口参数设置的场景。
2 String、Array 匹配查找
String.prototype.includes()
、Array.prototype.includes()
方法。mdn
注:babel 不能编译,移动端兼容:ios9 及以上、安卓 5 及以上;PC:除 IE 外。
1 | const str = 'abcdef'; |
语法
1 | arr.includes(searchElement [, fromIndex]) |
其中:
searchElement
:需要查找的元素值。fromIndex
(可选):从该索引处开始查找 searchElement。如果为负值,则按升序从 array.length + fromIndex 的索引开始搜索。默认为 0。
应用场景
绝大部分的字符串/数组匹配判断场景,比 indexOf()加判断方便。
3 使用对象解构来解析字符串数组
例:
1 | const message = '1994,Micheal Wayne,Chine,michealwayne@163.com'; |
babel 编译:
1 | var message = '1994,Micheal Wayne,Chine,michealwayne@163.com'; |
4 还是那道题,变量 a、b 交换
1 | let a = 1, |
ps,上面代码 babel 结果:
1 | var a = 1, |
5 数组 concat()
的另一种解决方式
1 | let arr1 = [1, 2, 3], |
ps,上面代码 babel 结果:
1 | var arr1 = [1, 2, 3], |
6 ES6 模块加载
(CommonJS 和 ES6 module 的区别有很多,在此只记录基本的使用对应关系。对于它们的区别可参考《CommonJS 和 ES6 Module 究竟有什么区别?》)
1 | // a.js (CommonJS) |
引用
1 | // 方法1 |
7 利用...
运算符合并两个 Object
如
1 | const obj1 = { |
babel:
1 | // 实现拼接 |
应用场景
对象合并场景,比如默认值和设置值的合并
8 利用...
运算符展开字符串
如
1 | const str = 'abcdefg'; |
*这种方式还能避免 js 将 32 位 Unicode 识别为两个字符。如
1 | let str = '\uD83D\uDE80y'; |
9 利用...
运算符求极值
如
1 | let nums = [1, 3, 5, 7, 9, 11, 13] |
10 JavaScript 中默认为(或提供)iterable
的标准内建值包括:
- Arrays
- Strings
- Generators
- Collections / TypedArrays
11 被忽视的 Symbol
Symbol 作为 ES6 新出的一种新的基本类型,我显然忽视了它。
每个从 Symbol()
返回的 symbol 值都是唯一的。一个 symbol 值能作为对象属性的标识符;这是该数据类型仅有的目的
语法:
1 | Symbol([description]) |
其中 description 可选,是 symbol 的描述,可用于调试但不能访问 symbol 本身。
不支持语法:”
new Symbol()
“。围绕原始数据类型创建一个显式包装器对象从 ECMAScript 6 开始不再被支持。 然而,现有的原始包装器对象,如 new Boolean、new String 以及 new Number 因为遗留原因仍可被创建。
属性
length
:Symbol.length
为 0;prototype
:描述 symbol 构造函数的原型。
除了自己创建的 symbol,JavaScript 还内建了一些在 ECMAScript 5 之前没有暴露给开发者的 symbol,它们代表了内部语言行为。它们可以使用以下属性访问:
Symbol.iterator
:一个返回一个对象默认迭代器的方法。被for...of
使用。
1 | const str = 'abc'; |
Symbol.asyncIterator
:一个返回对象默认的异步迭代器的方法。被for await of
使用。- 正则表达式 symbols:
Symbol.match
、Symbol.replace
、Symbol.search
、Symbol.split
Symbol.hasInstance
:一个确定一个构造器对象识别的对象是否为它的实例的方法。被 instanceof 使用。
1 | class MyArray { |
Symbol.isConcatSpreadable
:一个布尔值,表明一个对象是否应该 flattened 为它的数组元素。被Array.prototype.concat()
使用。
1 | var alpha = ['a', 'b', 'c'], |
Symbol.unscopables
:拥有和继承属性名的一个对象的值被排除在与环境绑定的相关对象外。Symbol.species
:一个用于创建派生对象的构造器函数。
1 | class MyArray extends Array { |
Symbol.toPrimitive
:一个将对象转化为基本数据类型的方法。
1 | // 一个没有提供 Symbol.toPrimitive 属性的对象,参与运算时的输出结果 |
Symbol.toStringTag
:用于对象的默认描述的字符串值。被Object.prototype.toString()
使用。
1 | class ValidatorClass { |
方法
Symbol.for(key)
:使用给定的 key 搜索现有的 symbol,如果找到则返回该 symbol。否则将使用给定的 key 在全局 symbol 注册表中创建一个新的 symbol。
1 | Symbol.for('foo'); // 创建一个 symbol 并放入 symbol 注册表中,键为 "foo" |
Symbol.keyFor(sym)
:从全局 symbol 注册表中,为给定的 symbol 检索一个共享的?symbol key。
1 | // 创建一个 symbol 并放入 Symbol 注册表,key 为 "foo" |
唯一性
1 | let a1 = Symbol('a'), |
什么情况下可以让两个 symbol 变量“相等”:Symbol.for
:
1 | const a = Symbol.for('aa'); |
因为 Symbol.for()
其实是带有类似重用机制的,具体的说,就是通过 Symbol.for()
创建变量时,传入的参数 ( 假设为 x ) 会作为 Symbol 变量的 key ,然后到全局中搜索,是否已经有相同 key 的 Symbol 变量,如果存在,则直接返回这个 Symbol 变量。如果没有,才会创建一个 key 为传入参数 x 的 Symbol 变量 ,并将这个变量写到全局,供下次创建时被搜索。
通过 Symbol.for()
创建的 Symbol 变量,传入的参数是否相等决定得到的 Symbol 变量是否相等。
既然通过 Symbol.for()
创建的 Symbol 变量的 key 这么重要,那我们怎么获取到这个 key 呢,那就要 Symbol.keyFor()
方法了,该函数会返回一个已经写到全局的 Symbol 变量的 key 值。这样获取到 Symbol 变量的 key, 就可以创建一个和原 Symbol 变量相等的变量了。
属性
Symbol 属性值如下图:
更多可见:mdn Symbol
12 WeakSet/WeakMap
WeakSet
与 Set
类似,也是不重复的值的集合。它与 Set
有两个区别:
- 第一,
WeakSet
的成员只能是对象,而不能是其他类型的值。 - 第二,
WeakSet
中的对象都是弱引用,即垃圾回收机制不考虑WeakSet
对该对象的引用。即如果其他对象都不再引用该对象,那么垃圾回收机制会自动回收该对象所占用的内存,不考虑该对象是否还存在于WeakSet
之中。
原因:垃圾回收机制依赖引用计数,如果一个值的引用次数不为 0,垃圾回收机制就不会释放这块内存。结束使用改值之后,有时会忘记取消引用,导致内存无法释放,进而可能会引发内存泄露。而 WeakSet
里面的引用都不计入垃圾回收机制。
WeakSet
适合临时存放一组对象,以及存放跟对象绑定的信息。只要这些对象在外部消失,它在 WeakSet
里面的引用就会自动消失。由此可知,WeakSet
的成员是不适合引用的,因为它会随时消失。另外,WeakSet
内部有多少成员取决于垃圾回收机制有没有运行,运行前后很可能成员个数是不一样的,也因为垃圾运行机制何时运行是不可预测的,因此 ES6 规定 WeakSet
不可遍历。
同样的特点也适用于 WeakMap
,在此不过多介绍。
应用场景:DOM、部署私有属性。更多可见:https://exploringjs.com/es6/ch_maps-sets.html#_use-cases-for-weakmaps
13 对象类型
JavaScript 中,对象可根据特征分为:
- 宿主对象(host Objects):由 JavaScript 宿主环境提供的对象,它们的行为完全由宿主环境决定。比如 document 对象,Image()构造函数。
- 内置对象(Built-in Objects):由 JavaScript 语言提供的对象。
- 固有对象(Intrinsic Objects ):由标准规定,随着 JavaScript 运行时创建而自动创建的对象实例。
- 原生对象(Native Objects):可以由用户通过 Array、RegExp 等内置构造器或者特殊语法创建的对象。
- 普通对象(Ordinary Objects):由
{}
语法、Object 构造器或者 class 关键字定义类创建的对象,它能够被原型继承。
其中原生对象可见下表:
基本类型 | 基础功能和数据结构 | 错误类型 | 二进制操作 | 带类型的数组 |
---|---|---|---|---|
Boolean | Array | Error | ArrayBuffer | Float32Array |
String | Date | EvalError | SharedArrayBuffer | Float64Array |
Number | RegExp | RangeError | DataView | Int8Array |
Symbol | Promise | ReferenceError | Int16Array | |
Object | Proxy | SyntaxError | Int32Array | |
Map | TypeError | UInt8Array | ||
WeakMap | URIError | UInt16Array | ||
Set | UInt32Array | |||
WeakSet | UInt8ClampedArray | |||
Function |
14 函数与 new
函数类型 | new |
---|---|
普通函数 | 新对象 |
箭头函数 | 报错 |
方法 | 报错 |
生成器 | 报错 |
类 | 新对象 |
异步普通函数 | 报错 |
异步箭头函数 | 报错 |
异步生成器函数 | 报错 |
15 Array.from()
语法:
1 | Array.from(arrayLike[, mapFn[, thisArg]]) |
其中参数:
arrayLike
:想要转换成数组的伪数组对象或可迭代对象。mapFn
:(可选)如果指定了该参数,新数组中的每个元素会执行该回调函数。thisArg
:(可选)可选参数,执行回调函数 mapFn 时 this 对象。
返回:
- 一个新的数组实例。
1 | Array.from({ 0: 0, 1: 1, length: 2 }); // [0, 1] |
将类数组转换成数组
如 arguments,DOM 集合。
1 | function arguments2Arr() { |
克隆数组
如
1 | let arr1 = [1, 2, 3]; |
利用递归深拷贝数组:
1 | function deepCloneArr(val) { |
初始化填充数组
1 | let arr1 = Array.from({ length: 3 }, () => 1); // [1, 1, 1] |
如果填充内容为引用类型的时候,则表现不一样,如
1 | let arr1 = Array.from({ length: 3 }, () => ({})); |
当然我们还可以利用 mapFn 第二个参数索引来做一些有意义的事,如
1 | Array.from({ length: 5 }, (_, index) => index); // [0, 1, 2, 3, 4] |
16 String.prototype.matchAll(regexp)
给定一个字符串和正则表达式,该方法返回所有与该字符串匹配正则表达式的结果的迭代器,包括捕获 groups。
如:
1 | let reg = /t(e)(st(\d?))/g; |
17 Nullish value
一个 nullish 值要么是 null
要么是 undefined
。nullish 值总是 falsy。
Nullish 的出现能减少存取值时的判断操作。
可选链式调用
可选链操作符( ?.
)允许读取位于连接对象链深处的属性的值,而不必明确验证链中的每个引用是否有效。?.
操作符的功能类似于 .
链式操作符,不同之处在于,在引用为空(nullish) (null
或者 undefined
) 的情况下不会引起错误,该表达式短路返回值是 undefined
。
1 | // old |
空值合并运算符??
空值合并操作符(??
)是一个逻辑操作符,当左侧的操作数为 Nullish value(null
或者 undefined
) 时,返回其右侧操作数,否则返回左侧操作数。
1 | // old |
空值合并操作符经常用于数字金额的判断。
逻辑空赋值??=
逻辑空赋值运算符 (x ??= y
) 仅在 x 是 Nullish value (null
或 undefined
) 时对其赋值。
1 | // old |
安全赋值运算符?=
目前处于stage1草案阶段,具体提案:https://github.com/arthurfiorette/proposal-safe-assignment-operator
使用 ?=
运算符,你可以将函数结果转换为一个元组,更优雅地处理错误。如果出现错误,你将得到 [error, null]
,如果一切正常,你将得到 [null, result]
18 箭头函数可能不知道的地方
name 属性
匿名函数和空箭头函数的 name 属性为""
,如:
1 | (function () {}).name; // '' |
ES2015 起增加了函数名推断,可以在某些条件下检测函数名称。如
1 | const func1 = () => 123; |
在内联箭头函数中使用对象字面量可能会触发语法错误
js 认为花括号是代码块而不是对象。
如:
1 | [1,2,3].map(num => { 'number': num }) |
这种情况下需要增加括号:
1 | [1, 2, 3].map(num => ({ number: num })); |
19 Object.is()
Object.is()
方法判断两个值是否为“同一个值”,返回一个 Boolean。同一个值:
- 都是
undefined
- 都是
null
- 都是
true
或false
- 都是相同长度的字符串且相同字符按相同顺序排列
- 都是相同对象(意味着每个对象有同一个引用)
- 都是数字且
- 都是
+0
- 都是
-0
- 都是
NaN
- 或都是非零而且非
NaN
且为同一个值
- 都是
它与==
以及===
均不相同。==
没什么好说,与===
的主要区别在于:
===
运算符 (也包括==
运算符) 将数字-0
和+0
视为相等 ,而将Number.NaN
与NaN
视为不相等。
polyfill
1 | if (!Object.is) { |
20 (ES13)类静态属性
默认情况下类的所有属性都是公共的,也就是说我们可以直接通过实例对属性进行更改。ES13 中我们可以使用 #
前缀去定义私有属性,私有属性只能在类方法中被更改。如:
1 | class Test { |
Ts 编译后产物:
1 | ; |
可以看出主要通过一个局部变量_Test_num
的 WeakMap 来实现私有属性的效果。
21 (ES12)数字分隔符
ES2021(ES12)中允许 JavaScript 的数值使用下划线(_
)作为分隔符。
1 | let budget = 1_000_000_000_000; |
但数值分隔符有几个使用注意点。
- 不能放在数值的最前面(leading)或最后面(trailing)。
- 不能两个或两个以上的分隔符连在一起。
- 小数点的前后不能有分隔符。
- 科学计数法里面,表示指数的 e 或 E 前后不能有分隔符。
因此以下声明会报错:
1 | // 全部报错 |
Author
My name is Micheal Wayne and this is my blog.
I am a front-end software engineer.
Contact: michealwayne@163.com