《高性能JavaScript》读书笔记

零、组织结构
根据引言,作者将全书划分为四个部分:
一、页面加载js的最佳方式(开发前准备)
二、改善js代码的编程技巧(开发中)
三、构建与部署(发布)
四、发布后性能检测与问题追踪(线上问题优化)
这样的组织结构也符合我们的开发习惯,首先进入第一部分。

一、JavaScript加载
起因:script脚本的加载会阻塞浏览器渲染页面和处理用户交互,如果加载的script脚本太多太大,就会长时间阻塞,造成页面假死。
解决方案:

1.脚本位置
放在底部。
放在底部可以保证页面主体结构已经基本加载完成展示给用户,才去加载script脚本。这条准则也有特例,如果是一些公司的埋点脚本,可能要求必须放在head里,以确保能上报页面加载时长。

2.组织脚本
减少script数量,合并脚本。
不论是使用concat打包工具合并,还是cdn提供的combo,都能达到该目的。

3.无阻塞脚本
在页面加载完成之后再去触发加载js,这样可以防止script加载阻塞页面。

原生defer
script标签自带defer属性,可以达到这个目的。捎带一提,async属性可以触发异步加载,合理利用了宽带。

动态脚本
或者使用动态脚本元素,通过js文件动态创建script标签,文件加载完成之后会立刻执行。

这个技术的重点在于不论在何时启动下载,文件的下载和执行过程都不会阻塞页面其他进程。

XMLHttpRequest脚本注入
通过XHR加载脚本,由于不是在script标签中加载,所以可以控制script标签中的代码执行时间。在需要的时候再去把script标签添加到页面上。
这也是jsonp的原理。

作者推荐的无阻塞模式
先加载很少量的jsLoader代码,之后通过loader来加载剩余的代码。

function loadScript(url, callback) {
    var script = document.createElement('script');
    script.type = 'text/javascript';
    
    if (script.readyState) { // IE
        script.onreadystatechange = function () {
            if (script.readyState == 'loaded' || script.readyState == 'complete') {
                script.onreadystatechange = null;
                callback();
            }
        }
    } else { // 其他浏览器
        script.onload = function () {
            callback();
        }
    }
    
    script.src = url;
    document.getElementsByTagName('head')[0].appendChild(script);
}

loadScript('rest.js', function () {
    Rest.init()
});

YUI3、LazyLoad、LABjs
作者介绍了上述3个类库中lazyload的使用方式,其实原理就是上述所说,在体验上各有不同。关键就是loader中监听script的onload事件,决定script的执行时机。

二、高性能编程技巧
这个部分从2到8章分别介绍不同的提高性能的技巧。

1.数据存取
数据的存取有四种方式,字面量、变量、数据项、对象。字面量和变量快于数组项和对象。
局部变量位于作用域链的开始,查找速度最快,所处的位置越深,查找越慢,全局变量查找最慢。
with可以影响作用域链,try-catch语句中的catch也会影响。慎重使用。
如果当前对象没有某个属性或者方法,就会去遍历原型链上的属性和方法,也会造成性能损耗。
嵌套对象层数越多,也会减慢速度。例如,location.href要快于window.location.href。

解决方法:
大部分的性能损耗都是与对象查找相关,所以建议缓存对象,减少查找次数。

2.DOM编程
由于DOM渲染引擎和JS引擎一般是分离实现的,要让两个相互独立的模块建立通讯,就会产生消耗。所以,我们需要减少DOM操作,已经获取的DOM节点,缓存下来,防止多次重复操作。
其次,要注意重绘和重排,对DOM的哪些操作会引起重绘和重排,减少这方面的操作。
最后,需要注意DOM事件处理用户交互。

使用innerHTML和原生的document.createElement()类似的方法,速度相差不大。在最新版的webkit内核浏览器(chrome和safari)以外,innerHTML会更快,而且相较而言,代码量也少很多。

