作者 | 前端工匠
ECMAScript标准的产生过程包括从Stage 0到Stage 4的五个阶段,每个阶段提交到下一阶段都需要TC39的批准。 本文介绍这个新功能处于 Stage 3 或 Stage 4 阶段,这意味着它应该很快就会在浏览器和其他引擎中得到支持。
1.类的私有变量
最新的提议之一是在类中添加私有变量的技巧。 我们将使用#符号来表示类的私有变量。 这消除了使用闭包来隐藏您不想向外界公开的私有变量的需要。
class Counter {
#x = 0;
#increment() {
this.#x++;
}
onClick() {
this.#increment();
}
}
const c = new Counter();
c.onClick(); // 正常
c.#increment(); // 报错
用#修饰的成员变量或成员函数就成为私有变量,如果试图在类之外访问它,就会抛出异常。 此功能现已在最新版本的 Chrome 和 Node.js 中提供。
2. 可选的链接运算符
你可能遇到过这样的情况:当你需要访问对象内部嵌套了几层的属性时javascript引用类型,你会得到臭名昭著的错误 Cannot read property 'stop' of undefinedjavascript引用类型,然后你必须改变你的代码来处理每一个可能的 undefined属性链中的对象,例如:
let nestedProp = obj && obj.first && obj.first.second;
在访问 obj.first.second 之前,必须验证 obj 和 obj.first 的值是否为非 null(且不是未定义)。 目的是为了避免错误,如果不校准obj和obj.first,直接访问obj.first.second,可能会导致错误。
使用可选链接,您可以通过简单地编写以下内容来完成相同的操作:
let nestedProp = obj?.first?.second;
如果 obj 或 obj.first 为空/未定义,则表达式将直接返回未定义。
3. 间隙合并算子
在开发过程中,我们经常遇到这样的场景:如果变量为空,就会使用默认值。 我们是这样实现的:
let c = a ? a : b // 方式 1
let c = a || b // 方式 2
这两种方法有一个显着的缺点,它将覆盖所有错误值,例如 (0, '', false),这些值在个别情况下可能是有效输入。
为了解决这个问题,有人建议创建一个“nullish”合并运算符,用??表示。 有了它,我们仅在第一项为 null 或未定义时设置默认值。
let c = a ?? b;
// 等价于 let c = a !== undefined && a !== null ? a : b;
例如使用以下代码:
const x = null;
const y = x ?? 500;
console.log(y); // 500
const n = 0
const m = n ?? 9000;
console.log(m) // 0
4. 大整数
JS 在数学方面仍然很糟糕的原因之一是它无法精确表示小于 2^53 的数字,这使得处理相当大的数字变得困难。
1234567890123456789 * 123;
// -> 151851850485185200000 // 计算结果丢失精度
幸运的是,BigInt(大整数)就是来解决这个问题的。 您可以在 BigInt 上使用与普通数字相同的运算符,例如 +、-、/、*、% 等。
创建BigInt类型的值也非常简单,只需在数字前面添加n即可。 例如,123 变为 123n。 也可以使用全局方法BigInt(value)进行转换,输入参数value是一个数字或者一串数字。
const aNumber = 111;
const aBigInt = BigInt(aNumber);
aBigInt === 111n // true
typeof aBigInt === 'bigint' // true
typeof 111 // "number"
typeof 111n // "bigint"
只需在数字末尾添加 n 即可正确估计大数:
1234567890123456789n * 123n;
// -> 151851850485185185047n
但有一个问题,在大多数操作中,您不能将 BigInt 与 Number 混合使用。 可以比较 Number 和 BigInt,但不能将它们相减。
1n < 2
// true
1n + 2
// Uncaught TypeError: Cannot mix BigInt and other types, use explicit conversions
此功能现已在最新版本的 Chrome 和 Node.js 中提供。
五、静态场
它允许类拥有静态数组,类似于大多数 OOP 语言。 静态数组可以用来代替枚举,也可以用于私有数组。
class Colors {
// public static 字段
static red = '#ff0000';
static green = '#00ff00';
// private static 字段
static #secretColor = '#f0f0f0';
}
font.color = Colors.red;
font.color = Colors.#secretColor; // 出错
此功能现已在最新版本的 Chrome 和 Node.js 中提供。
6. 顶级等待
ES2017 (ES8) 中的 async/await 功能仅允许在异步函数中使用 wait 关键字。 新提案致力于允许在顶级内容中使用await关键字,例如,以简化动态模块加载的过程:
const strings = await import(`/i18n/${navigator.language}`);
此功能对于调试浏览器控件台北中的异步内容(例如 fetch)非常有用,而无需将其包装在异步函数中。
另一个使用场景是它可以用在以异步方法初始化的 ES 模块之上(例如构建数据库连接)。 当导出这样的“异步模块”时,模块系统将等待它被解析,然后再执行依赖于它的模块。 这使得处理异步初始化方法比当前返回初始化承诺并等待其解析更容易。 模块不知道其依赖项是否是异步的。
// db.mjs
export const connection = await createConnection();
// server.mjs
import { connection } from './db.mjs';
server.start();
在此示例中,在 server.mjs db.mjs 中完成连接之前不会发生任何事情。
该功能现已在最新版本的 Chrome 中提供。
七、弱引用
一般来说,在JavaScript中,对象引用是强保留的,这意味着只要持有对象的引用,它就不会被垃圾收集。
const ref = { x: 42, y: 51 };
// 只要我们访问 ref 对象(或者任何其他引用指向该对象),这个对象就不会被垃圾回收
目前在 Javascript 中,WeakMap 和 WeakSet 是弱引用对象的唯一方法:将对象添加为 WeakMap 或 WeakSet 的键并不会阻止它被垃圾收集。
const wm = new WeakMap();
{
const ref = {};
const metaData = 'foo';
wm.set(ref, metaData);
wm.get(ref);
// 返回 metaData
}
// 在这个块范围内,我们已经没有对 ref 对象的引用。
// 因此,虽然它是 wm 中的键,我们仍然可以访问,但是它能够被垃圾回收。
const ws = new WeakSet();
ws.add(ref);
ws.has(ref);// 返回 true
JavaScript 的 WeakMap 并不是真正的弱引用:只要键还活着,它实际上就持有对其内容的强引用。 WeakMap 仅在键被垃圾回收后才弱引用其内容。
WeakRef 是一个更中级的 API,提供真正的弱引用。 Weakref 实例有一个 deref 方法,该方法返回被引用的原始对象,如果原始对象已被收集,则返回 undefined 。
const cache = new Map();
const setValue = (key, obj) => {
cache.set(key, new WeakRef(obj));
};
const getValue = (key) => {
const ref = cache.get(key);
if (ref) {
return ref.deref();
}
};
// this will look for the value in the cache
// and recalculate if it's missing
const fibonacciCached = (number) => {
const cached = getValue(number);
if (cached) return cached;
const sum = calculateFibonacci(number);
setValue(number, sum);
return sum;
};
总而言之,JavaScript 中的对象引用是强引用。 WeakMap和WeakSet可以提供部分弱引用功能。 如果想在JavaScript中实现真正的弱引用,可以使用WeakRef和终结器(Finalizer)一起来实现。
此功能现已在最新版本的 Chrome 和 Node.js 中提供。
参考文章