摘要:除此以外,让元素脱离文档流也是一个很好的方法。因为元素一旦脱离文档流,它对其他元素的影响几乎为零,性能的损耗就能够有效局限于一个较小的范围。讲完重排与重绘,往元素上绑定事件也是引起性能问题的元凶。高性能这本书非常精致,内容也非常丰富。
入手《高性能JavaScript》一周后,终于断断续续看完了。简要说说感受,就是这本书非常薄,非常容易看,认真看的话其实两三个小时就能翻一遍了。这篇文章也是作为一篇阅读笔记,用来记录我在阅读过程中的一些理解与感悟。
乍一看上去,这本书里面有相当一部分内容是非常旧的,很多优化手段在如今高速的网络环境以及先进的浏览器的加持下,视乎已经失去了必要性。然而作为一个有洁癖的人,是无法允许自己的代码“大概差不多就好”,而且我也相信任何一个有追求的人都希望自己的作品是精益求精的,所以这本书仍然有非常大的学习意义。抛开主观,在阅读一些优秀开源库时,看到别人的某些代码非常不理解,在看完这本书以后回想起来才发出感慨,原来人家这么做的目的是为了提升性能。
全书共分为十章内容,以下将按照书本章节的顺序,来逐一撰写我的阅读笔记。
一、加载和执行把js放在
结束标签之前而不是标签内部能够避免浏览器阻塞,提升用户体验,已经算是一个常识。这个常识的背后,涉及到了浏览器单进程的概念。
事实上,多数浏览器使用单一进程来处理用户界面(UI)刷新和javascript脚本执行,所以同一时刻只能做一件事。
这里说的用户界面刷新,指的是我们“所能看到的UI”变化(比如点击一个按钮,会出现按钮被按下去的效果)。换句话来说,处理UI就无法处理javascript,反之亦然。所以如果一份运行时间很长的js脚本放在页面顶端,会阻塞之后页面的下载和渲染,给用户的感觉就是“页面一片空白卡死不会动”。
虽然现在网速和浏览器的效率已经得到了巨大的提升,但随着移动端的兴起以及前端框架如Vue,React的大量使用,这个问题还是非常值得我们注意的。
二、数据存取首先对于数据的存取,有以下这么一句关键:
每一个js函数都会带有一个叫做[[Scope]]的内部属性,也就是该函数的作用域链,它决定了哪些数据能被函数访问。
书上详细介绍了作用域链、执行上下文、活动对象、全局对象、闭包等概念,在这里就不进行复述了。用我自己理解的话来说,就是一个函数若要使用一个变量,它会从最近的地方,也就是定义在函数内部的局部变量里面去找;若没有找到,则往更远处的全局变量(或者上一级作用域)里面去找。恰恰是这个“找”的过程,产生了性能的问题。书上使用了“解析标识符”来表述“找”这个动作,而js性能恰恰是随着解析标识符深度的增加而降低,所以在最佳实践里,往往是通过把一个较深的变量赋值给一个局部变量,在函数内部直接调用这个局部变量来提升性能。
说完变量,就到了方法。在js中一切皆对象,然而js的对象是基于原型而来,这就引出了一个原型链的概念。与前文关于解析标识符的原理类似,要调用一个对象中的方法,首先会从这个对象实例中查找,若找不到,则会沿着其原型链一步一步由近到远地往上查找,其性能也是随之下降的。
另外,书上也讨论到了关于“嵌套成员”的问题。比如window.location.href,它会先找到window对象,然后查找嵌套于内的location对象,再找到这里面的href属性,前前后后套了多层,在性能上也有着一定的花销。所以在实际的编码过程中,我们更多时候会面对的往往是这种嵌套成员的问题,时刻记得缓存对象成员的值,在执行完毕后利用cacheObj = null的方式释放缓存,可以有效地提高性能,如下例子:
// bad document.querySelector(".xxx").style.margin = 10 + "px" document.querySelector(".xxx").style.padding = 10 + "px" document.querySelector(".xxx").style.color = "pink" // good let xxxStyle = document.querySelector(".xxx").style xxxStyle.margin = 10 + "px" xxxStyle.padding = 10 + "px" xxxStyle.color = "pink" xxxStyle = null三、浏览器中的DOM
这一章节详细介绍了关于dom操作的一系列问题。首先要明确一个知识点就是dom操作是具有“天生就慢”的问题。为什么会如此呢?因为在浏览器里面,处理html和js是两套不同的机制,他们通过接口来进行联系的。引用书中的原话,就是可以把html和js理解为两座岛,他们之间需要一座桥来进行沟通,而过桥则会产生时间与成本上面的开销,也因此引起了性能的问题。这一章节通过分析不同的dom操作函数,来综合对比了各种方法的速度。
dom操作往往容易引起浏览器的重绘与重排。重排,指的是页面的布局和几何属性改变时所发生的事情;重绘,是指把dom元素绘制到屏幕上面的过程。
会造成性能问题的,往往来自于重排,因为浏览器需要重新计算页面所有元素的大小与位置,然后把它们安置在正确的地方。所以,要提升页面的性能,很重要的一个举措就是避免页面的重排。
值得注意的是,并非只有在修改页面元素的大小和位置的时候才会引发重排,在获取的时候浏览器也会出发重排,以返回正确的值。
然而很多时候我们不得不直接操作dom,尽管它们会引起重排和重绘。书上给出了几个方案,都能有效提升性能。其实方法和上文关于js缓存局部变量的方法类似,也是通过缓存的机制,减少对于dom元素属性的查找,以及批量修改变量再一次性更新dom的办法去减少查询与修改。
除此以外,让元素脱离文档流也是一个很好的方法。因为元素一旦脱离文档流,它对其他元素的影响几乎为零,性能的损耗就能够有效局限于一个较小的范围。
讲完重排与重绘,往dom元素上绑定事件也是引起性能问题的元凶。利用浏览器自带的冒泡或捕获机制,可以通过事件委托的方式减少事件处理器的数量,从而把性能优化得更好。
四、算法和流程控制这一章首先分析了几种循环类型,结论是只有for-in循环的性能最慢,因为每次迭代都会同事搜索实例或原型属性,导致其性能只有其它类型速度的1/7。
循环在代码中非常常见,既然无法避免,则需要通过尽量减少循环次数,减轻每次循环的工作量的方式提升性能。
对于条件语句if else或者switch,其性能在现实中并没有太大区别,关键是要正确处理语义化的需求。有的时候也可以使用查表法进行。
对于递归算法,最好的提升性能方法是缓存上次执行的结果,在下一次递归的时候直接引用而非从头开始计算。
五、字符串和正则表达式TODO……
(对于正则表达式还没有特别熟悉,这一章跳着看了)
前面五章都是针对JS原生的语法分析性能问题,从这一章开始分析针对用户界面的可感知性能问题。
由于浏览器是单线程运作的,在处理UI事件的时候无法处理js事件,反之亦然,所以对于耗时过长的js任务来说,可以使用定时器的方法使其让出线程控制权,让浏览器优先处理UI事件以提升用户体验。
html5新增的web worker允许多开线程,意味着耗时较长、性能损耗较大的js任务可以放到web worker中进行,而无需阻塞浏览器UI线程的执行。值得注意的是,web worker无法使用浏览器相关的资源,所以无法用以进行dom操作等。
七、Ajaxajax技术已经是如今的主流技术,在这里就无需赘述了。书上关于其性能优化的内容,多集中在浏览器资源缓存上。如果能够有效利用浏览器的缓存机制,可以大大减少与服务端的交互,提升性能。
书上没有提及的是现在逐渐开始流行的fetch API,关于这方面的性能的问题也值得我们研究。
其他剩下的内容都是一些编程实践,代码优化等等。
在如今的前端开发领域中,上线的代码一般都会经过代码合并、压缩,服务端开启gzip等工作。随着http2的发展,网页的性能更会得到提高,可能传统”文件合并“这一工作会逐渐被摒弃。另外http2的服务端推送也能极大地提升页面加载速度,这部分内容在我另外一篇文章《深入研究:HTTP2 的真正性能到底如何》有详细研究,有兴趣的读者可以去看看。
《高性能JavaScript》这本书非常精致,内容也非常丰富。这篇读书笔记仅仅作为首次阅读草草而作的读书笔记,对于书中内容的理解或多或少都会有失偏颇,如果发现有错漏或者更好的理解方式,欢迎留言和我交流~
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/88133.html
摘要:局部变量位于作用域链的起始位置,因此访问速度最快全局变量位于作用域链的最末端,因此访问速度最慢。如访问时间实例属性第一层原型属性第二层原型属性在同一个函数中没必要多次读取同一个对象成员,建议第一次查询到值后就将其存储在局部变量中。 javascript中有四种基本的数据存取位置:字面量、变量、数组元素、对象成员。 1.访问字面量和局部变量的速度最快,访问数组元素和对象成员相对较慢。 2...
摘要:设计模式与开发实践读书笔记最近利用碎片时间在上面阅读设计模式与开发实践读书这本书,刚开始阅读前两章内容,和大家分享下我觉得可以在项目中用的上的一些笔记。事件绑定暂时这么多,以后会不定期更新一些关于我读这本书的笔记内容 JavaScript 设计模式与开发实践读书笔记 最近利用碎片时间在 Kindle 上面阅读《JavaScript 设计模式与开发实践读书》这本书,刚开始阅读前两章内容,...
摘要:作用域链查找作用域链的查找是逐层向上查找。而全局变量和闭包则会与之相反,继续保存,所以使用用后需手动标记清除,以免造成内存泄漏。获取元素的属性获取元素的属性等参考文档高级程序设计作者以乐之名本文原创,有不当的地方欢迎指出。 showImg(https://segmentfault.com/img/bVburXV?w=500&h=399); 作用域链查找 作用域链的查找是逐层向上查找。查...
摘要:包括元素的高度上下内边距上下边框值,如果元素的的值为那么该值为。该值为元素的包含元素。最后,所有这些偏移量都是只读的,而且每次访问他们都需要重新计算。为了避免重复计算,可以将计算的值保存起来,以提高性能。 offsetHeight 包括元素的高度、上下内边距、上下边框值,如果元素的style.display的值为none,那么该值为0。offsetWidth 包括元素的宽度、左...
摘要:设定浏览器属性的属性的方法叫做驼峰式命名是函数名方法名和对象属性名的命名首选格式。由浏览器预先定义的对象被称为宿主对象。在给某个元素添加了事件处理函数后,一旦事件发生,相应的代码就会执行。 1.JavaScript是一个使网页具有交互能力的程序设计语言。 2.设定浏览器属性的属性的方法叫做BOM. 3.驼峰式命名(myMood)是函数名、方法名和对象属性名的命名首选格式。 4.命名变量...
阅读 3325·2021-11-19 11:36
阅读 2926·2021-09-27 13:34
阅读 1989·2021-09-22 15:17
阅读 2402·2019-08-30 13:49
阅读 752·2019-08-26 13:58
阅读 1356·2019-08-26 10:47
阅读 2535·2019-08-23 18:05
阅读 597·2019-08-23 14:25