《高性能javascript》读书笔记

此书主要从js语法层面,算法,浏览器渲染机制,网络传输HTTP、XHR,项目创建和部署方面给与高性能网站的优化方式。

一、加载和运行
1.javascript阻塞dom渲染,尽量放在body底部。可以组合外接javascript,数量多性能更慢。 保持javascript文件小。//window.onload之后开始下载脚本。 不改变dom的脚本使用defered属性,其会异步下载,并不阻塞js执行,在dom完全渲染完再执行,defer在window.onload之前。
2.async是在下载完,defer是在dom准备完毕。(HTML4中的规范,目前无兼容问题)
3. script中动态插入script,会异步下载执行。动态加载脚本是非阻塞js下载常用的模式,可以跨浏览器。 通过创建XHR请求,动态创建script也可以实现非阻塞。(不常用)
4.剩余js代码压缩并放在在/body之前

二、数据访问
1.性能对比:直接量>变量性能更快>对象>数组 (目前浏览器影响较小)
2.内部【scope】属性包含一个函数被创建的作用域中对象的集合。决定哪些数据可被访问,函数运行时,查看执行上下文。函数运行时,每个标示符都要经过作用于链的搜索过程,这个查找过程会影响性能。
函数中局部变量的访问速度总是最快的,而全局变量通常是最慢的。
对策:用局部变量代替复用的全局变量
场景:try catch的时候,catch函数中所有局部变量会放在第二个作用于对象中,执行完恢复。with会产生新的作用域
3.闭包可以访问外函数的变量和全局变量,容易导致内存泄露,所以小心使用。
4.js对象基于原型链,调用深层的原型链上的属性或者方法会使性能下降。(目前浏览器影响较小)
成员嵌套越深,访问越慢。 以局部变量替代属性,避免多余的属性查找带来性能开销。在处理嵌套对象成员时这点特别重要,它们会对运行速度产生难以置信的影响。

三、DOM
你通过桥梁从 ECMAScript 岛到达 DOM 岛时,都会被收取 过桥费
  • 最小化 DOM 访问,在 JavaScript 端做尽可能多的事情。
  • 在反复访问的地方使用局部变量存放 DOM 引用.
  • 小心地处理 HTML 集合,因为他们表现出存在性,总是对底层文档重新查询。将集合的 length 属性缓存到一个变量中,在迭代中使用这个变量。如果经常操作这个集合,可以将集合拷贝到数组中。
  • 使用速度更快的 API,诸如 querySelectorAll()
  • 重绘和重排版;批量修改风格,离线操作 DOM 树,缓存并减少对布局信息的访问
  • 动画中使用绝对坐标,使用拖放代理。
  • 事件托管技术最小化事件句柄数量。

四、算法和流程控制
  • Loop循环(迭代): for、while、do while、for in 。其中for in会访问对象及其原型上的属性,速度上慢了50%,适合对数目不详的对象属性进行操作。 改善循环: 1.将循环终结条件存入局部变量 2.减少循环次数 
  • 条件: if else 适合数量少的条件判断,而 switch适合数量较多的条件判断。 优化if else 将可能性最大的放在前。  如何键值可以判断的条件,则可以使用数组或对象查表法。
  • 递归: 所有递归能实现的都可以用迭代来实现,优先使用迭代,因为迭代的循环开销要比递归的函数反复调用的开销小。大的递归可能造成stack overflow(栈溢出),因此可以用迭代改写。  递归的好处是逻辑比迭代的易懂。。

附:(合并排序算法) 将每个需要排序的数组拆分为两个数组进行排序,得到两个有序的数组就可以得到需要的结果,递归进行(1,2,4,8……),直到所拆的数组只剩下一项。
function merge(left, right){
    var result = [];
    while (left.length > 0 && right.length > 0){
    if (left[0] < right[0]){result.push(left.shift());
    } else {result.push(right.shift());
    }}
    return result.concat(left).concat(right);
}
function mergeSort(items){if (items.length == 1) { return items;}
    var middle = Math.floor(items.length / 2),
    left = items.slice(0, middle),
    right = items.slice(middle);
    return merge(mergeSort(left), mergeSort(right)); 
}

递归优化:  减少多次调用的重复工作,建立缓存
function memfactorial(n){
    if (!memfactorial.cache){
        memfactorial.cache = {"0": 1, "1": };
    }
    if (!memfactorial.cache.hasOwnProperty(n)){
        memfactorial.cache[n] = n * memfactorial (n-1);
    }
    return memfactorial.cache[n];
}

当遇到代码总量越大的时候,这些优化的用处越大

五、字符串和正则表达式
方式:字符串相加, array.join(), str.concat('',’’);  在大数量的字符串相加的情况下,使用数组方法实现字符串相连的效率更高,因为单纯的字符串相加包含了拷贝越来越大的字符串这个过程。; 使用concat方法的优势是比较灵活,缺点性能不如+ 

正则表达式的匹配流程: 1.找到起始位置 2.匹配正则表达式的每一个单元 3.成功或者失败回溯
正则中贪婪版本: 的* 表示0到正无穷,尽可能多的内容 
            懒惰版本: *?表示尽可能少的内容

[\s\S]表示所有的字符包括换行符,不能用.代替
/<html>[\s\S]*?<head>[\s\S]*?<title>[\s\S]*?<\/title>[\s\S]*?<\/head>[\s\S]*?<body>[\s\S]*?<\/body>[\s\S]*?<\/html>/

