GET
, 仅取决于cookie
数量Cookie
的大小Cookie
的域来存放静态资源(能够利用CDN
)cloneNode
在外部更新节点后在经过replace
与原始节点互换)var orig = document.getElementById('container'); var clone = orig.cloneNode(true); var list = ['foo', 'bar', 'baz']; var content; for (var i = 0; i < list.length; i++) { content = document.createTextNode(list[i]); clone.appendChild(content); } orig.parentNode.replaceChild(clone, orig);
var orig = document.getElementById('container'); var clone = orig.cloneNode(true); var list = ['foo', 'bar', 'baz']; var content; for (var i = 0; i < list.length; i++) { content = document.createTextNode(list[i]); clone.appendChild(content); } orig.parentNode.replaceChild(clone, orig);
DocumentFragment
, 在其中插入节点后再添加到页面)createSafeFragment(document) { var list = nodeNames.split( "|" ), safeFrag = document.createDocumentFragment(); if (safeFrag.createElement) { while (list.length) { safeFrag.createElement( list.pop(); ); }; }; return safeFrag; };
createSafeFragment(document) { var list = nodeNames.split( "|" ), safeFrag = document.createDocumentFragment(); if (safeFrag.createElement) { while (list.length) { safeFrag.createElement( list.pop(); ); }; }; return safeFrag; };
CSS
样式转换(尽可能采用触发reflow
次数少的方式, 使用直接设置元素的className
来代替逐条更改元素样式)// Not Recommended element.style.fontWeight = 'bold' ; element.style.marginLeft= '30px' ; element.style.marginRight = '30px' ; // Recommended element.className = 'selectedAnchor' ;
// Not Recommended element.style.fontWeight = 'bold' ; element.style.marginLeft= '30px' ; element.style.marginRight = '30px' ; // Recommended element.className = 'selectedAnchor' ;
DOM
元素数量 document.getElementsByTagName( '*' ).length <= 1000
javascript
DOM
操做优化 DOM
操做性能缘由php
优化DOM
操做css
JAVASCRIPT
执行时间是很短的。DOM访问次数
,尽量在js端执行。使用局部变量存储对它的引用
。谨慎处理HTML集合
(HTML集合实时连系底层文档),把集合的长度缓存到一个变量中,并在迭代中使用它,若是须要常常操做集合,建议把它拷贝到一个数组中
。querySelectorAll
和firstElementChild
。离线
操做DOM树。使用缓存
,并减小访问布局的次数。动画中使用绝对定位,使用拖放代理
。使用事件委托来减小事件处理器的数量
DOM
交互 最小化现场更新
多使用innerHTML
替代createElement()
和appendChild()
:html
reflow
回流发生场景前端
解决关键: 限制DOM
操做所引起的回流java
removeChild()
或者replaceChild()
实现真正意义上的删除。display
样式为“none
”。element.style.backgroundColor = "blue";
className
的方式替换style.xxx=xxx
的方式。style.cssText = '';
一次写入样式。fixed
或absolute
。JavaScript expressions(IE only)
。HTMLCollection
对象进行操做时,应该将访问的次数尽量的降至最低,最简单的,你能够将length属性缓存在一个本地变量中,这样就能大幅度的提升循环的效率。repaint
重绘innerHTML
来将HTML代码插入到页面中。)避免空的src和href
。
Expires
。把重要内容的优先级提升
。Post-load(次要加载)
不是必须的资源。预加载
优化资源。LocalStorage
合理缓存资源。避免CSS表达式和滤镜
。使用defer方式加载Js脚本
。will-change
,把即将发生的改变预先告诉浏览器。Beacon
,不堵塞队列的异步数据发送。HTML5 Web Workers来容许多线程工做
。为不一样的Viewports设置不一样大小的Content
。响应式图片
。新接口协议
(如HTTP2)。缓存离线机制
:Service Workers
。Resource Hints(preconnect, preload, 和prerender)
。Server-sent Events
。Meta Viewport
。/
” 的跳转;Alias
或者mod_rewirte
创建CNAME
(保存域名与域名之间关系的DNS记录) 高消耗属性在绘制前须要浏览器进行大量计算: box-shadows
border-radius
transparency
transforms
CSS filters(性能杀手)
node
当发生重排的时候,浏览器须要从新计算布局位置与大小,更多详情。mysql
常见的重排元素: width
height
padding
margin
display
border-width
position
top
left
right
bottom
font-size
float
text-align
overflow-y
font-weight
overflow
font-family
line-height
vertical-align
clear
white-space
min-height
jquery
Display
的属性 Display
属性会影响页面的渲染,请合理使用。web
display: inline
后不该该再使用 width
height
margin
padding
以及 float
display: inline-block
后不该该再使用 float
;display: block
后不该该再使用 vertical-align
;display: table-*
后不该该再使用 margin
或者 float
;Float在渲染时计算量比较大,尽可能减小使用。
动画的基本概念:
通常浏览器的渲染刷新频率是 60 fps
,因此在网页当中,帧率若是达到 50-60 fps
的动画将会至关流畅,让人感到温馨。
javaScript
的动画,尽可能使用 requestAnimationFrame
. 避免使用 setTimeout
, setInterval
.jQuery animate()-style
改变每帧的样式,使用 CSS 声明动画会获得更好的浏览器优化。translate
取代 absolute
定位就会获得更好的 fps
,动画会更顺滑。position:absolute;
或 position:fixed
(只须要repaint
),而position: static
或position: relative
元素应用动画效果会形成频繁的reflow
timer
完成多个元素的动画timer
完成多个对象的动画效果jQuery
实现动画
slideUp/Down()
fadeIn/fadeOut()
等方法;animate()
方法; 通常在 Chrome
中,3D或透视变换(perspective transform
)CSS
属性和对 opacity
进行 CSS
动画会建立新的图层,在硬件加速渲染通道的优化下,GPU
完成 3D
变形等操做后,将图层进行复合操做(Compesite Layers
),从而避免触发浏览器大面积重绘和重排。
使用 translate3d
右移 500px
的动画流畅度要明显优于直接使用 left
:
.ball-1 { transition: -webkit-transform .5s ease; -webkit-transform: translate3d(0, 0, 0); } .ball-1.slidein{ -webkit-transform: translate3d(500px, 0, 0); } .ball-2 { transition: left .5s ease; left:0; } .ball-2.slidein { left:500px; }
.ball-1 { transition: -webkit-transform .5s ease; -webkit-transform: translate3d(0, 0, 0); } .ball-1.slidein{ -webkit-transform: translate3d(500px, 0, 0); } .ball-2 { transition: left .5s ease; left:0; } .ball-2.slidein { left:500px; }
CSS 选择器对性能的影响源于浏览器匹配选择器和文档元素时所消耗的时间,因此优化选择器的原则是应尽可能避免使用消耗更多匹配时间的选择器。CSS 选择器匹配的机制, 如子选择器规则:
#header > a {font-weight:blod;}
#header > a {font-weight:blod;}
CSS 选择器是从右到左进行规则匹配。
最右边选择符为关键选择器。——更多详情
class
选择器限制 id
选择器class
选择器class
选择器替换,减小css
查找/* Not recommended */ #bookmarkMenuItem > .menu-left { list-style-image: url(blah) } /* Recommended */ #bookmarkMenuItem { list-style-image: url(blah) }
/* Not recommended */ #bookmarkMenuItem > .menu-left { list-style-image: url(blah) } /* Recommended */ #bookmarkMenuItem { list-style-image: url(blah) }
PNG
文件, 以后进行拆包, 只要使用画布API的getImageData()。能够在不缩小数据的状况下,多压缩35%左右。并且是无损压缩,对比较庞大的脚原本说,在图片指向画布、读取像素的过程当中,会有一段读取时间。 设置Cache-Control
和Expires
头
(function() { var script, scripts = document.getElementsByTagName('script')[0]; function load(url) { script = document.createElement('script'); script.async = true; script.src = url; scripts.parentNode.insertBefore(script, scripts); } load('//apis.google.com/js/plusone.js'); load('//platform.twitter.com/widgets.js'); load('//s.widgetsite.com/widget.js'); }());
(function() { var script, scripts = document.getElementsByTagName('script')[0]; function load(url) { script = document.createElement('script'); script.async = true; script.src = url; scripts.parentNode.insertBefore(script, scripts); } load('//apis.google.com/js/plusone.js'); load('//platform.twitter.com/widgets.js'); load('//s.widgetsite.com/widget.js'); }());
Gzip
压缩, 比 deflate
更高效
Accept-Encoding
中声明能够支持Gzip
。Content-Encoding
中声明该回复为Gzip
格式。Gzip
解压缩。Javascript
优化ES6
可使用...args
代替隐式的arguments
// 显式判断处理优化 function sum() { var r = 0; for (var i = 0; i < arguments.length; i++) { r += arguments[i]; } return r; } // 参数较少时优化 function sum() { switch (arguments.length) { case 1: return arguments[0]; case 2: return arguments[0] + arguments[1]; case 3: return arguments[0] + arguments[1] + arguments[2]; case 4: return arguments[0] + arguments[1] + arguments[2] + arguments[3]; default: var r = 0; for (var i = 0; i < arguments.length; i++) { r += arguments[i]; } return r; } } // 显式调用优化 (速度至少快1倍) function sum(a, b, c, d, e, f, g) { var r = a ? b ? c ? d ? e ? f ? a + b + c + d + e + f : a + b + c + d + e : a + b + c + d : a + b + c : a + b : a : 0; if (g === undefined) return r; for (var i = 6; i < arguments.length; i++) { r += arguments[i]; } return r; }
// 显式判断处理优化 function sum() { var r = 0; for (var i = 0; i < arguments.length; i++) { r += arguments[i]; } return r; } // 参数较少时优化 function sum() { switch (arguments.length) { case 1: return arguments[0]; case 2: return arguments[0] + arguments[1]; case 3: return arguments[0] + arguments[1] + arguments[2]; case 4: return arguments[0] + arguments[1] + arguments[2] + arguments[3]; default: var r = 0; for (var i = 0; i < arguments.length; i++) { r += arguments[i]; } return r; } } // 显式调用优化 (速度至少快1倍) function sum(a, b, c, d, e, f, g) { var r = a ? b ? c ? d ? e ? f ? a + b + c + d + e + f : a + b + c + d + e : a + b + c + d : a + b + c : a + b : a : 0; if (g === undefined) return r; for (var i = 6; i < arguments.length; i++) { r += arguments[i]; } return r; }
setInterval
取代屡次setTimeout
, 屡次执行相同的代码setTimeout()
setInterval()
传递方法取代方法字符串
setTimeout(test, 1);
取代 setTimeout('test()', 1);
var min = a<b?a:b;
取代 var min = Math.min(a, b);
eval函数
、new Function构造函数
和setTimeout
传一个字符串时等状况下会遇到)
eval("alert('hello world');");
var sayHi = new Function("alert('hello world');");
setTimeout("alert('hello world');", 100);
var name = values[i++];
Array()
Object()
offsetLeft
offsetTop
offsetHeight
offsetWidth
scrollTop/Left/Width/Height
clientTop/Left/Width/Height
getComputedStyle()
eval()
: 由于eval()
会致使代码脏, 消耗大量时间, 没法被压缩工具压缩, 容易形成安全漏洞with
: 尽量地少用with语句,由于它会增长with语句之外的数据的访问代价。C
写的)+=
(若是考虑IE6
,则使用Array.join("")
), 编译器已经优化window
对象成员, 会一直存在老生代堆内存中, 直到页面被关闭data = null;
var
, ES6
中为let
,
分隔function getData(callback) { var data = 'some big data'; callback(null, data); } getData(function(err, data) { console.log(data); });
function getData(callback) { var data = 'some big data'; callback(null, data); } getData(function(err, data) { console.log(data); });
+=
-=
*=
\=
运算符, 而不是直接赋值位运算
swich
语句来优化多个if...else
语句||
&&
来优化多个if
语句""+num
> String()
> .toString()
> new String()
Math.floor()
或 Math.round()
parseInt(str,10)
O(n^2)
))Javascript
对象JSON
建立对象, 而不是var obj = new Object()
a.b.c.d.e
嵌套对象,须要进行4
次查询,嵌套的对象成员会明显影响性能。)JSON
格式的语法do-while
> for(;;)
≈ while()
> for(in)
for(;;)
-=1
。IE6
下, divs.length
在每次循环执行中都会计算一下长度for(in)
for(in)
内部实现是构造一个全部元素的列表,包括array
继承的属性,而后再开始循环,而且须要查询hasOwnProperty
。变量
或数组
是O(1)
操做对象
上的属性
是一个O(n)操做。(局部变量缓存)do-while
是后测试循环, 能够避免最初终止条件的计算)for(var i = 0; i < values.length; i++) { process(values[i]); } // 优化1:简化终止条件 for(var i = 0, len = values.length; i < len; i++) { process(values[i]); } // 优化2:使用后测试循环(注意:使用后测试循环须要确保要处理的值至少有一个) var i values.length - 1; if(i > -1) { do { process(values[i]); } while(--i >= 0); }
for(var i = 0; i < values.length; i++) { process(values[i]); } // 优化1:简化终止条件 for(var i = 0, len = values.length; i < len; i++) { process(values[i]); } // 优化2:使用后测试循环(注意:使用后测试循环须要确保要处理的值至少有一个) var i values.length - 1; if(i > -1) { do { process(values[i]); } while(--i >= 0); }
Duff
装置来优化。(Duff
装置的基本概念是经过计算迭代的次数是否为8的倍数将一个循环展开为一系列语句。)// Jeff Greenberg for JS implementation of Duff's Device // 如上展开循环能够提高大数据集的处理速度。 // 假设: values.length > 0 function process(v) { alert(v); } var values = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17]; var iterations = Math.ceil(values.length / 8); var startAt = values.length % 8; var i = 0; do { switch(startAt) { case 0 : process(values[i++]); case 7 : process(values[i++]); case 6 : process(values[i++]); case 5 : process(values[i++]); case 4 : process(values[i++]); case 3 : process(values[i++]); case 2 : process(values[i++]); case 1 : process(values[i++]); } startAt = 0; } while(--iterations > 0); // 接下来给出更快的Duff装置技术, // 将do-while循环分红2个单独的循环。(注:这种方法几乎比原始的Duff装置实现快上40%。) function process(v) { alert(v); } var values = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17]; var iterations = Math.floor(values.length / 8); var leftover = values.length % 8; var i = 0; if(leftover > 0) { do { process(values[i++]); }while(--leftover > 0); } do { process(values[i++]); process(values[i++]); process(values[i++]); process(values[i++]); process(values[i++]); process(values[i++]); process(values[i++]); process(values[i++]); }while(--iterations > 0); // 针对大数据集使用展开循环能够节省不少时间,但对于小数据集,额外的开销则可能得不偿失。
// Jeff Greenberg for JS implementation of Duff's Device // 如上展开循环能够提高大数据集的处理速度。 // 假设: values.length > 0 function process(v) { alert(v); } var values = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17]; var iterations = Math.ceil(values.length / 8); var startAt = values.length % 8; var i = 0; do { switch(startAt) { case 0 : process(values[i++]); case 7 : process(values[i++]); case 6 : process(values[i++]); case 5 : process(values[i++]); case 4 : process(values[i++]); case 3 : process(values[i++]); case 2 : process(values[i++]); case 1 : process(values[i++]); } startAt = 0; } while(--iterations > 0); // 接下来给出更快的Duff装置技术, // 将do-while循环分红2个单独的循环。(注:这种方法几乎比原始的Duff装置实现快上40%。) function process(v) { alert(v); } var values = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17]; var iterations = Math.floor(values.length / 8); var leftover = values.length % 8; var i = 0; if(leftover > 0) { do { process(values[i++]); }while(--leftover > 0); } do { process(values[i++]); process(values[i++]); process(values[i++]); process(values[i++]); process(values[i++]); process(values[i++]); process(values[i++]); process(values[i++]); }while(--iterations > 0); // 针对大数据集使用展开循环能够节省不少时间,但对于小数据集,额外的开销则可能得不偿失。
避免遍历大量元素(避免对全局DOM元素进行遍历,若是parent
已知能够指定parent
在特定范围查询。)
var elements = document.getElementsByTagName( '*' ); for (i = 0; i < elements.length; i++) { if (elements[i].hasAttribute( 'selected' )) {} } // 若是已知元素存在于一个较小的范围内, var elements = document.getElementById( 'canvas' ).getElementsByTagName ( '*' ); for (i = 0; i < elements.length; i++) { if (elements[i].hasAttribute( 'selected' )) {} }
var elements = document.getElementsByTagName( '*' ); for (i = 0; i < elements.length; i++) { if (elements[i].hasAttribute( 'selected' )) {} } // 若是已知元素存在于一个较小的范围内, var elements = document.getElementById( 'canvas' ).getElementsByTagName ( '*' ); for (i = 0; i < elements.length; i++) { if (elements[i].hasAttribute( 'selected' )) {} }
避免在循环中使用try_catch
try-catch-finally
语句在catch
语句被执行的过程当中会动态构造变量插入到当前域中,对性能有必定影响。// Not recommended for ( var i = 0; i < 200; i++) { try {} catch (e) {} } // Recommended try { for ( var i = 0; i < 200; i++) {} } catch (e) {}
// Not recommended for ( var i = 0; i < 200; i++) { try {} catch (e) {} } // Recommended try { for ( var i = 0; i < 200; i++) {} } catch (e) {}
JAVASCRIPT中原型的概念,构造函数都有一个prototype属性,指向另外一个对象。这个对象的全部属性和方法,都会被构造函数的实例继承
经过原型优化方法定义
能够把那些不变的属性和方法,直接定义在prototype对象上
delete
操做符能够删除实例中的属性。 做用域(scope)
JAVASCRIPT编程中一个重要的运行机制,在JAVASCRIPT同步和异步编程以及JAVASCRIPT内存管理中起着相当重要的做用。
在JAVASCRIPT中,能造成做用域的有以下几点
with
语句 with会建立自已的做用域,所以会增长其中执行代码的做用域的长度。var foo = function() { var local = {}; }; foo(); console.log(local); //=> undefined var bar = function() { local = {}; }; bar(); console.log(local); //=> {} /**这里咱们定义了foo()函数和bar()函数,他们的意图都是为了定义一个名为local的变量。在foo()函数中,咱们使用var语句来声明定义了一个local变量,而由于函数体内部会造成一个做用域,因此这个变量便被定义到该做用域中。并且foo()函数体内并无作任何做用域延伸的处理,因此在该函数执行完毕后,这个local变量也随之被销毁。而在外层做用域中则没法访问到该变量。而在bar()函数内,local变量并无使用var语句进行声明,取而代之的是直接把local做为全局变量来定义。故外层做用域能够访问到这个变量。**/ local = {}; // 这里的定义等效于 global.local = {};
var foo = function() { var local = {}; }; foo(); console.log(local); //=> undefined var bar = function() { local = {}; }; bar(); console.log(local); //=> {} /**这里咱们定义了foo()函数和bar()函数,他们的意图都是为了定义一个名为local的变量。在foo()函数中,咱们使用var语句来声明定义了一个local变量,而由于函数体内部会造成一个做用域,因此这个变量便被定义到该做用域中。并且foo()函数体内并无作任何做用域延伸的处理,因此在该函数执行完毕后,这个local变量也随之被销毁。而在外层做用域中则没法访问到该变量。而在bar()函数内,local变量并无使用var语句进行声明,取而代之的是直接把local做为全局变量来定义。故外层做用域能够访问到这个变量。**/ local = {}; // 这里的定义等效于 global.local = {};
在JAVASCRIPT编程中,会遇到多层函数嵌套的场景,这就是典型的做用域链的表示。
function foo() { var val = 'hello'; function bar() { function baz() { global.val = 'world;' }; baz(); console.log(val); //=> hello }; bar(); }; foo(); /**在`JAVASCRIPT`中,变量标识符的查找是从当前做用域开始向外查找,直到全局做用域为止。因此`JAVASCRIPT`代码中对变量的访问只能向外进行,而不能逆而行之。baz()函数的执行在全局做用域中定义了一个全局变量val。而在bar()函数中,对val这一标识符进行访问时,按照从内到外的查找原则:在bar函数的做用域中没有找到,便到上一层,即foo()函数的做用域中查找。然而,使你们产生疑惑的关键就在这里:本次标识符访问在foo()函数的做用域中找到了符合的变量,便不会继续向外查找,故在baz()函数中定义的全局变量val并无在本次变量访问中产生影响。**/
function foo() { var val = 'hello'; function bar() { function baz() { global.val = 'world;' }; baz(); console.log(val); //=> hello }; bar(); }; foo(); /**在`JAVASCRIPT`中,变量标识符的查找是从当前做用域开始向外查找,直到全局做用域为止。因此`JAVASCRIPT`代码中对变量的访问只能向外进行,而不能逆而行之。baz()函数的执行在全局做用域中定义了一个全局变量val。而在bar()函数中,对val这一标识符进行访问时,按照从内到外的查找原则:在bar函数的做用域中没有找到,便到上一层,即foo()函数的做用域中查找。然而,使你们产生疑惑的关键就在这里:本次标识符访问在foo()函数的做用域中找到了符合的变量,便不会继续向外查找,故在baz()函数中定义的全局变量val并无在本次变量访问中产生影响。**/
减小做用域链上的查找次数
JAVASCRIPT代码在执行的时候,若是须要访问一个变量或者一个函数的时候,它须要遍历当前执行环境的做用域链,而遍历是从这个做用域链的前端一级一级的向后遍历,直到全局执行环境。
/**效率低**/ for(var i = 0; i < 10000; i++){ var but1 = document.getElementById("but1"); } /**效率高**/ /**避免全局查找**/ var doc = document; for(var i = 0; i < 10000; i++){ var but1 = doc.getElementById("but1"); } /**上面代码中,第二种状况是先把全局对象的变量放到函数里面先保存下来,而后直接访问这个变量,而第一种状况是每次都遍历做用域链,直到全局环境,咱们看到第二种状况实际上只遍历了一次,而第一种状况倒是每次都遍历了,并且这种差异在多级做用域链和多个全局变量的状况下还会表现的很是明显。在做用域链查找的次数是`O(n)`。经过建立一个指向`document`的局部变量,就能够经过限制一次全局查找来改进这个函数的性能。**/
/**效率低**/ for(var i = 0; i < 10000; i++){ var but1 = document.getElementById("but1"); } /**效率高**/ /**避免全局查找**/ var doc = document; for(var i = 0; i < 10000; i++){ var but1 = doc.getElementById("but1"); } /**上面代码中,第二种状况是先把全局对象的变量放到函数里面先保存下来,而后直接访问这个变量,而第一种状况是每次都遍历做用域链,直到全局环境,咱们看到第二种状况实际上只遍历了一次,而第一种状况倒是每次都遍历了,并且这种差异在多级做用域链和多个全局变量的状况下还会表现的很是明显。在做用域链查找的次数是`O(n)`。经过建立一个指向`document`的局部变量,就能够经过限制一次全局查找来改进这个函数的性能。**/
JAVASCRIPT中的标识符查找遵循从内到外的原则。
function foo() { var local = 'Hello'; return function() { return local; }; } var bar = foo(); console.log(bar()); //=> Hello /**这里所展现的让外层做用域访问内层做用域的技术即是闭包(Closure)。得益于高阶函数的应用,使foo()函数的做用域获得`延伸`。foo()函数返回了一个匿名函数,该函数存在于foo()函数的做用域内,因此能够访问到foo()函数做用域内的local变量,并保存其引用。而因这个函数直接返回了local变量,因此在外层做用域中即可直接执行bar()函数以得到local变量。**/
function foo() { var local = 'Hello'; return function() { return local; }; } var bar = foo(); console.log(bar()); //=> Hello /**这里所展现的让外层做用域访问内层做用域的技术即是闭包(Closure)。得益于高阶函数的应用,使foo()函数的做用域获得`延伸`。foo()函数返回了一个匿名函数,该函数存在于foo()函数的做用域内,因此能够访问到foo()函数做用域内的local变量,并保存其引用。而因这个函数直接返回了local变量,因此在外层做用域中即可直接执行bar()函数以得到local变量。**/
闭包是JAVASCRIPT的高级特性,由于把带有内部变量引用的函数带出了函数外部,因此该做用域内的变量在函数执行完毕后的并不必定会被销毁,直到内部变量的引用被所有解除。因此闭包的应用很容易形成内存没法释放的状况。
良好的闭包管理。
循环事件绑定、私有属性、含参回调等必定要使用闭包时,并谨慎对待其中的细节。
循环绑定事件,咱们假设一个场景:有六个按钮,分别对应六种事件,当用户点击按钮时,在指定的地方输出相应的事件。
var btns = document.querySelectorAll('.btn'); // 6 elements var output = document.querySelector('#output'); var events = [1, 2, 3, 4, 5, 6]; // Case 1 for (var i = 0; i < btns.length; i++) { btns[i].onclick = function(evt) { output.innerText += 'Clicked ' + events[i]; }; } /**这里第一个解决方案显然是典型的循环绑定事件错误**/ // Case 2 for (var i = 0; i < btns.length; i++) { btns[i].onclick = (function(index) { return function(evt) { output.innerText += 'Clicked ' + events[index]; }; })(i); } /**第二个方案传入的参数是当前循环下标,然后者是直接传入相应的事件对象。事实上,后者更适合在大量数据应用的时候,由于在JavaScript的函数式编程中,函数调用时传入的参数是基本类型对象,那么在函数体内获得的形参会是一个复制值,这样这个值就被看成一个局部变量定义在函数体的做用域内,在完成事件绑定以后就能够对events变量进行手工解除引用,以减轻外层做用域中的内存占用了。并且当某个元素被删除时,相应的事件监听函数、事件对象、闭包函数也随之被销毁回收。**/ // Case 3 for (var i = 0; i < btns.length; i++) { btns[i].onclick = (function(event) { return function(evt) { output.innerText += 'Clicked ' + event; }; })(events[i]); }
var btns = document.querySelectorAll('.btn'); // 6 elements var output = document.querySelector('#output'); var events = [1, 2, 3, 4, 5, 6]; // Case 1 for (var i = 0; i < btns.length; i++) { btns[i].onclick = function(evt) { output.innerText += 'Clicked ' + events[i]; }; } /**这里第一个解决方案显然是典型的循环绑定事件错误**/ // Case 2 for (var i = 0; i < btns.length; i++) { btns[i].onclick = (function(index) { return function(evt) { output.innerText += 'Clicked ' + events[index]; }; })(i); } /**第二个方案传入的参数是当前循环下标,然后者是直接传入相应的事件对象。事实上,后者更适合在大量数据应用的时候,由于在JavaScript的函数式编程中,函数调用时传入的参数是基本类型对象,那么在函数体内获得的形参会是一个复制值,这样这个值就被看成一个局部变量定义在函数体的做用域内,在完成事件绑定以后就能够对events变量进行手工解除引用,以减轻外层做用域中的内存占用了。并且当某个元素被删除时,相应的事件监听函数、事件对象、闭包函数也随之被销毁回收。**/ // Case 3 for (var i = 0; i < btns.length; i++) { btns[i].onclick = (function(event) { return function(evt) { output.innerText += 'Clicked ' + event; }; })(events[i]); }
避开闭包陷阱
闭包是个强大的工具,但同时也是性能问题的主要诱因之一。不合理的使用闭包会致使内存泄漏。
闭包的性能不如使用内部方法,更不如重用外部方法。
因为IE 9
浏览器的DOM
节点做为COM
对象来实现,COM
的内存管理是经过引用计数的方式,引用计数有个难题就是循环引用,一旦DOM
引用了闭包(例如event handler
),闭包的上层元素又引用了这个DOM
,就会形成循环引用从而致使内存泄漏。
善用函数, 避免闭包陷阱
(function() { // 主业务代码 })();
(function(win, doc, $, undefined) { // 主业务代码 })(window, document, jQuery);
(function(win, doc, $, undefined) { // 主业务代码 })(window, document, jQuery);
/**RequireJS**/ define(['jquery'], function($) { // 主业务代码 }); /**SeaJS**/ define('module', ['dep', 'underscore'], function($, _) { // 主业务代码 });
/**RequireJS**/ define(['jquery'], function($) { // 主业务代码 }); /**SeaJS**/ define('module', ['dep', 'underscore'], function($, _) { // 主业务代码 });
被定义在全局做用域的对象,多是会一直存活到进程退出的,若是是一个很大的对象,那就麻烦了。
好比有的人喜欢在JavaScript中作模版渲染:
<?php $db = mysqli_connect(server, user, password, 'myapp'); $topics = mysqli_query($db, "SELECT * FROM topics;"); ?> <!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <title>你是猴子请来的逗比么?</title> </head> <body> <ul id="topics"></ul> <script type="text/tmpl" id="topic-tmpl"> <li class="topic"> <h1><%=title%></h1> <p><%=content%></p> </li> </script> <script type="text/javascript"> var data = <?php echo json_encode($topics); ?>; var topicTmpl = document.querySelector('#topic-tmpl').innerHTML; var render = function(tmlp, view) { var complied = tmlp .replace(/\n/g, '\\n') .replace(/<%=([\s\S]+?)%>/g, function(match, code) { return '" + escape(' + code + ') + "'; }); complied = [ 'var res = "";', 'with (view || {}) {', 'res = "' + complied + '";', '}', 'return res;' ].join('\n'); var fn = new Function('view', complied); return fn(view); }; var topics = document.querySelector('#topics'); function init() data.forEach(function(topic) { topics.innerHTML += render(topicTmpl, topic); }); } init(); </script> </body> </html>
<?php $db = mysqli_connect(server, user, password, 'myapp'); $topics = mysqli_query($db, "SELECT * FROM topics;"); ?> <!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <title>你是猴子请来的逗比么?</title> </head> <body> <ul id="topics"></ul> <script type="text/tmpl" id="topic-tmpl"> <li class="topic"> <h1><%=title%></h1> <p><%=content%></p> </li> </script> <script type="text/javascript"> var data = <?php echo json_encode($topics); ?>; var topicTmpl = document.querySelector('#topic-tmpl').innerHTML; var render = function(tmlp, view) { var complied = tmlp .replace(/\n/g, '\\n') .replace(/<%=([\s\S]+?)%>/g, function(match, code) { return '" + escape(' + code + ') + "'; }); complied = [ 'var res = "";', 'with (view || {}) {', 'res = "' + complied + '";', '}', 'return res;' ].join('\n'); var fn = new Function('view', complied); return fn(view); }; var topics = document.querySelector('#topics'); function init() data.forEach(function(topic) { topics.innerHTML += render(topicTmpl, topic); }); } init(); </script> </body> </html>
在从数据库中获取到的数据的量是很是大的话,前端完成模板渲染之后,data变量便被闲置在一边。可由于这个变量是被定义在全局做用域中的,因此JAVASCRIPT引擎不会将其回收销毁。如此该变量就会一直存在于老生代堆内存中,直到页面被关闭。但是 若是咱们做出一些很简单的修改,在逻辑代码外包装一层函数,这样效果就大不一样了。当UI渲染完成以后,代码对data的引用也就随之解除,而在最外层函数执行完毕时,JAVASCRIPT引擎就开始对其中的对象进行检查,data也就能够随之被回收
DOM Level2事件模型
中全部事件默认会传播到上层文档对象,能够借助这个机制在上层元素注册一个统一事件对不一样子元素进行相应处理。// 捕获型事件先发生。 // 两种事件流会触发DOM中的全部对象,从document对象开始,也在document对象结束。 <ul id="parent-list"> <li id="post-1">Item 1</li> <li id="post-2">Item 2</li> <li id="post-3">Item 3</li> <li id="post-4">Item 4</li> <li id="post-5">Item 5</li> <li id="post-6">Item 6</li> </ul> // Get the element, add a click listener... document.getElementById("parent-list").addEventListener("click",function(e) { // e.target is the clicked element! // If it was a list item if(e.target && e.target.nodeName == "LI") { // List item found! Output the ID! console.log("List item ",e.target.id.replace("post-")," was clicked!"); } });
// 捕获型事件先发生。 // 两种事件流会触发DOM中的全部对象,从document对象开始,也在document对象结束。 <ul id="parent-list"> <li id="post-1">Item 1</li> <li id="post-2">Item 2</li> <li id="post-3">Item 3</li> <li id="post-4">Item 4</li> <li id="post-5">Item 5</li> <li id="post-6">Item 6</li> </ul> // Get the element, add a click listener... document.getElementById("parent-list").addEventListener("click",function(e) { // e.target is the clicked element! // If it was a list item if(e.target && e.target.nodeName == "LI") { // List item found! Output the ID! console.log("List item ",e.target.id.replace("post-")," was clicked!"); } });
diviefirebug
performance
性能评估打分,右击箭头可看到改进建议。stats
缓存状态分析,传输内容分析。components
全部加载内容分析,能够查看传输速度,找出页面访问慢的瓶颈。tools
能够查看js和css,并打印页面评估报告。sIEve
sIEve
是基于IE
的内存泄露检测工具,须要下载运行,能够查看dom
孤立节点和内存泄露及内存使用状况。leak monitor
leak monitor
在安装后,当离开一个页面时,好比关闭窗口,若是页面有内存泄露,会弹出一个文本框进行即时提示。Blink/Webkit
浏览器中(Chrome
, Safari
, Opera
),咱们能够借助其中的Developer Tools
的Profiles
工具来 对咱们的程序进行内存检查。Node.js
中的内存检查OneApm
或 alinode
进行线上监控Node.js
中,咱们可使用node-heapdump
和node-memwatch
模块进行内存检查。var heapdump = require('heapdump');
var fs = require('fs');
var path = require('path');
fs.writeFileSync(path.join(__dirname, 'app.pid'), process.pid);
在业务代码中引入node-heapdump
以后,咱们须要在某个运行时期,向Node.js
进程发送SIGUSR2
信号,让node-heapdump
抓拍一份堆内存的快照。
$ kill -USR2 (cat app.pid)
这样在文件目录下会有一个以heapdump-<sec>.<usec>.heapsnapshot
格式命名的快照文件,咱们可使用浏览器的Developer Tools
中的Profiles
工具将其打开,并进行检查。
Navigation
Resource
User timing
Developer Tools - Profiles
JIT
与GC
优化(内存优化)number+number
,string+string
等等可使用JIT
优化,但特殊状况,如:number+undefined
没法被优化list
很大时, JIT
没法优化Type-specializing JIT优化
在V8引擎中全部的JAVASCRIPT对象都是经过堆来进行内存分配的。当咱们在代码中声明变量并赋值时,V8引擎就会在堆内存中分配一部分给这个变量。若是已申请的内存不足以存储这个变量时,V8引擎就会继续申请内存,直到堆的大小达到了V8引擎的内存上限为止(默认状况下,V8引擎的堆内存的大小上限在64位
系统中为1464MB
,在32位
系统中则为732MB
)
V8
引擎对堆内存中的JAVASCRIPT对象进行分代管理。
垃圾回收算法
Scavange
算法:经过复制的方式进行内存空间管理,主要用于新生代的内存空间;Mark-Sweep
算法和Mark-Compact
算法:经过标记来对堆内存进行整理和回收,主要用于老生代对象的检查和回收。回收对象
JAVASCRIPT
引擎不会把正在使用的对象进行回收。因此判断对象是否正在使用中的标准,就是是否仍然存在对该对象的 引用// 当代码执行完毕时,对象val和bar()并无被回收释放, // JAVASCRIPT代码中,每一个变量做为单独一行而不作任何操做, // JAVASCRIPT引擎都会认为这是对对象的访问行为,存在了对对象的引用 var val = 'hello world'; function foo() { return function() { return val; }; } global.bar = foo();
// 当代码执行完毕时,对象val和bar()并无被回收释放, // JAVASCRIPT代码中,每一个变量做为单独一行而不作任何操做, // JAVASCRIPT引擎都会认为这是对对象的访问行为,存在了对对象的引用 var val = 'hello world'; function foo() { return function() { return val; }; } global.bar = foo();
给DOM对象添加的属性是一个对象的引用。
var MyObject = {}; document.getElementByIdx_x('myDiv').myProp = MyObject;
var MyObject = {}; document.getElementByIdx_x('myDiv').myProp = MyObject;
解决方法:在window.onunload
事件中写上:
document.getElementByIdx_x('myDiv').myProp = null;
document.getElementByIdx_x('myDiv').myProp = null;
DOM对象与JS对象相互引用。
function Encapsulator(element) { this.elementReference = element; element.myProp = this; } new Encapsulator(document.getElementByIdx_x('myDiv'));
function Encapsulator(element) { this.elementReference = element; element.myProp = this; } new Encapsulator(document.getElementByIdx_x('myDiv'));
解决方法:在window.onunload
事件中写上:
document.getElementByIdx_x('myDiv').myProp = null;
document.getElementByIdx_x('myDiv').myProp = null;
给DOM对象用attachEvent
绑定事件。
function doClick() {} element.attachEvent("onclick", doClick);
function doClick() {} element.attachEvent("onclick", doClick);
解决方法:在onunload事件中写上:
element.detachEvent('onclick', doClick);
element.detachEvent('onclick', doClick);
从外到内执行appendChild
。这时即便调用removeChild
也没法释放。
var parentDiv = document.createElement_x("div"); var childDiv = document.createElement_x("div"); document.body.appendChild(parentDiv); parentDiv.appendChild(childDiv);
var parentDiv = document.createElement_x("div"); var childDiv = document.createElement_x("div"); document.body.appendChild(parentDiv); parentDiv.appendChild(childDiv);
解决方法:从内到外执行appendChild:
var parentDiv = document.createElement_x("div"); var childDiv = document.createElement_x("div"); parentDiv.appendChild(childDiv); document.body.appendChild(parentDiv);
var parentDiv = document.createElement_x("div"); var childDiv = document.createElement_x("div"); parentDiv.appendChild(childDiv); document.body.appendChild(parentDiv);
反复重写同一个属性会形成内存大量占用(但关闭IE后内存会被释放)。
for(i = 0; i < 5000; i++) { hostElement.text = "asdfasdfasdf"; } // 这种方式至关于定义了5000个属性
for(i = 0; i < 5000; i++) { hostElement.text = "asdfasdfasdf"; } // 这种方式至关于定义了5000个属性
解决方法:无, 避免这样书写代码。
IE下闭包
会引发跨页面内存泄露。
内存不是缓存
CollectGarbage。
CollectGarbage是IE的一个特有属性,用于释放内存的使用方法,将该变量或引用对象设置为null或delete而后在进行释放动做,
在作CollectGarbage前,要必需清楚的两个必备条件:(引用)。
404
JavaScript
和CSS
HTTP
请求IE
Firefox
中无论脚本是否可缓存, 都存在重复运算的问题)ETags
配置Entity
标签, 能够有效减小Web应用负载Keep-alive
机制减小TCP链接。BigPipe
)。Image Sprites
减小HTTP请求。