- 提交者:@hax
- 360TC状态:已讨论
- 反馈收集:#2
Hashbang 提案 允许脚本的第一行是如下形式:
#!/usr/bin/env node
'use strict';
console.log(1);即如果第一行是以#!开头的话,忽略第一行。
这基本上是当前 node.js 等 CLI 的行为。
Hashbang 提案在2018年3月达到 stage 2,同年11月达到 stage 3,Chrome 74+ 和 Firefox 67+ 已实现。
#! 必须在脚本的最开始,之前不能有任何内容,包括但不限于注释、换行、空白、BOM等。否则会因为 invalid token 而产生 SyntaxError。
这是能正常执行的脚本:
<script>#! ...
console.log(ok)
</script>以下均是 SyntaxError:
<script>
#! ...
console.log(ok)
</script>
<script>// copyright 2019 hax
#! ...
console.log(ok)
</script>
<script>/*debug info generated by server*/#! ...
console.log(ok)
</script>注意上面用inline script做例子,但也适用于引用外部脚本的情形。
在现实世界中,各种预处理器,服务器SSI,第三方组件等,均可能对脚本 prepend/append 一些内容,例子包括:
当原始内容包含 hashbang #!,转换后内容中 #! 就不再在文件最开始,从而导致 SyntaxError。
这里的风险是,脚本的原作者或使用脚本的开发人员可能并不能预见到其脚本将会被转换,即使他们在理论上知晓这种可能,在实践中也经常会忘记这一点,不能及时注意到加入 hashbang 对转换造成的影响,从而轻率的加入 hashbang,并且极不可能将这一变更视为 breaking change 而特地为此发布 major 版本。使用脚本的用户如不能及时注意到这一变更和其后果(极大可能),就会因为转换导致的 SyntaxError 引发线上故障。
另一方面,转换设施和组件很可能是由独立的基础设施团队或运维团队部署和维护的,几乎不可能指望所有这些团队都能知晓 hashbang 所带来的风险。即使他们知晓了这一风险,也可能由于各种客观条件因素而不能及时响应,或简单将此归于 edge case,或认为这是开发人员的责任。在最好的情况下,基础设施团队或运维团队想要主动避免这个问题,就必须让涉及的所有转换设施和组件都对 hashbang 做特殊处理,这可能是不现实的,或者实施成本可能超出预期。
短期来说,这一风险并不显著,因为组件转换通常用于在浏览器中执行的脚本,而目前所有使用 #! 的用例仅限于 CLI 的入口脚本。但长期来说,可能会有一些同时适用于 CLI 和浏览器环境的脚本会因为引入 hashbang 而遭受问题。另一方面,由于因该风险导致实际后果的频率可能很低(即被认为是 edge case),反而可能导致这一问题一直被有意和无意的忽略。考虑整个生态和产业的规模,这一风险在未来时间所累积的成本可能比我们想象的要更大。
CSS的 @charset "xxx" 机制类似于 #!,必须严格在文件首。但这个例子有个重要不同:
@charset "xxx" 不在文件首只是会使得charset声明无效,但并不一定导致整个CSS文件解析失败。事实上,UTF-16文件只能以BOM来提供编码信息,而不能依赖charset声明。而其他的常见编码(UTF-8、ISO-8859-x、Shift-JIS、GBK等)均是ASCII兼容编码,因此不正确的解码只是导致少部分乱码,但CSS整体上仍然是可以工作的。
另一个例子是 DOCTYPE 声明,在老IE浏览器中(IE9和之前),DOCTYPE 前如果有任何内容,包括注释和XML声明,均会触发 quirk mode(即 DOCTYPE 声明无效)。但该例中,前置内容也不会导致解析失败,并且新浏览器也没有这个问题。
从历史的角度看,HTML/CSS/JS 的前置或后置注释、换行、空白等从来不会导致整体性的解析失败。进一步说,JS前置和后置注释、换行、空白等从来不会改变JS的解析结果和语义。之前提及的许多转换的例子潜在地依赖这一点。所以本质上,这也应该被视为一种 Web Compatibility 的要求。
- 不限定
#!必须在文件首,而是按spec附录中的 HTML-like Comments 类似的方式来处理,具体来说可采用与-->(SingleLineHTMLCloseComment)相同的grammar(将-->换为#!)。 - 撤销 hashbang 提案,hashbang 需求仍像现在一样由 CLI 自行处理。开发者知晓 hashbang 只能用于特定 CLI 的入口脚本,而不会期待一般的 JS 文件可以带有 hashbang。