HTML集合是包含了DOM节点引用的类数组对象,例如document.getElementByName()这样的方法,需要注意的是HTML集合一直与文档保持着连接,每次需要新信息的时候,都会重新查询。

在查找DOM元素时,尽可能使用原生方法,如最新支持比较完善的querySelectorAll()。

在页面布局和几何属性改变时就需要“重排”,例如:
添加或者删除可见的DOM元素
元素位置改变
元素尺寸改变(包括:外边距、内边距、边框厚度、宽度、高度等属性改变)。
内容改变,例如:文本改变或者图片被另一个尺寸的图片替代
页面渲染器初始化
浏览器窗口尺寸改变
为了减少重排和重绘,批量修改样式时,“离线”操作DOM树,使用缓存,并减少访问布局信息的次数。在动画中使用绝对定位,使用拖放代理。

使用事件委托来减少事件处理器的数量。

3.算法和流程控制
减少迭代的工作量,比如使用局部变量缓存数组的长度,减少查询次数。
减少迭代次数,使用Duff's Device。
尽可能不用forin,forin要比for、while、do-while要慢。
switch比if-else要快,但要综合考虑代码可读性。
判断条件较多时,查找表要比if-else和switch更快。
递归容易造成栈溢出,控制递归的循环次数。
4.字符串和正则表达式
连接巨大的字符串时,数组项合并在IE7或更早之前版本是最优方法。
如果在现代浏览器中,推荐使用简单的+和+=操作符代替,避免不必要的中间字符串。
回溯是正则的基本组成,也会带来性能问题。
回溯失控发生在正则表达式本应快速匹配的地方,因为某些特殊的字符串匹配动作导致运行缓慢甚至浏览器崩溃。为了避免这种情况,应该使相邻的字元互斥,避免嵌套量词对同一字符串的相同部分多次匹配,通过利用预查的原子组去除不必要的回溯。
5.快速响应的用户界面
任何JavaScript任务不应该超过100ms,否则用户体验会变差。
JavaScript运行期间,浏览器响应用户交互的行为存在差异。
定时器可用来安排代码延迟执行,但不一定绝对精确。
web worker允许程序员在UI线程外执行JavaScript代码,从而避免锁定UI。
6.AJAX
减少请求数
缩短页面加载时间,页面主要内容加载完成之后,用Ajax获取次要内容
代码错误尽可能对用户屏蔽
7.最佳实践
避免使用eval和Function构造器带来双重求值带来的性能损耗。
使用直接量创建对象和数组,直接量更快
避免重复工作,需要检测浏览器时,可使用延迟加载或者条件预加载。
进行数学计算时,考虑使用位运算。
尽量使用原生方法
三、构建与部署
合并JavaScript文件以减少HTTP请求数
使用YUI Compressor压缩JavaScript文件(事实上,如今nodejs下的打包工具更加好用)
在服务端压缩JavaScript文件(Gzip编码)
通过正确设置HTTP响应头来缓存JavaScript文件,通过向文件名加时间戳来避免缓存问题
使用CDN
四、发布后性能检测与问题追踪
使用网络分析工具找出加载脚本和页面中其他资源的瓶颈。
把脚本延迟加载可以加快页面渲染速度,带来更好的用户体验
使用性能分析工具找出脚本运行速度慢的地方,检查每个函数消耗的时间,以及函数被调用的次数,通过调用栈自身的一些线索来找出需要集中精力优化的地方
一些工具:
YUI Profile
Firebug
Page Speed
Fiddler
YSlow
dynaTrace Ajax Edition
事实上,这些工具的功能在新版的chrome开发工具都有了,所以我建议好好学习chrome开发者工具。
感谢阅读。


文章来源:

《高性能JavaScript》读书笔记

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,793评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,567评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,342评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,825评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,814评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,680评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,033评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,687评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,175评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,668评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,775评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,419评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,020评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,206评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,092评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,510评论 2 343

推荐阅读更多精彩内容