正则优化思想:减少回溯

六、相应接口
浏览器进程的队列处理机制。。。
  • 大多数浏览器有一个单独的处理进程,它由两个任务所共享: JavaScript 任务和用户界面更新任务。每个时刻只有其中的一个操作得以执行,也就是说当 JavaScript代码运行时用户界面不能对输入产生反应,反之亦然。这些任务不是运行 JavaScript 代码,就是执行 UI 更新,包括重绘和重排版。大多数浏览器在 JavaScript 运行时停止 UI 线程队列中的任务,也就是说 JavaScript 任务必须尽快结束,以免对用户体验造成不良影响,如果长时间未响应,浏览器会触发无法相应弹出框。
单一的js应当在100毫秒内相应用户的输入,若超出100ms,用户就不能感受到对接口的控制。
为了让出对UI的控制,js定时器进入了视野。
windows上的定时器分辨率为15ms,值为15的定时器延时将会根据最后一次系统时间刷新转化为0或者15. 一般建议最小为25ms
  • 如果单端js执行时间太长,可以分解成若干个步骤存在一个数组中。通过定时器的异步原理,可以不阻塞UI的渲染。
function multistep(steps, args, callback){
    var tasks = steps.concat(); //clone the array
    setTimeout(function(){
//execute the next task
var task = tasks.shift();
task.apply(null, args || []); //determine if there's more
if (tasks.length > 0){
    setTimeout(arguments.callee, 25);
} else {
      callback();
}
}, 25);
}
  • web worker可以让js代码运行不占用浏览UI进程的时间。
(Web work的运行环境:
一个浏览器对象,只包含四个属性: appName, appVersion, userAgent, platform
一个 location 对象(和 window 里的一样,只是所有属性都是只读的)
一个 self 对象指向全局工人线程对象
一个 importScripts() 方法,使工人线程可以加载外部 JavaScript 文件 • All ECMAScript objects, such as Object , Array , Date , etc.
所有 ECMAScript 对象,诸如 Object Array Data ,等等。 • The XMLHttpRequest constructor
XMLHttpRequest 构造器
setTimeout() setInterval() 方法
close() 方法可立即停止工人线程)
webwork需要单独建立一个js文件, 要创建网页工人线程,你必须传入这个 JavaScript 文件的 URL :

var worker = new Worker("code.js”);
worker.onmessage = function(event){
    alert(event.data);
};
worker.postMessage("Nicholas”);

//inside code.js
self.onmessage = function(event){
    self.postMessage("Hello, " + event.data + "!");
};
消息系统是页面和工人线程之间唯一的交互途径。
应用场景: 网页工人线程适合于那些纯数据的,或者与浏览器 UI 没关系的长运行脚本。它看起来用处不大,而网页应用程序中通常有一些数据处理功能将受益于工人线程,而不是定时器。例如解析json即json.parse(…),编码或解码字符串, 复杂数学运算,给一个大数组排序,等超过100ms且不适合用定时器分解。
Ps: chrome浏览器不支持本地的情况下使用webwork, 需要架在服务器上, firefox支持。


七. AJAX异步js和XML

1.如果请求不改变服务器状态只是取回数据(又称作幂等动作)则使用 GET GET 请求被缓冲起来,如果你多次提取相同的数据可提高性能。只有当 URL 和参数的长度超过了 2'048 个字符时才使用 POST 提取数据。因为 Internet Explorer 限制 URL的长度,过长将导致请求(参数)被截断。
2.可以通过动态创建script并添加src属性,无法获得报头,数据而且必须在一个回调函数中组装。 由于是script执行,速度比XMR快
3.减少HTTP请求
4. Beacons 灯标 新建image对象,添加src
5.传输类型: json(快) json-p(快) 字符串自定义分隔符  数组形式(去除属性的对象)最快。  jsonp它将数据视为可运行的 JavaScript 而不是字符串,解析速度极快。它能够跨域使用,但不应涉及敏感数据
6.快速渲染的两种形式: 服务端组装好HTML传给客户端直接渲染,服务端穿json给客户端执行逻辑渲染
7.js字符串操作很慢,但字符串的split操作非常快
8.缓存: 最好的XHR优化就是不发出XHR,1.在服务器端,设置 HTTP 头,确保返回报文将被缓存在浏览器中。 2. 在客户端,设置一个 Expires 头,于本地缓存已获取的数据,不要多次请求同一个数据。
    如果你希望 Ajax 响应报文能够被浏览器所缓存,你必须在发起请求时使用 GET 方法
9.CDN优化。

八.最佳实践
1.不要使用eval或Function,setTimeout等通过传入字符串产生函数
2.使用数组或者对象字面量来创建,速度非常快
3.避免进行重复的操作
4.可以的情况下,进行位操作的方式来处理数字,因为它是在原生层面产生作用的
5.尽量使用js的原生api,因为它是在c++低级语言上编译的,速度比任何js封装的代码快的多。

九.构建和部署
1.合并js文件,减少HTTP请求的数量
2.YUI 压缩器减少js代码大小,压缩形式提供js文件(gzip)
3.设置HTTP头可缓存,通过文件名附加时间戳控制
4.使用CDN


十.学会使用各种浏览器(chrome,firefox,safari,IE等)的调试 

    

猜你喜欢

转载自blog.csdn.net/weixin_40821790/article/details/79387314