读高性能JavaScript编程 第三章

时间:2021-07-27 16:34:10

第三章  DOM Scripting 

  1. 最小化 DOM 访问,在 JavaScript 端做尽可能多的事情。
  2. 在反复访问的地方使用局部变量存放 DOM 引用.
  3. 小心地处理 HTML 集合,因为他们表现出“存在性”,总是对底层文档重新查询。将集合的 length 属性缓
  4. 存到一个变量中,在迭代中使用这个变量。如果经常操作这个集合,可以将集合拷贝到数组中。
  5. 如果可能的话,使用速度更快的 API,诸如 querySelectorAll()和 firstElementChild。
  6. 注意重绘和重排版;批量修改风格,离线操作 DOM 树,缓存并减少对布局信息的访问。
  7. 动画中使用绝对坐标,使用拖放代理。
  8. 使用事件托管技术最小化事件句柄数量。
  9. 上面八条建议都是抄的。

dom 文档对象模型 类似的还有 bom 浏览器对象模型 。

要使用一个 对象首先要知道该对象存在的位置,比如 document 对象 它并不是被定义在 ECMAScript 中而是 dom 中。所以 本来你每次访问 document就很慢。

引用:

  文档对象模型(DOM)是一个独立于语言的,使用 XML和 HTML 文档操作的应用程序接口(API)。
在浏览器中,主要与 HTML 文档打交道,在网页应用中检索 XML 文档也很常见。DOM APIs 主要用于访
问这些文档中的数据。

  尽管 DOM 是与语言无关的 API,在浏览器中的接口却是以 JavaScript 实现的。客户端大多数脚本程序

与文档打交道,DOM 就成为 JavaScript 代码日常行为中重要的组成部分。

  浏览器通常要求 DOM 实现和 JavaScript 实现保持相互独立。 例如, 在 Internet Explorer中, 被称为 JScript

的 JavaScript实现位于库文件 jscript.dll中,而 DOM 实现位于另一个库 mshtml.dll(内部代号 Trident)。

两个独立的部分以功能接口连接就会带来性能损耗,这就是为什么会有上面8条建议。

注意:

  1、html集合的存在性

eg:document.getElementsByName()  返回一个类数组对象,这个对象就是 HTML集合。

eg:

 // an accidentally infinite loop
var alldivs = document.getElementsByTagName_r('div');
for (var i = 0; i < alldivs.length; i++) {
document.body.appendChild(document.createElement('div'))
}

说明:这段代码看上去只是简单地倍增了页面中 div元素的数量。它遍历现有 div,每次创建一个新的 div并附

加到 body上面。但实际上这是个死循环,因为循环终止条件 alldivs.length 在每次迭代中都会增加,它反
映出底层文档的当前状态。

其实这里还有第二个问题,如果循环里没有做任何操作,这是一个正常的循环,但它还是存在性能问题,因为目标是一个HTML集合。

