前端性能优化

AJax 优化

  • 缓存 Ajax
  • 请求尽可能使用GET, 仅取决于cookie数量

Cookie 优化

  • 减小Cookie的大小
  • 使用无Cookie的域来存放静态资源(能够利用CDN)

DOM 优化

优化节点修改(使用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 <= 1000javascript

DOM操做优化

DOM操做性能缘由php

  • DOM元素过多致使元素定位缓慢。
  • 大量的DOM接口调用。
  • DOM操做触发频繁的 reflow(layout)(计算页面元素的几何信息)和 repaint(绘制页面元素)
  • layout发生在repaint以前,因此layout相对来讲会形成更多性能损耗。
  • 对DOM进行操做会致使浏览器执行回流reflow。

优化DOM操做css

  • JAVASCRIPT执行时间是很短的。
  • 最小化DOM访问次数,尽量在js端执行。
  • 若是须要屡次访问某个DOM节点,请使用局部变量存储对它的引用
  • 谨慎处理HTML集合(HTML集合实时连系底层文档),把集合的长度缓存到一个变量中,并在迭代中使用它,若是须要常常操做集合,建议把它拷贝到一个数组中
  • 若是可能的话,使用速度更快的API,好比querySelectorAllfirstElementChild
  • 要留意重绘和重排。
  • 批量修改样式时,离线操做DOM树。
  • 使用缓存,并减小访问布局的次数。
  • 动画中使用绝对定位,使用拖放代理
  • 使用事件委托来减小事件处理器的数量

优化DOM交互

最小化现场更新
多使用innerHTML替代createElement()appendChild():html

reflow回流

发生场景前端

  • 改变窗体大小。
  • 更改字体。
  • 添加移除stylesheet块。
  • 内容改变哪怕是输入框输入文字。
  • CSS虚类被触发如 :hover。
  • 更改元素的className。
  • 当对DOM节点执行新增或者删除操做或内容更改时。
  • 动态设置一个style样式时(好比element.style.width="10px")。
  • 当获取一个必须通过计算的尺寸值时,好比访问offsetWidth、clientHeight或者其余须要通过计算的CSS值。

解决关键: 限制DOM操做所引起的回流java

  • 在对当前DOM进行操做以前,尽量多的作一些准备工做,保证N次建立,1次写入。
  • 在对DOM操做以前,把要操做的元素,先从当前DOM结构中删除:
    • 经过removeChild()或者replaceChild()实现真正意义上的删除。
    • 设置该元素的display样式为“none”。
  • 每次修改元素的style属性都会触发回流操做。element.style.backgroundColor = "blue";
    • 使用更改className的方式替换style.xxx=xxx的方式。
    • 使用style.cssText = '';一次写入样式。
    • 避免设置过多的行内样式。
    • 添加的结构外元素尽可能设置它们的位置为fixedabsolute
    • 避免使用表格来布局。
    • 避免在CSS中使用JavaScript expressions(IE only)
  • 将获取的DOM数据缓存起来。这种方法,对获取那些会触发回流操做的属性(好比offsetWidth等)尤其重要。
  • 当对HTMLCollection对象进行操做时,应该将访问的次数尽量的降至最低,最简单的,你能够将length属性缓存在一个本地变量中,这样就能大幅度的提升循环的效率。

repaint重绘

  • 注意JavaScript代码优化, 减小重绘。
  • 使用HTML5和CSS3的一些新特性。
  • 避免在HTML里面缩放图片。
  • 避免使用插件。
  • 确保使用正确的字体大小。

HTML 优化

  • 插入HTML。(找一个容器元素,并使用innerHTML来将HTML代码插入到页面中。)
  • 避免空的src和href
    • 当link标签的href属性为空、script标签的src属性为空的时候,浏览器渲染的时候会把当前页面的URL做为它们的属性值,从而把页面的内容加载进来做为它们的值。
  • 为文件头指定Expires
  • 重构HTML,把重要内容的优先级提升
  • Post-load(次要加载)不是必须的资源。
  • 利用预加载优化资源。
  • 合理架构,使DOM结构尽可能简单。
  • 利用LocalStorage合理缓存资源。
  • 尽可能避免CSS表达式和滤镜
  • 尝试使用defer方式加载Js脚本
  • 新特性:will-change,把即将发生的改变预先告诉浏览器。
  • 新特性Beacon,不堵塞队列的异步数据发送。
  • 尽可能多地缓存文件。
  • 使用HTML5 Web Workers来容许多线程工做
  • 为不一样的Viewports设置不一样大小的Content
  • 正确设置可Tap的目标的大小。
  • 使用响应式图片
  • 支持新接口协议(如HTTP2)。
  • 将来的缓存离线机制Service Workers
  • 将来的资源优化Resource Hints(preconnect, preload, 和prerender)
  • 使用Server-sent Events
  • 设置一个Meta Viewport
  • 避免跨域
    • 同域:注意避免反斜杠 “/” 的跳转;
    • 跨域:使用Alias或者mod_rewirte创建CNAME(保存域名与域名之间关系的DNS记录)

动画优化

CSS 优化

慎重选择高消耗的样式

高消耗属性在绘制前须要浏览器进行大量计算: 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-heightjquery

正确使用 Display 的属性

Display 属性会影响页面的渲染,请合理使用。web

  • display: inline后不该该再使用 width height margin padding 以及 float
  • display: inline-block 后不该该再使用 float
  • display: block 后不该该再使用 vertical-align
  • display: table-* 后不该该再使用 margin 或者 float

不滥用 Float

Float在渲染时计算量比较大,尽可能减小使用。

动画性能优化

动画的基本概念:

  • 帧:在动画过程当中,每一幅静止画面即为一“帧”;
  • 帧率:即每秒钟播放的静止画面的数量,单位是fps(Frame per second);
  • 帧时长:即每一幅静止画面的停留时间,单位通常是ms(毫秒);
  • 跳帧(掉帧/丢帧):在帧率固定的动画中,某一帧的时长远高于平均帧时长,致使其后续数帧被挤压而丢失的现象。

通常浏览器的渲染刷新频率是 60 fps,因此在网页当中,帧率若是达到 50-60 fps 的动画将会至关流畅,让人感到温馨。

  • 若是使用基于 javaScript 的动画,尽可能使用 requestAnimationFrame. 避免使用 setTimeoutsetInterval.
  • 避免经过相似 jQuery animate()-style 改变每帧的样式,使用 CSS 声明动画会获得更好的浏览器优化。
  • 使用 translate 取代 absolute 定位就会获得更好的 fps,动画会更顺滑。
  • 动画效果在缺乏硬件加速支持的状况下反应缓慢,例如手机客户端。
  • 特效只在确实可以改善用户体验时才使用
  • 至少给用户一个能够禁用动画效果的选项
  • 设置动画元素为 position:absolute; 或 position:fixed(只须要repaint),而position: staticposition: relative元素应用动画效果会形成频繁的reflow
  • 使用一个timer完成多个元素的动画
  • 使用一个timer完成多个对象的动画效果
  • 以脚本为基础的动画, 由浏览器控制动画的更新频率
  • 避免使用 jQuery 实现动画
    • 禁止使用 slideUp/Down() fadeIn/fadeOut() 等方法;
    • 尽可能不使用 animate() 方法;

高性能动画

多利用硬件能力,如经过 3D 变形开启 GPU 加速(3D 变形会消耗更多的内存和功耗)

通常在 Chrome 中,3D或透视变换(perspective transformCSS属性和对 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 选择器对性能的影响源于浏览器匹配选择器和文档元素时所消耗的时间,因此优化选择器的原则是应尽可能避免使用消耗更多匹配时间的选择器。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) }

