个人简介 Stoyan Stefanov是Facebook的一名工程师(前雅虎员工),也是位著作家和投稿人,并且是许多 O'Reilly 书籍的技术审查者。他经常在会议和博客上(www.phpied.com)谈论网络开发相关的主题。Stoyan是smush.it图片优化工具的创造者和YUI贡献者,还是雅虎性能优化工具YSlow 2.0的设计师。
QCon是由InfoQ主办的全球顶级技术盛会,每年在伦敦、北京、东京、纽约、圣保罗、杭州、旧金山召开。自2007年3月份首次举办以来,已经有包括传统制造、金融、电信、互联网、航空航天等领域的近万名架构师、项目经理、团队领导者和高级开发人员参加过QCon大会。
1. 们正在QCon的2012旧金山会议的现场与Stoyan Stefanov谈论有关JavaScript模式的话题。你好Stoyan,先来个自我介绍吧?
Stoyan:大家好,我是Stoyan Stefanov。现在就职于Facebook,目前的工作主要跟社交插件有关,比如“Like”按钮。我们试图让所有的功能都能尽量快的响应,因为这种插件在网络上随处可见,通过加快那些插件的响应速度可以让整个网络环境变的更快。在此之前我在Yahoo工作,主要处理性能方面的事务,并开发一些工具比如YSlow或者Smush.it图片优化工具,一般这些工具用于帮助团队提高他们的产品性能。我以前出过几本书,而且很希望在会议上和其他人讨论性能方面的问题。就说这么多吧。
2. 你提到书籍并且曾经授权O’Reilly出版过《JavaScript Patterns》,在书中完整清晰的讲述了诸如JavaScript性能之类的事情。你能告诉我们对于JavaScript开发者需要面对的独特地挑战吗,尤其是当他们要处理大型的代码库和性能方面的问题时。我知道你有着深厚的PHP背景,所以你怎么看待JavaScript特有的挑战性?
Stoyan:我不认为语言本身有什么独特性,它的特点在于JavaScript程序通常的运行方式。事实上它是运行在浏览器中的,你不用事先将其编译成字节码或其他东西,却必须将程序代码通过网络传输过去并在浏览器中解译。随之带来一些挑战,特别是像你说的大型代码库。所以你首先要考虑的就是JavaScript程序或应用是运行在网络上的,我通常建议先从这一点开始入手,然后再考虑优化循环或者继承模型或者其他之类的东西。仅仅就优化网络而言,首先就是JavaScript代码的传输,这意味着修改代码,用Gzip压缩,削减HTTP请求的数量或者将网络相关基础做好。其次比较独特的是如何载入JavaScript代码,要么事先载入所有的东西并且在载入1Mb多的代码时使用启动画面,或者用更好的策略来让代码按需载入。理想的设计是让应用程序甚至不用JavaScript就能运行,如果这可能的话。
然后只要页面或应用程序的工作方式不是异步的时候,你异步加载JavaScript。无论程序何时触发,它都会以渐进的方式让所有的东西越来越美妙。
3. 你认为什么样的工具能够较好的帮助JavaScript开发者处理性能方面的问题,不仅仅是异步加载之类的开发库,也可以是开发工具,用于帮助他们定位性能问题的区域,以便用少量优化产生较大收益?
Stoyan:就像我说的,最重要的是网络本身。如果你能使用webpagetest.org之类的运行YSlow和PageSpeed将所有网络基础层面的障碍都解决并使之正确运行,那么所有这些代码的加载就是以最有效的方式进行的。然后你再在此的投入时间所产生的效益是越来越低的,所以将网络配置正确后就要进行下一步的工作。一般来讲应用程序会在DOM操作上花费大量的时间,导致浏览器回流(reflow)和重绘(repaint)等等。因此与布局相关的方面是下个阶段的重点。至于工具方面,现在浏览器可用于调试的插件已经比几年前好多了。
火狐浏览器的Firebug和网络监测工具确实对此很有帮助,它甚至能给你显示出你在布局和绘制时花了多少时间,还能提供给你CPU资源消耗情况。这些浏览器内嵌的工具是非常棒的。对于性能测试我比较喜欢在线工具,你不需要安装任何东西,仅仅就是打开页面,提交URL后立刻就能看到效果。因而我们最喜欢的一类在线测试工具就是www.jsPerf.com,在那里你能够在任何预设的假设和规则下进行测试,无论这些它们在你的用例中是否有意义,也不论你将来是否会注意到。我们的建议是每次做优化时,首先就是做测试如果……哦,对了,首先是对产品框架进行全面的剖析,因为这确实会是应用程序的性能瓶颈,而且毫无原因地做优化可能根本不会产生任何提升。在分析之后就需要在jsPref中验证某些假设条件是否值得花时间做优化。当有些人给你提建议说:“应该这样而不是那样”,对我来说正确的回应就是给我看jsPerf的测试结果,或许他说的情况根本不会发生。
4. 你还提到在处理DOM时引起回流等造成的性能问题。现在实际上至少要面对三种不同的引擎,比如WebKit和Gecko。且不论Internet Explorer当时使用的是什么,也不论旧有的浏览器以及移动客户端的特殊性,单说开发者该如何处理这些担忧,例如你可能对某个开源浏览器非常熟悉,并懂得它如何处理回流等问题,但是你如何将这种经验应用到所有的浏览器中?
Stoyan:很明显,各个浏览器都有些与众不同的特点。不过我认为一般只需要盯紧一种浏览器,如果保证所有DOM操作在其中都能快速执行的话,很有可能在其他各种浏览器中也会有良好的表现,除非是为兼容IE6或IE7而做的特殊处理。一旦发现某些细节在Chrome或者Firefox中运行缓慢,并且在IE有同样表现,那此处就值得你花大力气研究解决了。目前有一系列的工具,我刚才忘记提了,支持IE的dynaTrace Ajax Edition或其他软件可以给出布局、执行函数和网络通讯所花费的时间。它就像一个瀑布式的WebPageTest外加布局的功能,所以它可以协助在IE中调试,不过一般只是减少DOM操作。所有这些事情,都是离线构建DOM树而非在真实页面上,它所减少的回流很可能在所有浏览器上都有良好的效果。
5. 据我了解有些debug版本支持Firefox,在其中可以看到页面加载的彩色矩形图,也能看到回流。至于Internet Explorer平台上是否有相似的东西?
Stoyan:我也不太清楚。
6. 谈到JavaScript,对于将其作为一种前端的汇编语言有着很大的争议。我们有好几种方式来开发JavaScript,使用GWT已经很长一段时间,CoffeeScript也是一种相当主流的开发语言,Dart语言似乎也雄心勃勃,我们还有几个项目是用于将ECMAScript 6转化为当前浏览器兼容的JavaScript的,你认为用哪种方式开发JavaScript更合适?在未来两年哪些方式会比较主流?
Stoyan:我喜欢JavaScript的运行方式,所以我不知道……目前确实发生了一些事情,微软微软开发的TypeScript在某些方面看起来很好用,我愿意去尝试一下,因为它在语法等方面类似于ECMAScript 6。不过我知道关于GWT的一些其他的事情,如果你在设定你的Java方式,那可能对我是非常有帮助的。很明显CoffeeScript也是这样,它确实是个好东西,而且用的人也非常多,但是对于了解JavaScript的人来说,我觉得学习另一种语法或者以Java的方式书写JavaScript并没有什么好处,那是件令人很讨厌的事情。GWT和CoffeeScript很不错,TypeScript看起来也很有前途,因为人们喜欢他们的类型,他们想要知道这些函数接受什么(参数)返回什么(结果)。
当你从一种类型映射到另一种类型只为满足类型检查时,类型可能有点碍事,但总的看来这是一种良好的尝试。我最喜欢的是它编译成了具有良好可读性的JavaScript,所以你可以用类(ECMAScript 6中可能会引入),还能看到ECMAScript 3或者5的代码是如何被生成的,多么的精简和优美。它创作可读性的JavaScript代码并且在编译时进行所有的类型检查,这是值得肯定的,你还可以任意引入,甚至时间校验(也一样)。至于Dart,我不太清楚,这是个新语言,它被设想用于替代JavaScript,而不是真正推动JavaScript向前发展,所以我对它不太感冒。
7. 你提到ECMAScript 6的类,看起来你对此持积极态度,那么你如何看待ECMAScript 6将带来的其他修改?
Stoyan:实际上我并不是对类抱积极的观点,我对此并不着迷,不过看起来有些人是非常希望如此的。重要的是,如果你以前用过TypeScript,在你编写完TypeScript代码后翻看它的编译结果就会发现,向后兼容版本实际上非常简短,看起来非常的优美和整洁。因此如果能不引入任何语法而在语言中完成这一切才是最关键的所在。
8. 那么你的意思是,那些讨论类的人们可能并不清楚JavaScript的习惯用法是多么的简洁?
Stoyan:是的,有可能。因为当构造你的汽车做例子的时候,它看起来就很像类,因而人们会认为:“这就是一个类”,但这实际上是一个构造函数。他们希望基于类实现继承。这是JavaScript的美妙所在,如果你想做继承,有多种方式可供选择,实际上并不真的需要那么多。我的意思是继承只是代码重用方式的一种,而不是仅有这一种,只是习惯了类概念的人们还是欢迎将这些新特性添加进ECMAScript 6。对于其他方面我也没有紧紧跟随ECMAScript 6发展的脚步,它在我看来太臃肿了,我喜欢精简的事物。我猜我出的书下个版本又要变厚了,我也不希望这样,因为我喜欢将事物简单化。所以我希望Douglas Crockford最终能参与并拯救这个项目,但我不认为这有助于缩小项目的范围。
ECMAScript 5是非常好地改进语言的方式。因为它没有引入任何新语法,所以它是向下兼容的,大部分的改进都可以垫补(shim)到所有浏览器当中,这就是为什么它很出色并且很快便被采纳。而且那些没办法垫补的项目,我不觉得人们会像定义不可改变的属性一样经常使用,我觉得你不会的。人们并不倾向于用它,不过现在我们有太多转换编译器了,看起来好像每个人都是语言设计师。我觉得这些已有的转换编译器会使得转换更容易一些,TypeScript之类的表名,你使用新的语法并将其编译为易读的旧式JavaScript代码。
9. 你提到了垫补,你对于垫补(shims)和填充(polyfills)给性能造成的影响有什么经验吗?我曾听人抱怨说这些垫补在较老的浏览器上的表现多么多么的差或者性能是如何地不可预料,比如Underscore或类似库提供的某些ECMAScript 5特色功能,如果在本来没有这些功能的地方用的太多会碰到性能问题?
Stoyan:如果你垫补的仅仅是JavaScript的数组方法等等,事实上那不是真正的大瓶颈。这取决于应用程序的类型,如果你用JavaScript创建游戏、视频或者音频,那问题就多了。不过对大多数的应用程序,甚至更复杂的如Facebook或Gmail而言,我不认为这会成为其性能比赛的速度瓶颈。谈到垫补HTML 5功能,可能复杂一点,因为它涉及DOM,这可能会是性能下降的原因。对于JavaScript,就大多数用户而言,我不相信这是个大问题。事实上,John Dalton做过jsPerf测试,他发现在大多数浏览器中如果垫补诸如数组映射或过滤旧的循环结构,结果是新加的比原生的映射速度更快。他认为这是由于那些浏览器的测试基准造成的。他们花费了大量的时间用于优化循环结构,以期在基准测试中获得良好的表现,因而你所用的几个普通的循环在某些测试用例中可能比原生的速度更快,而你的垫补也比原生的更有效率。
我并不是不建议使用原生的功能,这仅仅是举个例子。这种事情很难一概而论,不能总说原生的更好,你可能第一次听说,但并不意味着一直是这个情况。所以我通常建议将所有的假设进行测试而不是简单的说:“某某说这个东西更快或更慢”。仅仅是看看jsPerf测试或者检查下这个建议是否能用到你的用例中去,数组中有迭代可多少元素以及你到底在那里做了什么等等。如果你有一个HTML集合并可以通过标签名获取元素,然后你遍历它并进行某些工作,实际的DOM操作会比之前提到的任何一种循环都慢得多。
10. 你提到了游戏开发,人们在做游戏或3D开发时常常进行大量运算,由此而在垃圾回收等VM自身相关的事情上遇到问题,对此您怎么看?
Stoyan:这方面我所知有限,实际上我还没开发过游戏。最近我在网络音频方面做了一些尝试,真的是很棒,除此之外我所关注的大部分是常规的网页类型的应用。游戏开发者会面对很多不同的挑战,这些东西跟迭代效率都有很大的关系。(10000像素?)
11. JavaScript就是这样一种让人又爱又恨又抱怨的语言。对于那些正进行大规模JavaScript开发或在筹备大型代码库的团队你有什么建议?有些什么常见的陷阱是需要注意的?
Stoyan:大型应用程序该担忧的主要是如何让程序不那么大,应该保持小而美,而不要花很多时间设计所谓优美的架构用以适应那些有的没的用例。你需要做的就是让功能运行起来,然后反复检查并优化代码,将部分的代码抽象出来等等。我觉得从开始就计划构建大型程序是错误的,应该顺其自然。当你需要反复叠加时也可以将其弃置,用更有效率的或更抽象的代码取而代之。
最主要的是注意不要计划着编写大量的JavaScript代码,你可以不用或者用少量的代码(就完成任务)。应该将精力放在网络传输上面,关注所有被传输到客户端的字节。考虑一下移动浏览器的特点,因为有这样的实验,你能看到它解析较大的库(如jQuery)会花费多少时间。尽管是从缓存中获取,尽管不是通过网络传输,可怜的移动浏览器也需要一段时间来评估这件任务。所以说开头时应该尽量少,而后根据需要叠加。我认为开始就做庞大的设计和设计用例是不靠谱的,那些用例可能将来都没有实用性,然后感觉在客户端不需要改变这个架构的任何东西了,因为你已经在这上面花了那么多的时间让其演化,并在他决定按需要将其抽象之前,已经将同样的代码复制到三遍了。
12. 与那些在其他平台或语言通用的工具相比,你觉得JavaScript的工具效率如何?你个人选择使用什么工具或IDE?
Stoyan:我一点也不喜欢IDE,因为我在几年前就改为使用Mac了。我一直用的就是像常规的文本编辑器这类的工具,听说人们现在更热衷于Sublime,它可能更好用。我只是按照我的习惯来的。此前使用Windows的时候,我用的是个叫Text Pad的简单的文本编辑器,其实你所需要的不过是代码高亮,更棒的搜索和重放功能,自动完成很棒,但并非是必需的。 在一个讲座中,Douglas Crockford谈到过这样一个例子:“如果你取来去年写过的所有代码重新输入,它可能会花掉你一天或一天半的时间。”那天肯定没什么趣味,你仅仅是坐下来,在很短暂的时间内就输入了去年编写的所有东西。那问题是:“你在剩下的时间干什么?”肯定不是打字,所以专注于IDE可能并不是解决问题的正确办法。大多数时间他们会尝试用eclipse写PHP,再之前可能用过Zen Studio,所以IDE太多了,不可能被一一列举。因而你的真正需求就是一个好的文本编辑器而已,无论这些工具有什么办不到的,你可以……你知道大家是如何谈论那个问题吗,任何问题实际上都是人的问题。我认为任何问题都有人为对策,因而可以采取比如代码审阅和培训团队成员的措施,查看相同代码,翻看代码历史,就能在源代码控制历史中知晓为什么这部分用这种方式实现,以及你是如何考虑这个问题的等等。
应该协同工作在相同代码库上,而不是相反,这是我的库,那是我的类,没有人能接触到它等等。至于为什么在提交代码前要有代码审阅,首先能获得验证,其次代码审阅能让代码更优美,而且成员可以跟上项目的开发进度并通过这种方式与别人相互学习。在工具方面,我觉得在某种过程控制和代码审阅工具方面做投资是个比较好的选择,因为代码是你关注最多的东西,就像是某种形式的社会,发展可能不错,它假设你只是在做什么事情,只是保护它,因为这是我的地盘。
13. 你觉得其他的HTML5平台怎么样?或者说其他的网络平台怎么样?从开发者的体验来讲,你觉得HTML5的API和CSS3的特性速度怎么样?有没有什么需要改进的,又或许你觉得如果让你来做你能处理的更好,或者正在考虑但还没有着手做的?
我喜欢这个问题。它发展的非常快,也许会有错误,但我也需要花上未来许多年的时间来发现。实际上我尤其喜欢Mozilla正在用Firefox OS所作的事情,他们试图给电话、SMS和用于震动的API设立标准化的新API。所有的这些都让人很兴奋,对于CSS 3我所需要的不过是将一大堆JavaScript动画库扔掉并替换成简单的CSS 3动画。我觉得这些确实很迷人很有趣。寻找任何新的,可以使用的新玩具就是这样。比方说我最近尝试了网络视频的API,我高兴的发现……
14. 你认为这次会涉及网络视频吗?
Stoyan:没错,我对目前为止看到的都很感兴趣。
15. 你还是位音乐家,那你可能已经在这些方面投注了些精力对吗?
Stoyan:是的,这是回去之后首先要做的事情之一。我在学校时曾上过Pascal的课程,学习了如何声明变量之后,还没学到数组我就想怎么用这些东西做出声音来。这很美妙很让人兴奋,因此我现在对重拾这个道路很高兴。因为上面那些原因,我觉得我看到的音乐软件就像Photoshop或你们的IDEs,太复杂太庞大。要是只使用简单的操作就能随心所欲的做出声音的话该多么好啊。
InfoQ:非常感谢,Stoyan!
Stoyan:也谢谢你Dio,不用客气!