简单说就是每次循环 都会访问 length 属性,而length属性的值是通过查询文档的操作得到的。每获取一次length都会重新查询一次文档,以保证获取到的数据是最新的,上面有一个现成的例证。更要命的是查询文档这个操作是天生就慢的。原因就在上面,有一段话可以加深印象:

  一个很形象的比喻是把 DOM 看成一个岛屿,把 JavaScript(ECMAScript)看成另一个岛屿,两者之间以一座收费桥连接(参见 John Hrvatin,微软,MIX09,http://videos.visitmix.com/MIX09/T53F)。每次 ECMAScript 需要访问 DOM 时,你需要过桥,交一次“过桥费”。

所以,在循环的时候 把 length缓存到一个变量里最好。

如果把 alldivs 复制到一个数组内 例如 :

 function toArray(coll) {
for (var i = 0, a = [], len = coll.length; i < len; i++) {
a[i] = coll[i];
}
return a;
}

可以更优的解决问题,如果你需要在循环中多次操作item的话(建议4)。 同时 len = coll.length 这样写是非常值得学习的,但是在C#中就不晓得有没有益了。

  2、使用速度更快的api,这是一种更优的替代解决方案 eg:  var elements = document.getElementById('menu').getElementsByTagName_r('a');  elements =

toArray(elements); 和 var elements = document.querySelectorAll('#menu a'); 
建议5:这两个函数都是 DOM 节点的属性,所以你可以使用 document.querySelector('.myclass')来查询整个文档中的节点,或者使用 elref.querySelector('.myclass')在子树中进行查询,其中 elref是一个 DOM 元素的引用。 3、 重绘和重拍版引用原文的解释:

  当浏览器下载完所有页面 HTML标记,JavaScript,CSS,图片之后,它解析文件并创建两个内部数据
结构:    A DOM tree       表示页面结构   A render tree  表示 DOM 节点如何显示 。

  渲染树中为每个需要显示的 DOM 树节点存放至少一个节点(隐藏 DOM 元素在渲染树中没有对应节
点)。渲染树上的节点称为“框”或者“盒”,符合 CSS 模型的定义,将页面元素看作一个具有填充、边距、
边框和位置的盒。一旦 DOM 树和渲染树构造完毕,浏览器就可以显示(绘制)页面上的元素了。

  当 DOM 改变影响到元素的几何属性(宽和高)——例如改变了边框宽度或在段落中添加文字,将发生
一系列后续动作——浏览器需要重新计算元素的几何属性,而且其他元素的几何属性和位置也会因此改变
受到影响。浏览器使渲染树上受到影响的部分失效,然后重构渲染树。这个过程被称作重排版。重排版完
成时,浏览器在一个重绘进程中重新绘制屏幕上受影响的部分。

  不是所有的 DOM 改变都会影响几何属性。例如,改变一个元素的背景颜色不会影响它的宽度或高度。
在这种情况下,只需要重绘(不需要重排版),因为元素的布局没有改变。

例子:(有可能根本不会出现,只为分析问题使用)
 // setting and retrieving styles in succession
var computed,
tmp = '',
bodystyle = document.body.style;
if (document.body.currentStyle) { // IE, Opera
computed = document.body.currentStyle;
} else { // W3C
computed = document.defaultView.getComputedStyle(docume
}
// inefficient way of modifying the same property
// and retrieving style information right after
bodystyle.color = 'red';
tmp = computed.backgroundColor;
bodystyle.color = 'white';
tmp = computed.backgroundImage;
bodystyle.color = 'green';
tmp = computed.backgroundAttachment; PK: bodystyle.color = 'red';
bodystyle.color = 'white';
bodystyle.color = 'green';
tmp = computed.backgroundColor;
tmp = computed.backgroundImage;
tmp = computed.backgroundAttachment; //winner
bodystyle.color 虽然和 computed.backgroundColor等三个属性无关,但是 浏览器仍要刷新渲染队列并重排版,注意computed被查询了。(如果把computed缓存到变量里就不会有这种情况)
这也是为什么 后者PK胜出了。
还有一种情况更加具有画面感,
 var el = document.getElementById('mydiv');
el.style.borderLeft = '1px';
el.style.borderRight = '2px';
el.style.padding = '5px';

这段代码使浏览器排版了三次。只不过速度比较快感官上只改变了一次,但是可以想象到绘制三次的画面。

(注意:现在大多数浏览器都优化了这种情况,只排版一次。)

如同下面代码一样:

 var el = document.getElementById('mydiv');
el.style.cssText = 'border-left: 1px; border-right: 2px; padding: 5px;'; el.style.cssText += '; border-left: 1px;'; //or el.className = 'active'; //or

  离线dom 很好理解,就是在 操作dom的时候不产生 重绘、重排,操作完成后一次性 ‘提交’。

离线dom的三种方式: 1、隐藏 and 操作 and 显示 eg:

 var ul = document.getElementById('mylist');
ul.style.display = 'none';
appendDataToElement(ul, data);
ul.style.display = 'block';

2、创建并更新一个文档片断,然后附加或替换,eg:

 var fragment = document.createDocumentFragment();
appendDataToElement(fragment, data);
document.getElementById('mylist').appendChild(fragment);

3、制作副本,替换原节点eg:

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

缓存并减少对布局信息的访问就是类似 于提取局部变量,只不过布局信息这类数据的访问影响比较大。

4、动画

  1. 使用绝对坐标定位页面动画的元素,使它位于页面布局流之外。
  2. 比如一个扩大和缩小的动画,当扩大使其覆盖原有布局,重绘这一部分而不会重排、重绘一大部分页面。
  3. 动画结束时,重新定位。 其他元素是一起被复位的。

5、事件托管技术最小化事件句柄数量

这里说自己的理解有可能是错误的,暂时先这样理解了:

首先 每个事件都有三个阶段:

• Capturing
捕获
• At target
到达目标
• Bubbling
冒泡

在onload(或DOMContentReady) 事件里  会让 很多元素和很多事件句柄 挂接/附加 (attached) ,而 有一些挂接是不必的。比如说一些根本不会被点到的按钮。

引用:

一个简单而优雅的处理 DOM 事件的技术是事件托管。它基于这样一个事实:事件逐层冒泡总能被父元
素捕获。采用事件托管技术之后,你只需要在一个包装元素上挂接一个句柄,用于处理子元素发生的所有
事件。

读高性能JavaScript编程 第三章的更多相关文章

  1. 读高性能JavaScript编程学英语 第一章第三页第一段话

    When the browser encounters a <script> tag, as in this HTML page, there is no way of knowing w ...

  2. 读高性能JavaScript编程 第四章 Conditionals

    if else 和 switch    &&    递归 if else 和 switch 一般来说,if-else 适用于判断两个离散的值或者判断几个不同的值域.如果判断多于两个离散 ...

  3. 读高性能JavaScript编程 第四章 Duff&&num;39&semi;s Device

    又要开始罗里吧嗦的 第四章  Summary 了. 这一次我尽量精简语言. 如果你认为 重复调用一个方法数次有点辣眼睛的话 比如: function test(i){ process(i++); pr ...

  4. 读高性能JavaScript编程 第二章 让我知道了代码为什么要这样写

    代码为什么要这样写? function initUI(){ var doc = document, bd = doc.body, links = doc.getElementsByTagName_r( ...

  5. 读高性能JavaScript编程 第一章

    草草的看完第一章,虽然看的是译文也是感觉涨姿势了, 我来总结一下: 由于 大多数浏览器都是 single process 处理 ui updatas and js execute 于是产生问题: js ...

  6. 列表的实现-----数据结构与算法JavaScript描述 第三章

    实现一个列表 script var booklist = new List(); booklist.append('jsbook'); booklist.append('cssbook'); book ...

  7. 读《编写可维护的JavaScript》第二三章总结

    第二章 注释 添加注释的一般原则是,在需要让代码变得清晰时添加注释. 2.1 ① 单行注释 独占一行的注释,用来解释下一行代码.这行注释之前总是有一个空行,且缩进层级和下一行代码保持一致. 在代码行的 ...

  8. &lbrack;转&rsqb;Windows Shell 编程 第三章 【转自:http&colon;&sol;&sol;blog&period;csdn&period;net&sol;wangqiulin123456&sol;article&sol;details&sol;7987901】

    第三章 操作文件 我依然清楚地记得,Windows95 的贝塔版出现的情形,它在朋友之间和学院中传播,好酷,全新的文件管理器,一种全图标,全彩色可客户化的界面,以及活泼的动画标识使得在文件拷贝和删除方 ...

  9. 高性能JavaScript 编程实践

    前言 最近在翻<高性能JavaScript>这本书(2010年版 丁琛译),感觉可能是因为浏览器引擎的改进或是其他原因,书中有些原本能提高性能的代码在最新的浏览器中已经失效.但是有些章节的 ...

随机推荐

  1. Activity的生命周期

    Activity的生命周期 以往我们实现页面间的跳转都是实例化Intent类的对象,但是页面在我们眼前的出现与消失没有我们所看到的那么简单,它有一个复杂的生命周期,一个页面的出现,被覆盖,再次出现,被 ...

  2. &lbrack;知识点&rsqb;状态压缩DP

    // 此博文为迁移而来,写于2015年7月15日,不代表本人现在的观点与看法.原始地址:http://blog.sina.com.cn/s/blog_6022c4720102w6jf.html 1.前 ...

  3. PHP MySQL 读取数据

    PHP MySQL 读取数据 从 MySQL 数据库读取数据 SELECT 语句用于从数据表中读取数据: SELECT column_name(s) FROM table_name 如需学习更多关于 ...

  4. easyui datagrid 单元格编辑 自动聚焦 、全选

    $.extend($.fn.datagrid.methods, { editCell: function (jq, param) { return jq.each(function () { var ...

  5. fiddler 路由设置

    REGEX:^http://data.51buy.com/biz/oppmsmobile/web/js/app/(.+)/(.+).js E:\svns\new\dev\webapp\data_ics ...

  6. &lt&semi;iOS 组件与框架&gt&semi; -- UIKit Dynamics

    UIKit Dynamics 结合 『iOS 组件与框架 』一书.总结的知识点与demo demo 地址: GitHub地址 一.概述 1.UIKit Dynamics 是 iOS 7 新增的内容.其 ...

  7. &lbrack;Swift&rsqb;LeetCode741&period; 摘樱桃 &vert; Cherry Pickup

    In a N x N grid representing a field of cherries, each cell is one of three possible integers. 0 mea ...

  8. BP神经网络 详解模板

    %原始数据输入 P=[ - 6.142 - 27.5 5.068 - 31.7 5.196 - 34.1 6.362 - 31.54 6.472 - 30.17 6.578 - 29.53 6.351 ...

  9. 【C&sol;C&plus;&plus;】实现龙贝格算法

    1. 复化梯形法公式以及递推化 复化梯形法是一种有效改善求积公式精度的方法.将[a,b]区间n等分,步长h = (b-a)/n,分点xk = a + kh.复化求积公式就是将这n等分的每一个小区间进行 ...

  10. 深入理解CSS绝对定位absolute

    前面的话 前面已经介绍了定位的偏移和层叠,例子中大量的应用了绝对定位.因为相较于相对定位和固定定位,绝对定位在实际中应用频率更高.应用场景更广泛.本文将介绍使用绝对定位时的具体细节 定义 当元素绝对定 ...