JS 载入优化

  • 使用加快JS载入速度的工具, 使JS并行载入
  • 使用CDN
  • 网页尾部载入JS, 头部载入必须异步载入
  • 跟踪代码等跟页面关系不大的代码异步载入或延迟载入
  • 将JS打包成PNG文件, 以后进行拆包, 只要使用画布API的getImageData()。能够在不缩小数据的状况下,多压缩35%左右。并且是无损压缩,对比较庞大的脚原本说,在图片指向画布、读取像素的过程当中,会有一段读取时间。
  • 设置Cache-ControlExpires

    (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优化

优化原则

  • 只须要为IE6(未打补丁的JScript 5.6或更早版本)作优化
  • 解释执行的状况下,在全部操做中,函数调用的效率是较低的。此外,过深的prototype继承链或者多级引用也会下降效率。
  • JS优化老是出如今大规模循环的地方
  • 尽可能避免过多的引用层级和没必要要的屡次方法调用
  • arguments优化:
    • 若是一个可变参数的简单函数成为性能瓶颈的时候,能够将其内部作一些改变,不要访问arguments,而是经过对参数的显式判断来处理
    • 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()
  • 避免使用属性访问方法, 直接访问属性
  • 减小使用元素位置操做
    • 通常浏览器都会使用增量reflow的方式将须要reflow的操做积累到必定程度而后再一块儿触发,可是若是脚本中要获取如下属性,那么 积累的reflow将会立刻执行,用来获得准确的位置信息。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;
  • 变量查找优化
    • 变量声明带上varES6中为let
    • 经过一条语句声明变量, 变量用,分隔
    • 缓存重复使用的全局变量(重复调用方法, 也能够用局部缓存提速)
  • 善用回调(后续传递风格(Continuation Passing Style, CPS)的技术)
    • 若是传入的参数是基础类型(如字符串、数值),回调函数中传入的形参就会是复制值,业务代码使用完毕之后,更容易被回收。
    • 经过回调,咱们除了能够完成同步的请求外,还能够用在异步编程中,这也就是如今很是流行的一种编写风格。
    • 回调函数自身一般也是临时的匿名函数,一旦请求函数执行完毕,回调函数自身的引用就会被解除,自身也获得回收。
      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)

