《高性能JavaScript》读书笔记

加载和执行

  • 所有的script放在</body>之前
  • 合并脚本,尽量减少script,因为每个script就是一次http请求
  • 内嵌的javascript不能放在加载CSS的link之后
  • 无阻塞脚本,使用scriptdefer属性
  • 动态创建script元素来下载并执行代码,动态创建的建议放在head里

数据存取

  • 能用局部变量就用局部变量,因为查找作用域链越深越消耗性能
  • 若是多处使用某个全局属性,应该把全局属性赋值给一个局部
  • 对象的属性比字面量消耗性能,再多处使用某对象的属性,应该将其赋值给局部变量,若是方法则不需要,因为可能会改变内部方法的this指向
  • 尽量不要用闭包,闭包应及时释放
  • 不要使用with语句,
  • 嵌套的对象成员会影响性能,尽量少用
  • 访问字面量和局部变量的速度最快,访问数组元素和对象成员相对较慢

DOM编程

  • 减少DOM操作
  • 计算在js完成,之后再修改DOM

性能差的

function innerHTMLLoop(){
    for(let count = 0; count < 15000; count++){
        document.getElementById('here').innerHTML += 'a';
    }
}

性能较好的

function innerHTMLLoop(){
    let content = '';
    for(let count = 0; count < 15000; count++){
        content += 'a';
    }
    document.getElementById('here').innerHTML += content;
}
  • 大多数情况下,节点克隆效率比创建元素高

  • HTML集合是类数组,拥有length和Index,但没有push

  • HTML集合是实时更新的,所以在遍历时,添加元素后,长度会自动发生变化

这是一个死循环

let alldivs = document.getElementsByTagName('div');
for(let i = 0; i < alldivs.length; i++){
    document.body.appendChild(document.createElement('div'));
}
  • HTML集合的性能是昂贵的,可以将length属性缓存,或将集合转化为真数组,不同场合考虑不同用法

  • 需要多次访问DOM属性或方法时,将其赋值给局部变量

  • 在IE中,查找DOM节点时,使用nextSibling()

  • 使用children会比childNodes效率高

  • 选择器API使用querySelectorAllquerySelector,比document.getElementById()这些更有效率,而且返回的是NodeList,这是一个类数组,而且不会实时对应文档结构

  • 较少重绘和重排

重排和重绘

重排是浏览器重新渲染,重绘只绘制一部分DOM元素

触发重排的条件

  • 添加或删除可见DOM
  • 元素位置改变
  • 元素尺寸改变(包括:外边距、内边距、边框厚度、高度、宽度等属性改变)
  • 内容改变,例如文本改变或图片被另一个不同尺寸的图片改变
  • 页面渲染器初始化
  • 浏览器窗口尺寸改变

触发重绘的条件

例如,背景色发生变化,颜色发生变化

在元素脱离文档流时,改变元素大小不会触发重排

  • 较少重复修改CSS样式

性能差的

const el = document.getElementById('mydiv');
el.style.borderLeft = '1px';
el.style.borderRight = '2px';
el.style.padding = '5px';
//发生了三次重排

性能较好的

const el = document.getElementById('mydiv');
el.style.cssText = 'border-left: 1px; border-right: 2px; padding: 5px;';
//只会修改DOM一次

使用cssText会覆盖已存在的样式信息,如果不想覆盖可以这样做

el.style.cssText += '; border-left: 1px;';

批量修改DOM

当需要对DOM元素进行一系列操作时,可以利用以下方法,减少重绘和重排

  • 隐藏元素,应用修改,重新显示
  • 使用文档片段在当前DOM之外构建一个子数,再把它拷贝到文档
  • 将原始元素拷贝到一个脱离文档的节点中,修改副本,完成后替换原始元素

第一种方法

给元素设置display: none,修改元素的一系列操作,元素显示display: block

第二种方法

const fragment = document.createDocumentFragment();
appendDataToElement(fragment, data); // 一系列元素操作
document.getElementById('mylist').appendChild(fragment)

第三种

const old = document.getElementById('mylist');
const clone = old.cloneNode(true);
appendDataToElemnet(clone, data);
old.parentNode.replaceChild(clone, old);

推荐使用第二种,因为发生的重排最少

  • 较少使用会使队列刷新的方法
offsetTop | offsetLeft | offsetWidth | offsetHeight
scrollTop | scrollLeft | scrollWidth | scrollHeight
clientTop | clientLeft | clientWidth | clientHeight
getComputedStyle() (computedStyle in IE)
  • 需要动画的DOM,使其脱离文档流

使用绝对位置定位页面的动画元素,使其脱离文档流

元素执行动画

执行完毕,恢复位置

  • 在很长的列表或表格时,避免使用:hover

  • 能使用事件委托的就使用事件委托

算法和流程控制

  • 较少的条件分支,使用if,较多时使用switch
  • 在遍历时,知道长度的使用for循环,不要使用for...in,不知道长度时才使用for...in
  • 循环之前,把元素长度赋值给局部变量,避免每次循环查找长度
  • 倒循环的性能比正循环要好
  • 函数迭代(forEach)性能比for循环差

快速响应的用户界面

用于执行js和更新用户界面的线程,称为浏览器UI线程,该线程要么执行js代码,要么执行UI更新,因此当js执行过长时,会影响UI的更新,比如点击按钮,Js执行过长时,无法看到按钮被按下的样式

  • js执行时间不要超过100毫秒,否则用户会觉得与界面失联了
  • 定时器的时间最少是25毫秒,再少由于各个浏览器的实现机制的问题,会引发其他不可预测的问题
  • 当不需要操作DOM的时候,而且是复杂的计算时,如编码/解码大字符串,复杂数学运算(包括图像或视频处理),大数组排序等,使用Worker。任何操作100毫秒的处理过程,都应该考虑Worker

其他

创建数组或对象时,使用字面量

const o = {
    name: 'zhangsan'
}
const arr = [1, 2]

运算时使用位运算,会使性能提高很多

使用原生方法,原生方法是效率最高的方法

posted @ 2020-05-04 21:46  司徒炼  阅读(234)  评论(0编辑  收藏  举报