平滑 JavaScript

2025-06-07

平滑 JavaScript

封面图片由 David 在 Flickr 上发布,由我裁剪。

作为一名 JavaScript 开发者,你可能听说过 smoosh-gate。一些TC39成员(负责设计定义 JavaScript 语言行为的 ECMAScript 标准)提出了将重命名 flattensmooshflatMap的想法smooshMap。这样做的原因是,如果不这样做,一些使用 MooTools 的网站可能会崩溃。这些方法目前还处于提案阶段,并非标准的一部分。

无论如何,我对 MooTools 或标准了解不多,但我想解释一下这些功能实际上起什么作用。

为什么

在编写 JavaScript 代码时,你可能经常会用到你最喜欢的函子、数组和 Promise。有时它们是嵌套的,但你并不在意。你需要一个数字数组,而不是一个数字数组的数组;或者你需要从服务器获取 JSON 结果,而不是一个包含 JSON 结果的 Promise。

什么

承诺已经带有一种then方法,可以展平嵌套的承诺。

// loadDataA returns a promise
const dataAPromise = loadDataA();

// dataAPromise resolves to a result with an id that is used to load the next data
// loadDataB returns a promise too
const dataBPromise = dataAPromise.then(result => loadDataB(result.id))

// dataBPromise resolves to the resulting data
// and not the the promise returned from the callback above.
// It's flattened out right away so you only care about the result
const upperCasePromise = dataBPromise.then(result => result.someText.toUpperCase())

// upperCasePromise resolves to the upper case text
// returned by the previous callback.
upperCasePromise.then(upperCaseText => console.log(upperCaseText));
Enter fullscreen mode Exit fullscreen mode

所以这里没什么可做的。一些 Promise 库,比如Bluebird,自带了单独的mapflatMap方法,但你主要会用到它们then,所以不用太在意这里的扁平化。

解决数组中这个问题的方法是向数组添加flattenand方法。该方法会用嵌套元素的内容替换数组中的每个嵌套元素,并删除元素。flatMapflatten

该函数可以借助手动编写reduce

const flatten = a => a.reduce(
  (newArray, element) =>
    element instanceof Array
      ? [...newArray, ...element]
      : element !== undefined? [...newArray, element] : newArray,
  []
);

const a = [1, [2, 3, 4], , 5];
flatten(a); // -> [1, 2, 3, 4, 5]
Enter fullscreen mode Exit fullscreen mode

我们通过将每个元素添加到其中来简化a为一个,如果这个数组是一个,我们将其中的每个元素添加到中。(对于任何一种情况,-operator 都会创建一个新数组,而不是添加到现有数组中,但我想你明白我的意思)。newArrayelementistanceof ArrayelementnewArray...

命令式版本可能如下所示:

function flatten(a) {
  let b = [];
  for (let element of a) {
    if (element instanceof Array) {
      for (let subElement of element) {
        b.push(subElement);
      }
    } else if (element !== undefined) {
      b.push(element);
    }
  }
  return b;
}

const a = [1, [2, 3, 4], , 5];
flatten(a); // -> [1, 2, 3, 4, 5]
Enter fullscreen mode Exit fullscreen mode

此版本flatMap只是调用flatten由 a 发出的新数组map

const flatMap = (f, a) => flatten(a.map(f));

const a = [1,0,7,-3];
flatMap(x => x != 0? 1/x : undefined, a);
// -> [1, 0.14285714285714285, -0.3333333333333333]

const c = [1,2,5];
flatMap(x => ''.padEnd(x, ".").split(""), c);
// -> [".", ".", ".", ".", ".", ".", ".", "."]
Enter fullscreen mode Exit fullscreen mode

真正的实现可能像方法而不是函数一样工作:

// Instead of 
flatten(a.map(x => [1,x]));

// it would be
a.map(x => [1,x]).flatten();

// instead of
flatMap(x => [1,x], a);

// it would be
a.flatMap(x => [1,x]);
Enter fullscreen mode Exit fullscreen mode

结论

扁平化是每个程序中多次执行的一个相当重要的操作,所以如果 JavaScript 附带一个内置版本,与其最终名称无关,那就太好了,哈哈。

文章来源:https://dev.to/kayis/smooshing-javascript--5dpc
PREV
单元测试的替代方案有哪些?
NEXT
最大 JavaScript 性能