12月阅读周·编写可测试的JavaScript代码:调试之生产环境调试篇

举报
叶一一 发表于 2024/12/23 18:17:17 2024/12/23
【摘要】 背景去年下半年,我在微信书架里加入了许多技术书籍,各种类别的都有,断断续续的读了一部分。没有计划的阅读,收效甚微。新年伊始,我准备尝试一下其他方式,比如阅读周。每月抽出1~2个非连续周,完整阅读一本书籍。这个“玩法”虽然常见且板正,但是有效,已经坚持阅读十一个月。已读完书籍:《架构简洁之道》、《深入浅出的Node.js》、《你不知道的JavaScript(上卷)》、《你不知道的JavaScr...

背景

去年下半年,我在微信书架里加入了许多技术书籍,各种类别的都有,断断续续的读了一部分。

没有计划的阅读,收效甚微。

新年伊始,我准备尝试一下其他方式,比如阅读周。每月抽出1~2个非连续周,完整阅读一本书籍。

这个“玩法”虽然常见且板正,但是有效,已经坚持阅读十一个月。

已读完书籍《架构简洁之道》、《深入浅出的Node.js》、《你不知道的JavaScript(上卷)》、《你不知道的JavaScript(中卷)》、《你不知道的JavaScript(下卷)》、《数据结构与算法JavaScript描述》、《WebKit技术内幕》、《前端架构:从入门到微前端》、《秒懂算法:用常识解读数据结构与算法》、《JavaScript权威指南》、《JavaScript异步编程设计快速响应的网络应用》

当前阅读周书籍编写可测试的JavaScript代码

生产环境调试

最小化、组合和混淆让生产代码很难调试。我们已经讨论过,WebKit浏览器有一个“prettify”按钮可以将缩小代码展开(unwrap),但是对于组合和混淆代码呢?或者说,利用另外一种工具使用其他语言,比如CoffeScrit甚至是Java(通过GWT)生成的JavaScript代码又如何呢?

最小化代码

WebKit浏览器有一个“de-minify”按钮,是用于压缩代码的最简单的例子。图7-27展示了Chrome调试器里的最小化代码。

没办法设置断点,如果代码停在某处,是不可能告诉我们确切的位置的。

点击{}按钮,解压代码。此时的代码是具可读性的,并且可以在每一行进行断点设置。但由于这段代码也被Google的Closure Compiler混淆过,所以我们没办法访问原始变量名(类似的混淆工具会产生类似的结果)。

源映像

源映像(source map)可以拯救我们。一个源映像是一个包含大JSON对象的文件,该对象将压缩过的、混淆过的或组合过的JavaScript代码行与原始代码进行映射。一个额外的好处是该“原始代码”不一定是JavaScript!可以查看源映像规范。

使用源映像,被压缩过的、混淆过的或组合过的JavaScript代码可以在调试器里映射到原始代码。本文撰写时,只有Google Chrome原生支持源映像(Beta分支),尽管其他浏览器也会紧随其后。Google Closure Compiler可以组合并生成压缩和混淆的代码,并且还可以对代码输出源映像文件。Google还提供了Closure Inspector,它是一个可以让Firebug理解源映像文件的Firebug扩展。希望不久的将来所有的浏览器都原生支持源映像。

看一下使用过程。在本例中,我们将使用前面看到的getIterator函数。这里它还是在文件iterator.js中:

function getIterator(countBy, startAt, upTill) {
  countBy = countBy || 1;
  startAt = startAt || 0;
  upTill = upTill || 100;
  var current = startAt;
  var ret = function () {
    current += countBy;
    return current > upTill ? NaN : current;
  };
  ret.displayName = 'Iterator from ' + startAt + ' until ' + upTill + ' by ' + countBy;
  return ret;
}

让我们从Google Closure Compiler主页下载最新版本的compiler.jar。现在,可以在iterator.js上运行Closure Compiler了:

java-jar compiler.jar --js iterator.js --js_output_file it-comp.js

该文件除了删除了所有空格(和评论)以外,变量名称也被重命名了:countBy变成了a,startAt变成了b,upTill变成了c,等等。这极大地减少了文件的大小,现在就可以部署到生产环境中了!

就这样吗?这段代码仍然有一个问题:在浏览器中调试该代码会非常困难。即便在WebKit调试器里使用{}按钮对它进行“unminifying”,也不会将其变成原始代码,因为变量已经被重命名了。

幸运的是,Closure Compiler可以使用更高级的转换方式将代码变得更小。然而,这些转换将进一步偏离我们原来的代码。

在这里,它就成了一个问题。如果将该代码作为Web页面的一部分加载到调试器并尝试调试时,我们可以看到代码很难看。

源映像来发力了!首先告诉Closure Compiler为我们生成一个源映像。

如果我们查看生成的源映像文件,就会发现,it-min.js.map确实是一个巨大的JSON对象,该对象的核心是mappings属性,它是一个Base64 VLQ编码的字符串。

最后一点神奇的地方就是将源映像链接到JavaScript代码给浏览器提供服务,通过添加一个额外的HTTP头或在JavaScript压缩中添加一行技巧代码,告诉调试器在哪里可以找到相应的源映像。这里,我们在Closure Compiler输出添加一行代码,将其指向到编译器生成的源映像文件。现在回到浏览器,加载文件并查看调试器。

调试器窗口中显示的不仅是我们确切的原始代码,而且下拉菜单中的文件名也是原来的文件名。断点设置就像在浏览器中的真实代码上设置的一样,但事实上浏览器运行的代码是Closure Compiler输出的最小化代码。

使用源映像的最大问题是像原始代码一样进行的打印/执行表达式,但因为执行的是最小化代码,所以这些原始变量名在浏览器中实际上根本不存在。我们不能对表达式求值从而确定变量的值,我们也不知道变量名被映射成哪个名字了。

在调试器中,我看到了变量current是在哪里定义的,但是我的监控表达式(Watch Expressions)显示它并没有定义!相反,却有一些随机的变量名,如初始值为0的变量d,但它似乎没有在源代码清单里定义。这很奇怪,并且需要注意。或许未来版本的规范可能会处理这种不一致性。

Google的GWT编译器也可以输出源映像,并且也适用于CoffeeScript编译器,那么在浏览器运行编译版本的代码时,就可以说我们在调试原始原生代码。

总结

使用源映像文件可以直接对生产环境和非JavaScript进行调试。


作者介绍
非职业「传道授业解惑」的开发者叶一一。
《趣学前端》、《CSS畅想》等系列作者。华夏美食、国漫、古风重度爱好者,刑侦、无限流小说初级玩家。
如果看完文章有所收获,欢迎点赞👍 | 收藏️ | 留言📝

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。