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

huiyifyj

你记得也好, 最好你忘掉...