SmooshGate 常见问题解答

Mathias Bynens
Mathias Bynens

发生了什么流畅?!

关于 JavaScript 的提案 名为Array.prototype.flatten的语言功能 与网站不兼容。在 Firefox 夜间模式下推出此功能会导致至少一个热门网站 。鉴于有问题的代码是广泛使用的 MooTools 的一部分 那么就会有更多网站受到影响。虽然 MooTools 在 2018 年并不常用于新网站,以前非常受欢迎, 仍然存在于许多制作网站上。)

提案作者开玩笑地建议将 flatten 重命名为 smoosh, 避免兼容性问题这个笑话对所有人来说并不清楚,有些人 人开始错误地认为新名称 但事情很快就升级了。

Array.prototype.flatten的作用是什么?

Array.prototype.flat,原提议为 Array.prototype.flatten, 以递归方式展平数组,直至达到指定的 depth(默认为 发送至 1

// Flatten one level:
const array = [1, [2, [3]]];
array.flat();
// → [1, 2, [3]]

// Flatten recursively until the array contains no more nested arrays:
array.flat(Infinity);
// → [1, 2, 3]

同一提案包含 Array.prototype.flatMap,类似于 Array.prototype.map,不过它会将结果展平为新数组。

[2, 3, 4].flatMap((x) => [x, x * 2]);
// → [2, 4, 3, 6, 4, 8]

MooTools 执行什么操作导致了此问题?

MooTools 定义了自己的非标准 Array.prototype.flatten 版本:

Array.prototype.flatten = /* non-standard implementation */;

MooTools 的 flatten 实现与建议的标准不同。 不过,这并不是问题!浏览器推出 Array.prototype.flatten,则 MooTools 会覆盖原生 实施。这可以确保依赖于 MooTools 行为的代码 无论原生 flatten 是否可用,它都会按预期运行。 到目前为止,一切进展顺利!

遗憾的是,之后发生了其他事情。MooTools 会复制其 Elements.prototype 的自定义数组方法(其中 Elements 是 MooTools 专用 API):

for (var key in Array.prototype) {
  Elements.prototype[key] = Array.prototype[key];
}

for-in 会迭代“可枚举”属性,但不包括 原生方法(例如 Array.prototype.sort),但它包含 定期分配的属性,例如 Array.prototype.foo = whatever。但是 - 这里要做的就是覆盖不可枚举的属性,例如 Array.prototype.sort = whatever,则它仍然不可枚举。

目前,Array.prototype.flatten = mooToolsFlattenImplementation会创建 一个可枚举的 flatten 属性,因此稍后会复制到 Elements。但如果 浏览器提供原生版本的 flatten,该版本将变为不可枚举,并且 不会复制到 Elements任何依赖于 MooTools 的代码 Elements.prototype.flatten 现已损坏。

虽然看起来似乎将原生 Array.prototype.flatten 更改为 但枚举可解决此问题,但可能会导致 兼容性问题。每个依赖于 for-in 进行迭代的网站 数组(这是糟糕的做法,但确实会发生)会突然 flatten 属性的额外循环迭代。

这里更严重的底层问题是修改内置对象。正在延长 如今,构建原生原型通常已被视为一种不良做法, 无法与其他库和第三方代码完美地组合起来。不修改 不归您所有的对象!

我们为什么不保留现有的名称,破坏网络呢?

1996 年,在 CSS 普及之前,“HTML5”成为 后来,《Space Jam》网站上线了时至今日,该网站仍可正常使用 就像 22 年前一样

这是怎么做到的?这些年来,是否有人维护该网站? 是否在每次浏览器供应商推出新功能时都进行更新?

事实证明,“不破坏网络”是首要的设计原则 适用于 HTML、CSS、JavaScript 以及 网络。如果推出新的浏览器功能会导致现有网站停止运行, 但对所有人来说都很糟糕:

  • 受影响网站的访问者突然中断用户体验;
  • 网站所有者从拥有完美无缺的网站 非功能性模型,不做任何变化;
  • 由于用户数量不足,推出新功能的浏览器供应商失去了市场份额 在看到“它能在浏览器 X 中正常运行”之后切换浏览器;
  • 一旦发现兼容性问题,其他浏览器供应商就会拒绝在产品 。功能规范与现实不符(“只有虚构作品”), 这对标准化过程不利。

当然,回想起来,MooTools 做错了事 — 但彻底改变了网络 而不是用户,而是用户。这些用户不知道什么是“moo” 工具的功能。或者,我们可以寻找其他解决方案,用户可以继续 使用网络。选择过程很简单。

这是否意味着绝不会从网络平台中移除不良 API?

这要视具体情况而定。在极少数情况下,不良功能可以从 Web 上移除。即使是 看看能否移除某个特征并非易事, 需要大量遥测数据来量化有多少网页具有 行为发生了变化。但是,当该功能足够不安全时,会损害 也可以做到这一点

<applet><keygen>showModalDialog()全部为 成功从网络平台移除的不良 API 示例。

我们为什么不直接修复 MooTools?

修补 MooTools 以使其不再扩展内置对象是很好的做法 想法。不过,这种方法并不能解决目前的问题。即使 MooTools 要发布修补版本,所有使用它的现有网站都必须 更新,兼容性问题就会消失。

人们不能直接更新自己的 MooTools 副本吗?

在理想情况下,MooTools 会发布一个补丁,每个网站 使用 MooTools 的团队就会在第二天神奇地完成更新。问题已解决, 对吧?!

很遗憾,这是不现实的。即使有人通过某种方式 所有受影响的网站,请设法查找相关网站的联系信息 从而成功接触到所有网站所有者 并说服他们执行更新(这可能意味着重构 整个代码库),整个过程最长可能需要数年时间。

请注意,其中许多网站都比较老旧,可能未得到维护。 即使维护人员还在身边,也有可能他们 像您这样技术精湛的 Web 开发者。我们并不指望所有人都 并因为 Web 兼容性问题而更换了已有 8 年历史的网站。

TC39 流程的运作方式

TC39 是负责改进 JavaScript 语言的委员会, ECMAScript 标准

#SmooshGate 让一些人误以为“TC39 希望将 flatten 重命名为 “smoosh”,这只是个笑话,对外部没有很好地进行沟通。 重命名提案等重大决定绝非草率之举 并且绝对不会基于单个人在一夜之前被利用 GitHub 注释。

TC39 针对功能提案制定了明确的预演流程。 ECMAScript 提案以及对其所做的任何重大更改(包括方法 重命名的内容)在 TC39 会议期间讨论,且需要获得 然后再正式成为委员会成员。如果 Array.prototype.flatten,该提案已经历了好几次 一直到第 3 阶段,这表明该功能 可以在网络浏览器中实现。对于其他规范 来避免在实施过程中出现的问题在本例中,最重要的是 反馈是在尝试发布之后发生的:功能的当前状态, 来破坏网络诸如此类难以预测的问题是原因之一 TC39 流程并不会在浏览器推出一项功能后就结束。

TC39 基于共识运作,这意味着委员会必须就任何新的 更改。即使 smoosh 的提议很严肃,但似乎 委员会成员会反对,并使用更常见的名称,例如 compactchain

flatten 更名为 smoosh(即使这并不是笑话) 从未在 TC39 会议上讨论过。因此,官方 TC39 对 此主题目前未知。任何个人都不能代表发言 直到下一次会议达成共识为止。

TC39 会议的出席者通常具有高度多元化 背景:部分拥有多年编程语言设计经验, 而越来越多的开发者在浏览器或 JavaScript 引擎上工作, 而与会人员则代表 JavaScript 开发者社区。

SmooshGate 最终是如何解决的?

2018 年 5 月 TC39 会议期间,#SmooshGate 已通过将 flatten 重命名为 flat 来正式解决相关问题。

Array.prototype.flatArray.prototype.flatMap 在 V8 v6.9 和 Chrome 69。