Javascript性能优化阅读笔记

第一章 加载和执行

大多数浏览器都是用单一进程处理UI界面的刷新和JavaScript的脚本执行,因此同一时间只能作一件事,Javascript执行过程耗时越久,浏览器等待响应的时间就越长。javascript

因此,HTML页面在遇到php

无阻塞的脚本

尽管减小Javascript文件的大小并限制HTTP请求次数仅仅只是第一步,下载单个较大的Javascript脚本执行也许要锁死大量的事件,因此无阻塞的脚本的意义在于页面加载完成以后再下载脚本。css

延迟的脚本

<script defer> 
这是告知,延迟脚本内的内容不会更改DOM,只有IE 4+和Firefox 3.5+浏览器支持。html

defer意味着脚本会先下载,但只有到DOM加载完成以前才会执行,不与页面的其余资源冲突,可并行。java

动态脚本元素

 

XMLHttpRequest脚本注入

这种方法的优势是,能够下载Javascript代码但不当即执行,并且几乎适用全部主流浏览器。node

局限性在于Javascript文件必须与所请求的页面处于相同的域,因此Javascript文件不能从CDN下载。web

推荐的无阻塞模式

向页面中添加大量的JavaScript的推荐作法只需两步:先添加动态加载所须要的代码,再加载初始化页面所须要的代码。ajax

前者代码精简,执行很快。正则表达式