对象优化

  • 对象建立
    • 尽可能减小没必要要的建立(JS的垃圾回收调度算法, 会随着对象个数增长, 性能会开始严重降低(复杂度O(n^2)))
    • 尽可能采用缓存缓存复杂的Javascript对象
    • 尽可能使用JSON建立对象, 而不是var obj = new Object()
  • 对象查找
    • 避免对象的嵌套查询(由于JAVASCRIPT的解释性,a.b.c.d.e嵌套对象,须要进行4次查询,嵌套的对象成员会明显影响性能。)
    • 若是出现嵌套对象, 利用局部变量, 把它缓存, 放入临时的地方查询
  • 对象属性
    • 先从本地变量表找到对象。
    • 而后遍历属性。
    • 若是在当前对象的属性列表里没找到。
    • 继续从prototype向上查找。
    • 且不能直接索引,只能遍历。

数组优化

  • 当须要使用数组时,可以使用JSON格式的语法
  • 若是须要遍历数组,应该先缓存数组长度,将数组长度放入局部变量中,避免屡次查询数组长度。

循环优化

  • 循环性能 do-while > for(;;) ≈ while() > for(in)
    • for(;;)
      • 推荐使用for循环,若是循环变量递增或递减,不要单独对循环变量赋值,而应该使用嵌套的++或–-运算符。
      • 代码的可读性对于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, SeaJS, OzJS 等前端模块化加载解决方案,都是采用相似的形式:
