void 运算符在 ES6 的运用

js Jan 19, 2020

void 操作符在过去有以下几个使用用例:

  • <a href="javascript:void(0)"> 一起使用来实现动态按钮, 但这是一种糟糕且过时的做法
  • 在 ES5 之前的年代标识符 undefined 随时可能被覆盖的, 它可以用来访问 undefined 原始值
  • undefined 缩写为 void 0 可以省去一些字符, 即使是现在这种缩写依然有人在使用

这些情况都都是在 ES3 中常用, 与现在常用的 ES6 没有特别的关系, 除了在 ES6 的箭头函数中带来了一些新的功能:

使用 void 解释没有返回值的函数

ES6 得箭头函数表达式为了代码更简洁允许省略函数体中的花括号。但这种简写会产生一种模糊性, 导致我们无法判断某个函数是否有返回值, 因为这些函数也有可能只是起到其它作用(如, console.log 只是用来打印 log, 调用后不会有返回值), 而它又没有返回值:

// 省略花括号
const id = x => x;

// 调用没有返回值的函数
const log = x => console.log(x);

console.log 是大家熟知的, 因此这个示例并不是模棱两可, 但是它说明了原理。

当然, 我们也可以有意地通过添加花括号来使箭头函数地函数性质更明确, 但这就失去了箭头函数该有地简洁:

const log = x => {
  console.log(x);
}

上面的函数这时候就可以借助 void 显示在一行, 相比像 Prettier 这类自动化格式工具通过添加换行符来显示函数没有返回值, 这种方式更显得明显(如果你有过其它语言学习的经验地话)也简洁:

const log = x => void console.log(x);

像这样使用 void 的方式最明显的好处是减少了行数。尤其是像经常依赖 void 回调函数的 CPS 函数式编程 (continuation-passing style)。

类型系统 and void 类型

在许多语言中的类型系统,包括 C/C++, TypeScript 的类型系统, 都具有 void 类型概念, 这使 void 运算符可用于更好地补充和认识那些没有返回值的函数。

React 中的 useEffect hook

void 的常见实用案例其实是在 React 中的 useEffect hook, 例如:

useEffect(() => void (document.title = 'example'));

这是一个很明显的示例,因为 useEffect 可以使用其回调函数的返回值来移除效果,因此如果回调的函数返回的是函数就可能在运行时出现 bug。

同样的情况是 Immerproduce() 函数:

produce(draft => void (draft.user.age += 1))

在 async IIFES 中使用 void

对于异步立即调用函数表达式(async immediately-invoked function expressions, 简写 async IIFES)的 void 运算符, 还有另外一个不太常见的用例。以往, IIFES 被用于实现模块作用域, 因为古旧的 JS (ES3)没有块作用域或实际模块, 但是现在的 JS (像ES5, ES6 之后的标准)却同时具有两者, 所以 IIFES 除了以下一种情况之外基本上不相关: 在模块的顶层上使用 theawaitfor-await-of 语法, 例如:

void (async () => {
  await fetch(something);
})();

这是个常见的用例, 而 ECMAScript 的 stage 3 proposal 就有相关探讨。现阶段我们仍需在函数表达式周围加上括号, 但是使用 void 解决了前括号不能与 ASI 一起使用的问题(更具体地说, 它在没有显式分号行终止符的情况下会中断)。

代码风格和 no-void

流行的代码风格指南(如 AirbnbStandard JS)使用 no-void ESLint 规则来禁止编程人员使用 void 运算符, 然而, 这种规定充满了争议, 它应该运行用户去重写或覆盖。



相关资料链接:

ECMAScript proposal: top-level await
Immer doc: Inline shortcuts using void
ESLint issue#'no-void' should have an option to allow void arrow functions or void async IIFEs

Great! You've successfully subscribed.
Great! Next, complete checkout for full access.
Welcome back! You've successfully signed in.
Success! Your account is fully activated, you now have access to all content.