老生常谈:你的 JavaScript 怎么运行得这么慢?

as23352512 8年前
   <p style="text-align:center"><img src="https://simg.open-open.com/show/6c0f778a9bef9ab5d43ce8477d7765c6.png"></p>    <p>最近,在啃一本为人所熟知的动物书《[O`Reilly] - High Performance JavaScript - [Zakas]》。本书中,作者主要以四大角度去谈及 JavaScript 如何在性能上得以提高。为了能使其变为我自身 JavaScript 知识体系中的一部分,我以自己的语言去加以总结(虽是英文,但描述得非常直白,希望大家不要害怕。)</p>    <p>在此,我希望通过这个 Issue,以中文的形式从更高的层次去总结所有的性能提升技巧以方便记忆与查询(但是细节也要深入去理解与学习,而非得过且过):</p>    <h3>1. JavaScript 的加载</h3>    <p>作者就此角度主要谈及如何去高效地加载与执行 JavaScript 代码。正因其执行过程会产生阻塞问题,因此我们需要通过若干方式去最小化该性能影响:</p>    <ul>     <li>尽可能地把所有的 script 标签置于页面的底部,即 body 标签内的末尾部分。</li>     <li>使用构建工具把多个 script 组合在一起</li>     <li>使用以下地方式去异步加载 JavaScript 代码:      <ul>       <li>添加 defer 属性可预加载 JavaScript 代码并等到页面加载渲染完成后及 onload 事件触发之前执行</li>       <li>以代码的方式动态加载 script</li>       <li>通过 XMLHttpRequest 来加载 script</li>      </ul> </li>    </ul>    <h3>2. 编码技巧</h3>    <p>以优雅的编码技巧去提高性能会涉及到几个方面,如数据访问方式、DOM 相关编码或代码结构等。经过此章的阅读,个人认为这些技巧并不仅仅针对于 JavaScript 语言,而是作为一种更高层次的编程哲学去浸染开发者的编程思想,从而在骨子里下意识地提高代码的优雅程度。</p>    <p>2.1 数据访问</p>    <p>首先,作者开篇所讲的是关于数据的访问,并区分出四种访问的方式:对字面值的访问、对变量的访问、对数组成员的访问以及对对象成员的访问。当然,访问的快慢也是与这顺序所相对应的。</p>    <ul>     <li>字面值与变量的访问比数组及对象的访问要快</li>     <li>访问本地变量要比访问外部变量要快</li>     <li>尽可能不使用 with 、 try-catch 及 eval() .</li>     <li>减少对象成员的嵌套</li>     <li>访问对象成员时,查找的原型链越长越消耗更多的时间</li>     <li>闭包不仅会占用内存,而且在低版本浏览器中导致内存泄漏,因此要权衡。</li>    </ul>    <p>2.2 DOM 编程</p>    <p>第二,关于 DOM 相关的编程,我们要谨记一点:DOM 的访问及操作犹如搭载在桥梁上的收费站,太多的访问与操作只会带来性能上的消耗。因此:</p>    <ul>     <li>我们应该尽可能地减少对 DOM 元素的访问,而把工作放在 JavaScript 代码中去执行</li>     <li>若需要重复操作 DOM 元素,则把其对应的引用存储在一个本地变量</li>     <li>像 document.getElementsByName() 、 document.getElementsByClassName() 或 document.getElementsByTagName() 等方法所放回的是一个 HTML Collections。遍历该集合时我们需要考虑到,若该集合不怎么大时,我们可以把集合的大小缓存在一个本地变量以作循环条件中的判断;而若集合非常大时则需要考虑把其转换成数组来遍历</li>     <li>若浏览器支持时,尽可能地使用更为高效的封装属性或函数,如 firstElementChild 、 document.querySelectorAll() 等。</li>     <li>尽可能地去减少页面的重排与重绘</li>     <li>在制作动画时,应把元素先置为绝对定位,以减少重排及重绘所带来的消耗</li>     <li>使用事件委托机制来减少事件处理函数的数量</li>    </ul>    <p>2.3 代码中的算法及代码流的控制</p>    <p>个人认为,为何我们写的代码总运行得这么慢?主要原因在于我们的算法不够高效,而且代码流的控制不够优雅。所执行的代码越多,执行的时间也就越长。因此不管是循环结构还是控制结构,作者都给出了自己的经验之谈:</p>    <ul>     <li>for 、 while 以及 do-while 结构都非常相似,并没说哪种循环结构更为高效</li>     <li>除非需要遍历一个属性未知的对象,不然请不要轻易使用 for-in 结构</li>     <li>尽可能地减少循环的次数及每次循环的工作量,以优化循环</li>     <li>尽管 switch 结构比 if-else 结构要高效,但并非是一个最佳的选择</li>     <li>对于代码有多种流向的情况,Lookup 表是一种不错的选择</li>     <li>当算法涉及到递归时,请注意各浏览器中栈使用量的限制</li>     <li>当递归算法超出栈使用量的限制时,尝试使用循环去替代算法或通过记忆的手段减少重复计算的次数</li>    </ul>    <p> </p>    <p> </p>    <p>来自:https://github.com/aleen42/PersonalWiki/issues/27</p>    <p> </p>