<script type="text/javascript"> function loadScript(url, callback) { var script = document.createElement("script"); script.type = "text/javascript"; if (script.readyState) { script.onreadstatechange = 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("the-rest.js", function() { Application.init(); }); </script>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

第二章 数据存取

数据的存储位置不一样,代码执行时的数据检索速度也不一样。算法

对于Javascript来讲,有下面四种基础的数据存取位置。

字面量 
字面量只表明自身,不存储在特定位置。JS中的字面量有:字符串、数字、布尔值、对象、数组、函数、正则表达式,以及特殊的null和undefined值。

本地变量 
开发人员使用关键字var定义的数据存储单元。

数组元素 
存储在JS数组对象内部,以数字做为索引。

对象成员 
存储在JS对象内部,以字符串做为索引。

管理做用域

做用域概念对于理解Javascript相当重要,不只在性能方面,还在功能方面。

做用域链和标识解析

每一个函数都是Function的实例,Function对象与其余对象同样,拥有能够编写的属性以及,一系列只供JavaScript引擎存储的内部属性,其中一个是[[scope]]。

[[scope]]包含了一个函数被建立的做用域中对象的集合。

在函数执行过程当中,每遇到一个变量,都会经历一次标识符解析过程以及从哪里获取存储数据。若是当前执行环境(做用域头)找不到该变量,就搜索下一个做用域,若是都找不到则为undefined。

标识符解析的性能

标识符所在的位置越深,读写速度也就越慢。因此,读取局部变量时最快的,而读取全局变量时最慢的。

对此有个经验法则:若是某个跨做用域的值在函数中被引用一次以上,那么就把它存储到局部变量里。

改变做用域链

通常来讲,一个执行环境的做用域链是不会改变的。可是,有两个语句能够在执行时临时改变做用域链。

一个是with语句,另外一个是try-catch语句。

with(context),with语句有一个问题,那就是with()里的参数做为做用域链头后,函数的局部变量都会变成第二个做用域链对象中,这样每次访问都得访问两次。

try-catch的catch子句也有这种效果,它把一个异常对象推到做用域链首部,可是子句执行完毕,做用域链就会返回以前的状态。

动态做用域

with、try-catch和eval()都是动态做用域。

闭包、做用域和内存

闭包是JS最强大的特性之一,它容许函数访问局部做用于以外的数据。

一般执行环境一销毁,活动对象也应该销毁,可是由于闭包中,对活动对象的引用依旧存在,因此活动对象并不会被销毁,所以也须要更高的内存开销。

对象成员

原型

JS对象基于原型,实例属性proto指向原型对象且只对开发者可见。

hasOwnProperty()区分原型属性和实例属性。

原型链

很少解释。

嵌套成员

不太常见的写法:window.location.href,嵌套成员会致使JS引擎搜索全部对象成员。嵌套得越深,读取速度就会越慢。

缓存对象成员值

很少解释

小结

在JS中,数据存储的位置会对代码总体性能产生重大的影响。数据存储有4种方法:字面量、变量、数组项和对象成员。它们有着各自的性能特色。

  • 访问字面量和局部变量的速度最快,相反,访问数组元素和对象成员相对较慢。
  • 访问局部变量比访问跨做用域变量更快。变量在做用域链中的位置越深,访问时间越长。
  • 避免使用动态做用域链
  • 嵌套的对象成员会明显影响性能,少用
  • 属性或方法在原型链中位置越深,访问速度越慢
  • 可把经常使用元素保存在局部变量中改善性能

第三章 DOM编程

首先必须先明确一点:用脚本进行DOM操做的代价很是昂贵。

DOM至关于浏览器HTML文档,XML文档与JS的程序接口(API),与语言无关。因此DOM与JS之间的交流消耗费用也就越高。

DOM访问与修改

通用的经验法则:减小访问DOM的次数,把运算尽可能留在ECMAScript这端来处理。

innerHTML与DOM方法的对比

innerHTML非标准可是支持性良好,在老浏览器中innerHTML比DOM更加高效,可是innerHTML最好与数组结合起来使用。这样效率会更高。

节点克隆

element.cloneNode()方法克隆节点。

HTML集合

HTML集合是包含了DOM节点引用的类数组对象。如下方法的返回值就是一个集合。

类数组对象没有push.slice等方法,可是有length属性且可遍历。

HTML集合与文档时刻保持链接,因此须要最新的消息须要时刻查询。

昂贵的集合

在循环语句中读取数组的length是不推荐的作法,最好是把数组的长度存储在一个局部变量中。

访问集合时使用局部变量

第一优化原则是把集合存储在局部变量中,并把length缓存在循环外部,而后用局部变量替代这些须要屡次读取的元素。

举个例子:

// 较慢 function collectionGlobal() { var coll = document.getElementsByTagName('div'), len = coll.length, name = ''; for (var count = 0; count < len; count++) { name = document.getElementsByTagName('div')[count].nodeName; name = document.getElementsByTagName('div')[count].nodeType } } // 很快 function collectionGlobal() { var coll = document.getElementsByTagName('div'), len = coll.length, name = '', el = null; for (var count = 0; count < len; count++) { el = coll[count]; name = el.nodeName; name = el.nodeType } }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

遍历DOM

获取DOM元素

一般你须要从某一个DOM元素开始,操做周围的元素,或者递归查找全部子节点。你可使用childNodes获得元素集合,或者用nextSibling来获取每一个相邻元素。

元素节点

DOM元素属性诸如childNodes,firstChild和nextSibling并不区分元素节点和其余类型节点,若是要过滤的话,实际上是没必要要的DOM操做。

如今能区分元素节点和其余节点的DOM属性以下:

属性名 被替代的属性
children childNodes
childElementCount childNodes.length
firstElementChild firstChild
lastElementChild lastChild
nextElementSibling nextSibling
previousElementSibling previousSibling

children属性的支持率较高。

选择器API

document.querySelectorAll(‘#menu a’); 
document.querySelector(”) 选择匹配的第一个节点

重绘与重排(Repaints and Reflows)

浏览器下载完页面中的全部组件,以后会解析并生成两个内部数据结构:

DOM树 
表示页面结构

渲染树 
表示DOM节点如何显示

DOM树中全部须要显示的节点在渲染树中至少存在一个对应的节点(隐藏的DOM元素在渲染树中没有对应的节点)。

一旦DOM树与渲染树构建完成,浏览器就开始绘制页面元素。

重排

当DOM的变化影响了元素的几何属性(宽和高)——好比改变边框宽度或给段落增长文字,致使行数增长——浏览器须要从新计算元素的几何属性,一样其余元素的集合属性位置也会所以受到影响。浏览器会使渲染树中受到影响的部分失效,并从新构造渲染树,这个过程被称为重排。

重绘

完成重排后,浏览器会从新绘制受影响的部分到屏幕中,这个过程被称为重绘。有些样式的改变,好比背景的变化不影响到布局,因此只会重绘。

渲染树变化的排队和刷新

因为每次重排都会产生计算消耗,大多数浏览器经过队列化修改并批量执行来优化重排过程。然而,你可能会不自以为强制刷新队列并要求计划任务马上执行。获取布局信息的操做会致使列队刷新,好比: 
- offsetTop,offsetLeft… 
- scrollTop,scrollLeft… 
- clientTop,clientLeft… 
- getComputedStyle() 
由于以上方法要求返回最新的布局信息,因此浏览器不得不把“待处理变化”触发重排。

最小化重绘和重排

  • el.style.cssText = "";修改样式信息。
  • 修改css的class名称 el.className = "";

批量修改DOM

当你须要对DOM元素进行一系列操做时,能够经过如下步骤来减小重绘和重排的次数: 
1.使元素脱离文档流 
2.对其应用多重改变 
3.把元素带回文档

有三种方法可使DOM脱离文档: 
- 隐藏元素,应用修改,从新显示 
- 使用文档片断在当前DOM以外构建一个子树,再把它拷贝回文档 
- 将原始元素拷贝到一个脱离文档的节点中,修改副本,完成后再替换原始元素

举个例子:

<ul id="mylist"> <li><a></a></li> <li><a></a></li> <li><a></a></li> </ul> // 假设要将更多的数据插入到这个列表中 var data = [ { "name": "Nicholas", "url": "...." } ] // 用来更新指定节点数据的通用函数 function appendDataToElement(appendToElement, data) { var a, li; for (var i = 0, max = data.length; i < max; i++) { a = document.createElement('a'); a.href = data[i].url; a.appendChild(document.createTextNode(data[i].name)); li = document.createElement('li'); li.appendChild(a); appendToElement.appendChild(li); } } // 不考虑重排 var ul = document.getElementById('mylist'); appendDataToElement(ul, data); // 第一种方法 var ul = document.getElementById('mylist'); ul.style.display = 'none'; appendDataToElement(ul, data); ul.style.display = 'block'; // 第二种方法(推荐) var fragment = document.createDocumentFragment(); appendDataToElement(fragment, data); document.getElementById('mylist').appendChild(fragment); // 第三种方法 var old = document.getElementById('mylist'); var clone = old.cloneNode(true); appendDataToElement(clone, data); old.parentNode.replaceChild(clone, old);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47

缓存布局信息

对于须要操做布局信息的地方,最好用一个局部变量来缓存,否则查询一次就会刷新一次渲染队列并应用全部变动。

让元素脱离动画流

通常而言动画的展开与隐藏会影响大量元素的重排,使用一下步骤能够避免页面中大部分重排。

  • 使用绝对位置定位页面上的动画元素,使其脱离文档流。
  • 让元素动起来。当它扩大时,会临时覆盖部分页面,但这只是页面一个小区域的重绘。
  • 当动画结束时恢复定位,从而只会下移一次文档的其余元素。

IE和:hover

尽可能避免使用hover。

事件委托

绑定的事件越多,代价也越大,要么加剧了页面负担,要么是增长了运行期的执行时间。 
在父元素绑定个事件,利用冒泡。

小结

访问和操做DOM是现代Web应用的重要部分。但每次穿越链接ECMAScript和DOM两个岛屿之间的桥梁,都会被收取“过桥费”。 
- 最小化DOM访问次数,尽量在JavaScript端处理。 
- 若是须要屡次访问某个DOM节点,请使用局部变量存储它的引用。 
- 当心处理HTML集合,由于它实时链接着底层文档。把集合的长度缓存到一个变量中,并在迭代中使用它,若是须要常常操做集合,建议把它拷贝到一个数组中。 
- 若是可能的话,使用速度更快的API 
- 要留意重绘和重排,批量修改样式时,“离线”操做DOM树,使用缓存,减小访问布局信息的次数 
- 动画中使用绝对定位,使用拖放代理 
- 使用事件委托来减小事件处理器的数量

第四章 算法和流程控制

代码的数量不是影响代码运行速度的必然因素,代码的组织结构和解决具体问题的思路才是。

循环

大多数编程语言中,代码执行时间都消耗在循环中。

循环的类型

for循环 
while循环 
do-while循环 
for-in循环

循环性能

不断引起循环性能争论的源头是循环类型的选择。在JS提供的四种循环类型中,只有for-in明显比其余的慢。

优化循环的第一步就是减小对象成员和数组项的查询次数。好比把数组长度赋值给一个局部变量。

第二步是颠倒数组的顺序,一样能够提高循环性能。

第三步是减小迭代次数,达夫设备。 
达夫设备其实是把一次迭代操做展开成屡次迭代操做。

思路是,每次循环中最多可调用8次process(),若是有余数,则表示第一次process()执行几回。

var iterations = Math.floor(items.length / 8), startAt = items.length % 8, i = 0; do { switch(startAt) { case 0: process(items[i++]); ... ... case 8: process(items[i++]); } startAt = 0; } while (--iterations);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

达夫设备对于1000次以上的循环有很大的提高。

基于函数的迭代

Array.forEach(function(value, index, array){ process(value); })
  • 1
  • 2
  • 3

条件语句

if-else对比switch

条件数量少时用if-else,多时用switch。

优化if-else

目标:最小化到达正确分以前所需判断的条件数量。

最简单的优化方法是确保最可能出现的条件放在首位。

还有一种是增长if-else的嵌套,尽量减小判断次数。

查找表

当有大量离散值须要测试时,或者条件语句数量很大时,JS能够经过数组和普通对象来构建查找表,速度要快得多。

switch(value) { case 0: return result0; case 1: return result1; } var results = [results0, results1] return results[index]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

递归

阶乘就是用递归实现的,可是递归的问题在于终止条件不明确或缺乏终止条件会致使函数长时间运行,并且可能会遇到浏览器的“调用栈大小限制”。

调用栈限制

浏览器有栈限制。

递归模式

有两种递归模式。直接递归和隐伏模式。

迭代

任何递归能实现的算法一样能够用迭代来实现。

Memoization

把计算结果缓存,运行前先判断。

functiom memfactorial(n) {
    if (!memfactorial.cache) { memfactorial.cache = { "0": 1, "1": 1 } } if (!memfactorial.cache.hasOwnProperty(n)) { memfactorial.cache[n] = n * memfactorial(n-1) } return memfactorial.cache[n]; }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

小结

  • for/while/do-while循环性能特性至关,并无一种循环类型明显快于或慢于其余类型。
  • 避免使用for-in循环,除非你须要遍历一个属性数量未知的对象
  • 改善循环性能的最佳方式是减小每次迭代的运算量和减小循环迭代次数。
  • 一般来讲,switch老是比if-else快,但并不老是最佳决绝方案。
  • 再判断条件较多时,使用查找表比if-else和switch快
  • 浏览器的调用栈大小限制了递归算法在JavaScript中的应用;栈溢出错误会致使其余代码中断运行。
  • 若是你遇到栈溢出错误,可将方法改成迭代算法,或使用Memoization来避免重复计算。

运行的代码量数量越大,使用这些策略所带来的性能提高也就越明显。

第六章 快速响应的用户界面

浏览器UI线程

UI线程把一个个JS或者UI渲染任务放到队列中逐个执行,最理想的状况就是队列为空,这样任务能够即刻执行。

浏览器限制

浏览器对JS任务的执行时间有限制,从栈大小限制和运行时间两方面来限制。

多久才算“过久”

“若是JS运行了整整几秒钟,那么极可能是你作错了什么….” 
单个JS文件的操做总时间不能超过100毫秒。

使用定时器让出事件片断

若是100毫秒内不能解决JS任务,那么就把线程让出来执行UI渲染。

定时器基础

setTimeout()和setInterval()会告诉JS引擎等待一段时间,而后添加一个JS任务到UI队列。

定时器的精度

定时器不可用于测量实际时间,有几毫秒的误差。

使用定时器处理数组

若是第四章的循环优化仍是没有将JS任务缩短到100毫秒之内,那么下一步的优化步骤就是定时器。

分割任务

能够的话,将一个大任务分割成无数小任务。

记录代码运行时间

不超过50毫秒的JS任务是很是好的用户体验,可是有时候一次性只执行一个任务,这样执行效率反而不高。

定时器与性能

多个重复的定时器同时建立每每会出现性能问题。间隔在1s或者1s以上的重复定时器不会影响Web应用得响应速度。

Web Workers

Web Workers是HTML5最初的一部分,它的出现意味着JS任务能够单独分离出去而不占用浏览器的UI渲染。

Worker运行环境

Web Workers不能处理UI进程,这就意味着它不能接触不少浏览器的资源。

Web Workers的运行环境由以下部分组成: 
- navigator对象,包括appName、appVersion、user Agent和platform。 
- 一个location对象,(与window.location同,可是只读) 
- 一个self对象,指向全局worker对象。 
- 一个importScripts()方法,用来加载Worker所用到外部JS文件 
- 全部的ECMAScript对象 
- XMLHttpRequest构造器 
- setTimeout()和setInterval()方法 
-一个close()方法,它能马上中止Worker运行

var worker = new Worker("code.js")
  • 1

与Worker通讯

Worker与网页代码经过事件接口进行通讯,网页代码经过postMessage()方法给Worker传递数据,它接收一个参数,即须要传给Worker的数据。此外,Worker还有一个用来接收信息的onmessage事件处理器。

var worker = new Worker('code.js') worker.onmessage = function(event) { process(event.data) } worker.postMessage('data') //code.js self.onmessage = function(event) { self.postMessage("Hello" + event.data) }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

加载外部文件

importScripts()阻塞
  • 1

实际应用

var worker = new Worker("jsonparser.js"); worker.onmessage = function(event) { var jsonData = event.data evaluateData(jsonData) } worker.postMessage(jsonText) self.onmessage = function(event) { var jsonText = event.data var jsonData = JSON.parse(jsonText) self.postMessage(jsonData) }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

小结

  • 任何JavaScript任务都不该当执行超过100毫秒,过长的运行时间会致使UI更新出现明显的延迟,从而对用户体验产生负面影响。
  • 定时器能够用来安排代码延迟执行,把一个大任务分割成多个小任务,不影响UI渲染
  • Web Worker是新版浏览器支持的特性

第七章 Ajax

Ajax能够经过延迟和异步加载大资源。

数据传输

Ajax从最基本的层面来讲,是一种与服务器通讯而无需重载页面的方法,数据能够从服务器获取或发送给服务器。

请求数据

有五种经常使用技术用于向服务器请求数据: 
- XMLHttpRequest(XHR) 
- Dynamic script tag insertion 动态脚本注入 
- iframes 
- Comet 
- Multipart XHR

XHR

var url = '/data.php'; var params = [ 'id=934875', 'limit=20' ]; var req = new XMLHttpRequest(); req.onreadystatechange = function() { if (req.readystate === 4) { } } req.open('get', url + '?' + params.join('&'), true) req.setRequestHeader('X-Requested-with', 'XMLHttpRequst'); req.send(null)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

XHR的get请求是幂等行为,即一次请求和屡次请求并不会有反作用。

动态脚本注入 
这个与XHR不一样的地方在于它不用在乎跨域问题。

var scriptElement = document.createElement('script'); scriptElement.src = ''; document.getElementsByTagName('head')[0].appendChild(scriptElement)
  • 1
  • 2
  • 3
  • 4
  • 5

不能设置头信息,参数传递也只能用GET,不能设置请求的超时处理。

由于响应消息做为脚本标签的源码,它必须是可执行的JS代码。

Multipart XHR 
MXHR容许客户端只用一个HTTP请求就能够从服务端向客户端传送多个资源。它经过在服务端将资源打包成一个由双方约定的字符串分割的长字符串并发送到客户端,而后用JS代码处理这个长字符串,并根据它的mime-type类型和传入的其余“头信息”解析出每一个资源。

发送数据

数据格式

惟一须要比较的标准就是速度。

XML

须要解析结构,才能读取值。

JSON

JSON-P JSON填充,在动态脚本注入时必须放在回调函数里,否则就会被当作另一个JS文件执行(因此也要尤为注意脚本攻击。)

HTML

Ajax性能指南

缓存数据

最快的Ajax请求就是没有请求。有两种主要的方法能够避免发送没必要要的请求: 
- 在服务端,设置HTTP头信息以确保响应会被浏览器缓存 
- 在客户端,把获取到的信息存储到本地,从而避免再次请求。

设置HTTP头信息 
若是你但愿Ajax响应可以被浏览器缓存,那么你必须使用GET方式发出请求。但这还不够,你还必须在相应中发送正确的HTTP头信息。

Expires头信息会告诉浏览器应该缓存响应多久,它的值是一个日期,过时以后,对该URL的任何请求都再也不从缓存中获取,而是会从新访问服务器。

本地数据存储 
能够把响应文本保存到一个对象中,以URL为键值作索引。

var localCache = {}; function xhrRequest(url, callback) { // 检查此URL的本地缓存 if (localCache[url]) { callback.success(localCache[url]); return; } // 此URL对应的缓存没有找到,则发送请求 var req = createXhrObject(); req.onerror = function() { callback.error(); } req.onreadystatechange = function() { ... localCache[url] = req.responseText; callback.success(req.responseText) } req.open("GET", url, true) req.send(null) }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

了解Ajax类库的局限

浏览器之间有些差别,不过大多数类库都有封装。

小结

  • 减小请求数,可经过合并JavaScript和CSS文件,或使用MXHR
  • 缩短页面的加载时间,页面主要内容加载完成后,用Ajax获取次要文件
  • 代码错误不会输出给用户
  • 知道什么时候使用类库,什么时候编写本身的底层Ajax代码

第八章 编程实践

避免双重求值(Double Evaluation)

JS和其余不少脚本语言同样,容许你在程序中提取一个包含代码的字符串,而后动态执行。

有四种标准方法能够实现:eval(),Function(),setTimeout(),setInterval()。

在JS代码中执行另一段JS代码就会形成双重求值,因此eval和Function不推荐使用,setTimeout和setInterval的第一个参数最好是回调函数。

使用Object/Array 直接量

使用直接量建立Object/Array

避免重复工做

延迟加载

当一个函数在页面中不会被当即调用时,延迟加载时最好的选择,即若是须要针对不一样的浏览器写不一样的代码,在第一次就判断用的是哪一种,而后在内部重写函数。

条件预加载

var addHandler = document.body.addEventListener ? function1 : function2
  • 1

使用速度快的部分

位操做

原生方法

小结

  • 经过避免使用eval()和Function()构造器来避免双重求值带来的性能消耗。一样的,给setTimeout()和setInterval()传递函数而不是字符串做为参数。
  • 尽可能使用直接量建立对象和数组
  • 避免作重复的工做。当须要检测浏览器时,可使用延迟加载或条件预加载。
  • 进行数学计算时,考虑使用直接操做数字的二进制形式的位运算。
  • 尽可能使用原生方法
相关文章
相关标签/搜索