high performence javascript(高性能JavaScript)
2016-05-26 19:43
274 查看
本书推荐指数:4星
无论JavaScript代码是内嵌还是包含在外部文件中,页面的下载和渲染都必须要停下来等待脚本执行完成
浏览器都允许并行下载js文件,但是js下载过程中仍然会阻塞其他资源的下载,比如图片。尽管脚本的下载过程不会相互影响,但页面仍然要等到所有的js代码下载并执行完毕才能继续。
无阻塞的脚本:
defer和async。相同点是采用并行下载,在下载过程中不会阻塞。区别在于执行时机:async是在加载完成后立马自动执行,而defer是并行下载之后再onload事件之前执行。(注意标准规定defer只能在外部脚本中起作用)
动态脚本元素:
文本在被添加到页面时开始下载并立即执行,但不会阻塞页面其他进程。
XMLHttpRequest 脚本注入
使用XMLHttpRequest对象获取脚本并注入到页面中
本地变量:用var定义的数据存储单元
数组元素:存储在js数组对象内部,以数字为索引
对象成员:存储在js对象内部,以字符串为索引
字面量和本地变量的访问速度快于数组项和对象成员的访问速度
如果某个跨作用域的值在函数中被引用一次以上,那么就把它存储到局部变量里。
try-catch语句中catch从句
他们的本质都是把一个新的变量对象插入到作用域链的首部,然后函数的局部变量就处于第二个作用域链对象中,因此访问的代价更高了。
使用with时,一个新的变量对象被创建,它包含了参数指定的对象的所有属性,此对象被推入作用域链的首部。catch从句是把一个错误对象插入到作用域链的首部
操作DOM很慢:把DOM和ECMAScript比作是一个岛屿,它们之间用收费桥梁连接,ECMAScript每次访问DOM都需要成本。
最佳实践:减少访问DOM的次数,把运算尽量留在ECMAScript这一端处理
DOM树:表示页面结构
渲染树:表示DOM节点如何显示
DOM树中的一个节点至少对应渲染树中的一个节点(隐藏的DOM元素在渲染树中没有对应的节点),DOM树和渲染树构建完成,浏览器就开始绘制页面元素
并不是所有的DOM变化都会影响几何属性,比如改变一个元素的背景色并不会影响其位置和大小,这是只会触发重绘而不需要重排
元素位置改变
元素大小改变(如margin、border、padding、width、height改变)
内容改变:文本改变或图片被另一个不同尺寸的图片替代
页面渲染器初始化
浏览器窗口尺寸变化
使元素脱离文档流
对元素应用多重改变
把元素带回文档中
有三种方法使元素脱离文档流
1. 隐藏元素,应用修改,重新显示
2. 使用文档片断(documentFragment),在DOM之外构造一个子树,然后把它拷贝回文档
3. 将原始元素拷贝到一个脱离文档的副本中,修改副本,完成后再替换原始元素
解决办法:
1. 使用定时器分割任务
2. 使用worker在UI线程之外运行代码(创建多线程)
1. eval()
2. function()构造函数
3. setTiemeout()
4. setInterval()
他们允许传入一个js代码字符串并执行它
以上四种方法都是在JavaScript代码中执行另一段JavaScript代码,这回导致双重求值的性能消耗,此代码先会正常求值,然后在执行过程中对包含在字符串中的代码发起另一次求值运算。
按位或
按位异或
按位取反
比如判断奇偶
第一章 加载和执行
浏览器使用单一线程来处理用户界面(UI)刷新和JavaScript脚本,同一时刻只能做一件事。当浏览器在执行JavaScript代码时不能做其他的事情,比如相应用户点击按钮、刷新UI等无论JavaScript代码是内嵌还是包含在外部文件中,页面的下载和渲染都必须要停下来等待脚本执行完成
浏览器都允许并行下载js文件,但是js下载过程中仍然会阻塞其他资源的下载,比如图片。尽管脚本的下载过程不会相互影响,但页面仍然要等到所有的js代码下载并执行完毕才能继续。
无阻塞的脚本:
defer和async。相同点是采用并行下载,在下载过程中不会阻塞。区别在于执行时机:async是在加载完成后立马自动执行,而defer是并行下载之后再onload事件之前执行。(注意标准规定defer只能在外部脚本中起作用)
动态脚本元素:
var script = document.createElement("script"); script.type = "type/javascript"; script.src = "xxx.js"; document.getElementsByTagName("head")[0].appendChild("script");
文本在被添加到页面时开始下载并立即执行,但不会阻塞页面其他进程。
XMLHttpRequest 脚本注入
使用XMLHttpRequest对象获取脚本并注入到页面中
var xhr = new XMLHttpRequest(); xhr.open("get", "file1.js", true); xhr.onreadystatechange() { if(xhr.readyState == 4) { if(xhr.staus >= 200 && xhr.status < 300 || xhr.status == 304) { var script = document.createElement("script"); script.type = "type/javascript"; script.text = xhr.responseText; document.body.appendChild(script); } } }
数据存取
js中有四种基本的数据存取位置:
字面量:字符串、数字、布尔值、对象、数组、函数、正则表达式、null、undefined本地变量:用var定义的数据存储单元
数组元素:存储在js数组对象内部,以数字为索引
对象成员:存储在js对象内部,以字符串为索引
字面量和本地变量的访问速度快于数组项和对象成员的访问速度
标识符解析的性能:
一个标识符所在的位置越深,读取的速度也就越慢。因此函数中读取局部变量总是最快的,而读取全局变量总是最慢的。如果某个跨作用域的值在函数中被引用一次以上,那么就把它存储到局部变量里。
改变作用域链:
withtry-catch语句中catch从句
他们的本质都是把一个新的变量对象插入到作用域链的首部,然后函数的局部变量就处于第二个作用域链对象中,因此访问的代价更高了。
使用with时,一个新的变量对象被创建,它包含了参数指定的对象的所有属性,此对象被推入作用域链的首部。catch从句是把一个错误对象插入到作用域链的首部
DOM 编程
文档对象模型(DOM)是一个独立于语言的,用于操作XML和HTML文档的程序接口,使用DOM API来访问文档中的数据操作DOM很慢:把DOM和ECMAScript比作是一个岛屿,它们之间用收费桥梁连接,ECMAScript每次访问DOM都需要成本。
最佳实践:减少访问DOM的次数,把运算尽量留在ECMAScript这一端处理
只返回元素节点的API
忽略注释和文本节点(通常只是两个节点间的空白)children childNodes childElementCount childNodes.length firstElementChild firstChild lastElementChild lastChild nextElementSibling nextSibling previousElementSibling previousSibling
使用高效的选择器API
querySelector();querySelectorAll();
重绘与重排
浏览器将页面中所有组件下载完成之后会生成两个内部数据结构:DOM树:表示页面结构
渲染树:表示DOM节点如何显示
DOM树中的一个节点至少对应渲染树中的一个节点(隐藏的DOM元素在渲染树中没有对应的节点),DOM树和渲染树构建完成,浏览器就开始绘制页面元素
并不是所有的DOM变化都会影响几何属性,比如改变一个元素的背景色并不会影响其位置和大小,这是只会触发重绘而不需要重排
重排何时发生:
添加或删除可见的DOM元素元素位置改变
元素大小改变(如margin、border、padding、width、height改变)
内容改变:文本改变或图片被另一个不同尺寸的图片替代
页面渲染器初始化
浏览器窗口尺寸变化
最小化重排与重绘
改变样式:使用cssText或css类
var el = document.getElementById("mydiv"); el.style.borderLeft = "1px"; el.style.borderRight = "2px"; el.style.padding = "5px"; //改进:使用cssText var el = document.getElementById("mydiv"); el.style.cssText = "border-left: 1px; border-right: 2px; padding: 5px;"; //另一种改进方式:使用js操作css类,做到js和css分离 var el = document.getElementById("mydiv"); el.className = "xxx"; //el.classList.toggle("xxx");
批量修改DOM元素,减少重排和重绘
步骤:使元素脱离文档流
对元素应用多重改变
把元素带回文档中
有三种方法使元素脱离文档流
1. 隐藏元素,应用修改,重新显示
2. 使用文档片断(documentFragment),在DOM之外构造一个子树,然后把它拷贝回文档
3. 将原始元素拷贝到一个脱离文档的副本中,修改副本,完成后再替换原始元素
创建快速相应的用户界面
js的单个运行任务不要超过100ms,否则会让用户感觉到明显的缓慢解决办法:
1. 使用定时器分割任务
2. 使用worker在UI线程之外运行代码(创建多线程)
编程实战
避免双重求值
js中运行包含代码的字符串有四种方式:1. eval()
2. function()构造函数
3. setTiemeout()
4. setInterval()
他们允许传入一个js代码字符串并执行它
var num1 = 1, num2 = 2, resEval = eval("num1 + num2"); resFunction = new Function("arg1", "arg2", "return arg1 + arg2"); function sum() { console.log(num1 + num2); }; console.log(resEval); console.log(resFunction(num1, num2)); setTimeut(sum, 1000); //使用此种方式而非下一行的方式 setTimeout("sum()", 1000); //setInterval()函数同理
以上四种方法都是在JavaScript代码中执行另一段JavaScript代码,这回导致双重求值的性能消耗,此代码先会正常求值,然后在执行过程中对包含在字符串中的代码发起另一次求值运算。
使用Object/Array直接量而不是对象
对象字面量和数组字面量运行更快,有助于节省代码量避免重复工作
还记得时间处理时候的eventUtil对象吗?addHandler: function(element, type, handler) { if(element.addEventListener) { element.addEventListener(type, handler, false); } else if(element.attachEvent) { element.attachEvent('on'+type, handler); } else { element['on'+type] = handler; } }, /* 上一种方式的问题在于每一次运行addHandler函数的时候都要进行能力检测,然而第二次 检测是完全没有必要的,故采用延迟加载模式,方法在第一次被调用时检查使用哪种事件处理程序。然后原始函数被包含正确操作的新函数覆盖。 addHandler: function(element, type, handler) { //重写函数 if(element.addEventListener) { addHandler = function(element, type, handler) { element.addEventListener(type, handler, false); } } else if(element.attachEvent) { addHandler = function(element, type, handler) { element.attachEvent('on'+type, handler); } } else { addHandler = function(element, type, handler) { element['on'+type] = handler; } } //调用函数 addHandler(element, type, handler); },*/
使用位运算加快速度
按位与&
按位或
|
按位异或
^
按位取反
~
比如判断奇偶
var i = 5; if(i % 2) { //odd console.log("odd"); } else { //even console.log("even"); } //使用位运算判断更快! if(i & 1) { //odd console.log("odd"); } else { //even console.log("even"); } /** 如果函数是奇数,那么其最低位是1,与1&的结果是1(true) 如果函数是偶数,那么其最低位是0,与1&的结果是0(false) */
相关文章推荐
- JQuery1——基础($对象,选择器,对象转换)
- Android学习笔记(二九):嵌入浏览器
- Android java 与 javascript互访(相互调用)的方法例子
- JavaScript演示排序算法
- javascript实现10进制转为N进制数
- 最后一次说说闭包
- Ajax
- 2019年开发人员应该学习的8个JavaScript框架
- HTML中的script标签研究
- 对一个分号引发的错误研究
- 异步流程控制:7 行代码学会 co 模块
- ES6 走马观花(ECMAScript2015 新特性)
- JavaScript拆分字符串时产生空字符的原因
- Canvas 在高清屏下绘制图片变模糊的解决方法
- Redux系列02:一个炒鸡简单的react+redux例子
- JavaScript 各种遍历方式详解
- call/apply/bind 的理解与实例分享