/**RequireJS**/ define(['jquery'], function($) { // 主业务代码 }); /**SeaJS**/ define('m​​odule', ['dep', 'underscore'], function($, _) { // 主业务代码 });/**RequireJS**/ define(['jquery'], function($) { // 主业务代码 }); /**SeaJS**/ define('m​​odule', ['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!"); } });

性能测试工具

js性能优化和内存泄露问题及检测分析工具

  • 性能优化ajax工具diviefirebug
  • web性能分析工具YSlow
    • performance性能评估打分,右击箭头可看到改进建议。
    • stats缓存状态分析,传输内容分析。
    • components全部加载内容分析,能够查看传输速度,找出页面访问慢的瓶颈。
    • tools能够查看js和css,并打印页面评估报告。
  • 内存泄露检测工具sIEve
    • sIEve是基于IE的内存泄露检测工具,须要下载运行,能够查看dom孤立节点和内存泄露及内存使用状况。
    • 列出当前页面内全部dom节点的基本信息(html id style 等)
    • 页面内全部dom节点的高级信息 (内存占用,数量,节点的引用)
    • 能够查找出页面中的孤立节点
    • 能够查找出页面中的循环引用
    • 能够查找出页面中产生内存泄露的节点
  • 内存泄露提示工具leak monitor
    • leak monitor在安装后,当离开一个页面时,好比关闭窗口,若是页面有内存泄露,会弹出一个文本框进行即时提示。
  • 代码压缩工具
    • YUI压缩工具
    • Dean Edwards Packer
    • JSMin
    • Uglify
  • Blink/Webkit浏览器中(ChromeSafariOpera),咱们能够借助其中的Developer ToolsProfiles工具来 对咱们的程序进行内存检查

Node.js中的内存检查

  • OneApm 或 alinode 进行线上监控
  • Node.js中,咱们可使用node-heapdumpnode-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工具将其打开,并进行检查。

分析浏览器提供的Waterfall图片来思考优化入口。

新的测试手段Navigation Resource User timing

Developer Tools - Profiles

JITGC优化(内存优化)

  • number+numberstring+string 等等可使用JIT优化,但特殊状况,如:number+undefined没法被优化
  • list很大时, JIT没法优化

Type-specializing JIT优化

  • 使用代价
    • 前置的扫描类型
    • 编译优化。
  • 使用场景
    • 热点代码。
    • 经过启发式算法估算出来的有价值的代码。
  • 当变量类型 发生变化时,引擎有2种处理方式:
    • 少许变动,重编译,再执行。
    • 大量变动,交给JIT执行。
  • 数组,object properties, 闭包变量 不在优化范畴之列。

JavaScript的内存回收机制

在V8引擎中全部的JAVASCRIPT对象都是经过堆来进行内存分配的。当咱们在代码中声明变量并赋值时,V8引擎就会在堆内存中分配一部分给这个变量。若是已申请的内存不足以存储这个变量时,V8引擎就会继续申请内存,直到堆的大小达到了V8引擎的内存上限为止(默认状况下,V8引擎的堆内存的大小上限在64位系统中为1464MB,在32位系统中则为732MB

V8引擎对堆内存中的JAVASCRIPT对象进行分代管理。

  • 新生代 即存活周期较短的JAVASCRIPT对象,如临时变量、字符串等
  • 老生代 则为通过屡次垃圾回收仍然存活,存活周期较长的对象,如主控制器、服务器对象等。

垃圾回收算法

  • Scavange算法:经过复制的方式进行内存空间管理,主要用于新生代的内存空间;
  • Mark-Sweep算法和Mark-Compact算法:经过标记来对堆内存进行整理和回收,主要用于老生代对象的检查和回收。

回收对象

  • 当函数执行完毕时,在函数内部所声明的对象不必定就会被销毁。
  • 为了保证垃圾回收的行为不影响程序逻辑的运行,JAVASCRIPT引擎不会把正在使用的对象进行回收。因此判断对象是否正在使用中的标准,就是是否仍然存在对该对象的 引用
  • 引用(Reference)是JAVASCRIPT编程中十分重要的一个机制。是指代码对对象的访问这一抽象关系
  • JAVASCRIPT的引用是能够进行转移的,那么就有可能出现某些引用被带到了全局做用域,但事实上在业务逻辑里已经不须要对其进行访问了,这个时候就应该被回收,可是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
  • 删除重复的JavaScriptCSS
    • 重复调用会增长额外的HTTP请求
    • 屡次运算也会浪费时间(IE Firefox中无论脚本是否可缓存, 都存在重复运算的问题)
  • ETags配置Entity标签, 能够有效减小Web应用负载
  • 权衡DNS查找次数
    • 减小主机名能够节省响应时间。但同时也会减小页面中并行下载的数量(IE浏览器在同一时刻只能从同一域名下载两个文件)
  • 经过Keep-alive机制减小TCP链接。
  • 经过CDN减小延时。
  • 平行处理请求(参考BigPipe)。
  • 经过合并文件或者Image Sprites减小HTTP请求。
  • 减小重定向( HTTP 301和40x/50x)。