javascript高性能编程笔记(个人自用)

个人读书笔记,仅用于梳理知识点,摘抄很随意,若无意搜到的朋友不必细看,不然必凌乱

 

1.大多数浏览器使用单进程处理UI更新和javascript运行,而同一时间只能有一个任务被执行,因此<script>标签的出现使整个页面因脚本解析,运行被阻塞而出现等待.部分新浏览器允许一个<script>标签正在下载外部资源时,不必阻塞其它<script>标签.不幸的时,仍然阻塞其它资源(例如图片)的下载过程.
2.根据标准,<script>标签可以放在<head>和<body>标签中,浏览器是遇到<body>标签后才开始渲染页面,因此<script>标签在文档中最靠后的位置为</body>前,这种做法可以最大限度的降低js对页面渲染的阻塞.
3.减少<script>总数也可以改善性能,每个http请求都会产生额外的性能负担,下载一个100K的文件比下载四个25K的文件要快.(可利用Closure谷歌js,YUI Compressor,Combo Handler雅虎等根据压缩合并)
4.如果需向页面加载大量js,可先加载包含动态加载js所需的代码,然后再加载页面初始化所需的其它js.YUI3核心理念,用一个很小的初始化代码,下载其余的功能代码.通用工具有LazyLoad库(http://github.com/rgrove/lazyload)和LABjs(http://labjs.com),LABjs还可以管理函数与文件的依赖关系.
5.javascript有4种数据存储位置,Literal values 直接量,Variables 变量,Array items 数组项,Object members 对象成员,不同的存储位置可以对代码整体性能产生重要影响.直接量和局部变量访问速度非常快,数组项和对象成员需要更长时间.
6.局部变量比域外变量快,因为它位于作用域链的第一个对象中.变量在作用域链中的位置越深,访问所需的时间就越长.全局变量总是最慢的,因为它们总是位于作用域链的最后一环.避免使用with表达式,因为它会增加新作用域链在最前端,try-catch中的catch也具有同样效果.嵌套对象成员会造成重大性能影响,尽量少用.
7.当浏览器下载完所有页面HTML标记,javascript,css,图片后,它解析文件并创建两个内部数据结构,一颗DOM树和一颗渲染树.渲染树中为每个需要显示的DOM树节点存放至少一个节点(隐藏DOM除外).渲染树上的节点成为"框"或者"盒",符合css模型定义,将页面元素看作一个具有填充,边距,边框和位置的盒.一旦DOM树和渲染树构造完毕,浏览器就可以显示(绘制)页面上的元素了.
8. 当DOM改变影响到元素的几何属性(宽和高)等影响到其它元素的几何属性和位置.渲染树上受到影响的部分失效,然后重构(重排)渲染树.重构完成时,浏览器在一个重绘进程中重新绘制屏幕上受影响的部分.而背景颜色等不会影响几何的属性变化时,只需要重绘,不需要重排.重绘和重排都是负担很重的操作.
9.当需要对DOM元素进行多次修改时,可以通过以下三步减少重绘和重排次数,(1)从文档流中摘除该元素(2)各种改变(3)将元素带回文档流,方法有三(1)隐藏元素,进行修改,然后再显示 (2)使用一个文档片断在已存DOM之外创建一个子树,然后将它拷贝到文档中 (3)将原始元素拷贝到一个脱离文档的节点中,修改副本,然后覆盖原始元素
10.有些元素的动画流使用绝对定位,脱离流布局,可以减少DOM的重排
11.利用DOM的冒泡机制,最小化元素事件绑定.
12.js有4种循环语法for,while,do-while,for-in;因为for-in要迭代搜索实例或者原形的属性,因此比其它三种语法慢很多.另外尽量保证循环条件是局部变量,可明显提升效率.
13.如果循环次数过大(如几十万次),可考虑使用"达夫设备"优化
14.JQuery.each()方法实现了ECMA-262的新方法forEach(),可用于遍历数组.不过此基于函数的迭代会慢于基于循环的迭代.
15.一般来说,switch总是比if-else更快;当判断条件较多时,查表法比if-else和switch更快.
16.浏览器的调用栈尺寸限制了递归算法在js中的应用,如果遇到一个栈溢出错误,将方法修改为一个迭代算法或者使用制表法(记忆递归)可以避免重复工具.
17. str+="one"+"two" 改写为 str=str+"one"+"two",会避免产生一个临时变量.
18.正在表达式处理慢往往是因为匹配失败过程慢,而不是匹配成功过程慢.最理想的情况是,一个正在表达式的起始字元应当尽可能快速地测试并排除明显不匹配的位置.减少分支的数量,缩小它们的范围,当分支必不可少时,将常用分支放在最前面.
19.如果不需要一个后向引用,可通过(?:...)替代(...).
20.javascript和UI更新共享进程通常被称作浏览器UI线程,此UI线程围绕着一个简单的队列系统工作,任务被保存到队列中直至进程空闲.一旦空闲,队列中的下一个任务将被检索和运行.这些任务不是运行javascript代码,就是执行UI更新,包括重绘和重排版.
21.对于js任务因为复杂性不能在100毫秒或更少时间内完成,这种情况下,理想方法是让出对UI线程的控制,可使用定时器setTimeout()或setInterval(),等待一段时间后再将js任务添加到UI队列.定时器与UI线程交互的方式有助于分解长运行脚本成为较短的片断.
22.定时器代码只有等创建它的函数运行完成之后,才有可能被执行.如果定时器很小,然后在创建定时器之后又调用了另外一个函数,定时器代码有可能在创建函数完成之前加入队列,比如
var button = document.getElementById("my-button");
button.onclick = function{
     oneMethod();
     setTimeout(function(){
          document.getElementById("notice").style.color="red";
          },50);
     anotherMethod(); 如果anotherMethod执行时间超过50毫秒,那么定时器代码将在onclick处理完成之前加入到队列中
}
23.在任何一种情况下,创建一个定时器造成UI线程暂停,如同它从一个任务切换到下一个任务.因此,定时器代码复位所有相关的浏览器限制,包括长运行脚本时间.此外,调用栈也在定时器代码中复位为零.这一特效使得定时器成为长运行javascript代码理想的跨浏览器解决方案.(因为浏览器会将执行时间超过5秒的脚本关闭)
24.如果一个循环占用太长时间,可以考虑用定时器优化,如
for(var i=0,len=items.length;i<len;i++){
     protoss(items[i]);
}     如果此处理过程不必同步且数据不必按顺序处理,则可优化为:
     var todo = items.concat();
     setTimeout(function(){
     process(todo.shift());
     if(todo.length>0){
          setTimeout(arguments.calle,25);
     }else{
     callback(items);
     }
},25)
25. 同理,一个函数太长,可以考虑是否能够分解成短时间完成的小函数,如:
function saveDocument(id){
     openDocument(id);
     writeText(id);
     closeDocument(id);
     updateUI(id);
} 优化为数组处理模式:
function saveDocument(id){
     var tasks = [openDocument,writeText,closeDocument,updateUI];
     setTimeout(function(){
     var task = tasks.shift();
     task(id);
     if(tasks.length>0){
          setTimeout(arguments.callee,25);
     }
     },25);
}  这个版本将每个方法放入任务数组,然后在每个定时器中调用一个方法.
26. js可连续运行的最大时间是100毫秒,建议将这个数字削减一半,不要让任何js代码持续运行超过50毫秒,确保不影响用户体验.可用Date对象跟踪:
var start = +new Date(),stop;
someLongProcess();
stop = +new Date();
if(stop-start<50){
     alert("Just about right.");
}else{
     alert("Taking too long.");
}
应用:
function timedProcessArray(items,process,callback){
     var todo = items.concat();
     setTimeout(function(){
     var start = +new Date();
     do {
     process(todo.shift());
     }while(todo.length>0 && (+new Date()-start<50));
     if(todo.length>0){
     setTimeout(arguments.calle,25);
     }else{
     callback(items);
     }
     },25);
}
27. 过渡的使用定时器会对性能产生负面影响,使用定时器序列,同一时间只有一个定时器存在,只有当这个定时器结束时才能创建一个新的定时器.以这种方式使用定时器不会带来性能问题.
28.实验发现,间隔在1秒或以上的定时器,几乎不影响整个网页应用的响应,而且200毫秒以下的定时器重复使用,程序会明显变慢.因此建议限制高频率重复定时器的数量,创建单独重复定时器,每次执行多个操作.
29 web workers,作为HTML5的一部分,工人线程AP,已经分离成独立的规范,在自己的线程中运行js.但工人线程不绑定UI线程,因此访问浏览器资源受到限制.工人线程包含一个navigator对象(只包含四个属性appName,appVersion,uerAgent和platform),一个location对象(所有属性只读),一个self对象指向全局工人线程对象,一个importScripts()方法,使工人线程可以加载外部JavaScript文件,所有ECMAScript对象,XMLHttpRequest构造器,setTimeout()和setInterval()方法,close()方法可以立即停止工人线程.
30.因为工人线程有不同的全局运行环境,因此需要创建一个完全独立的js文件, var worker = new Worker("code.js");此代码一旦执行,将为指定文件创建一个新线程和一个新的工人线程运行环境.此文件被异步下载,至少下载并运行完之后才启动工人线程.
31.工人线程和网页代码通过事件接口进行交互,网页代码可通过postMessage()方法向工人线程传递数据,它接收单个参数,即传递给工人线程的数据.此外,在工人线程中还有onmessage事件句柄用于接收信息,例如:
 var worker = new Worker("code.js");
worker.onmessage = function(event){
     alert(event.data);
}
worker.postMessage("Nicholas");
工人线程从message事件中接收数据,事件对象具有一个data属性存放传入的数据,工人线程可通过它自己的postMessage()方法将信息返回给页面.
self.onmessage = function(event){
     slef.postMessage("hello,"+event.data+"!");
}
消息系统是页面和工人线程之间唯一的交互途径,传递过去的值或对象都是完全独立的表述(深拷贝).
32.当工人线程通过importScripts()方法加载外部js时,它接收一个或多个URL参数.工人线程以阻塞方式调用importScripts(),直到所有文件加载完成并执行之后,脚本才继续运行.由于工人线程在UI线程之外运行,这种阻塞不会影响UI相应, importScripts("file1.js","file2.js");
33.工人线程适合于那些纯数据,或者与浏览器UI没关系的长运行脚本.任何超过100毫秒的处理,都可以考虑工人线程方案是不是比基于定时器的方案更合适.
34.有五种常用技术用于向服务器请求数据
 XMLHttpRequest(XHR)
 Dymamic script tag insertion  动态脚本标签插入
 iframes
 Comet
 Multipart XHR  多部分的XHR
35.其中XMLHttpRequest最常用,当使用XHR请求数据时,可选择post或get,如果请求不改变服务器状态只是取回数据,则使用GET.GET请求被缓冲起来,如果你多次提取相同的数据可提高性能.只有当URL和参数长度超过了2048个字符才使用post提取数据.
36.动态脚本插入可以跨域,但不能发送请求头,只能get,不能post,不能设置超时或重试.实际上,你不需要知道它是否失败了,必须等待所有数据返回后才可以访问它们.你不能使用裸XML,或者裸JSON,无论什么格式,必须在一个回调函数之中被组装起来.
var scriptElement = document.createElement('script');
scriptElement.src = 'http://any-domain.com/javascript/lib.js';
document.getElementByTagName_r('head')[0].appendChild(scriptElement);
function jsonCallback(jsonString){
     var data = ('('+jsonString+')');
}
其中lib.js文件内容为:
jsonCallback({"status":1,"colors":["#fff","#000","#ff0000"]});
37.多部分XHR技术允许只用一个HTTP请求就可以从服务器端获取多个资源.它通过将资源(css文件,html片断,js代码,base64编码图片)打包成一个由特定分隔符界定的大字符串,从服务器端发送到客户端.js根据媒体类型和其它"信息头"解析出每个资源.下面函数将用于js代码,css和图片转换为浏览器可用资源:
function handleImageData(data,mimeType){
     var img=document.createElement('img');
     img.src = 'data:'+mimeType+';base64,'+data;
     return img;
}
function handleCss(data){
     var style = document.createElement('style');
     style.tyle = 'text/css';
     var node = document.createTextNode(data);
     style.appendChild(node);
     document.getElementByTagName_r('head')[0].appendChild(style);
}
function handleJavaScript(data){
     (data);
}
由于MXHR响应报文越来越大,有必要在每个资源收到时立刻处理,可以通过监听readyState 3实现.每隔15毫秒检查一次响应报文数据,数据片断被手机起来直到发现一个分隔符,然后一切都作为一个完成的资源处理.此技术的最大缺点是此方法获得的资源不能被浏览器缓存.
38.有时你部关心接收数据,而只要将数据发送给服务器,如记录日志等.当数据只需要发送给服务器时,有两种广泛应用的技术:XHR和灯标.XHR技术同上,而灯标与动态脚本标签插入非常类似,js用于创建一个新的Image对象,将src这只为服务器上的一个脚本文件的URL.此URL包含我们打算通过GET格式传回键值对数据.注意并没用创建img元素或者将它们插入到DOM中.
var url = '/status_tracker.php';
var params = ['step = 2','time =1248027314'];
(new Image()).src=url+'?'+params.join('&');
服务器取得此数据并保存下来,而不必向客户端返回什么,因此没用实际的图像显示,这是将信息发回服务器的最有效方法.可以监听Image对象的load事件,告诉服务器是否成功接收了数据.你还可以检查服务器返回图片的宽度和高度(如果返回了一张图片)并用这些数字通过你服务器的状态,例如,宽度为1表示"成功",2表示"重试". 如果你不需要为此响应返回数据,那么你应该发送一个204 No Content响应代码,无消息正文.它将组织客户端继续等待.
var url = '/status_tracker.php';
var params = ['step = 2','time =1248027314'];
var beacon = new Image();
beacon.src = url + '?' + params.join('&');
beacon.onload = function{
     if(this.width == 1){
      //success
     }
     else if (this.width ==2){
     //Failure,create another beacon and try again
     }
};
beacon.onerror = function {
     //Error,wait a bit,then create another beacon and try again
}
39.在考虑数据传输技术时,必须考虑功能集,兼容性,性能,方向.在考虑数据格式时,唯一需要比较的就是速度.一般有XML,JSON,HTML,Custom Formatting(自定义格式)四种数据格式,总的来说越轻量级越好,最好是json或字符分隔的自定义格式.当创建自定义格式时,最重要的是决定采用何种分隔符,理想情况下,它应当是一个单字符,而且不能存在于你的数据之中.JSON-P数据,可以跨域,但不应涉及敏感数据.
40.最快的Ajax请求就是不用.有两种主要方法避免发出一个不必要的请求.(1)在服务器端,设置HTTP头,确保返回报文将被缓存在浏览器中 (2)在客户端,于本地缓存已获取的数据,不要多次请求同一个数据.  前一种技术最容易设置和维护,而第二个给你最大程度的控制.
41.如果你希望Ajax响应报文能够被浏览器所缓存,你必须在发起请求时使用GET方法.但这还不充分,你必须在响应报文中发送正确的HTTP头.Expires头告诉浏览器应当缓存响应报文多长时间.其值是一个日期,当过期之后任何对该URL发起的请求都不再从缓存中获得,而要重新访问服务器.一个Expres头如下: Expires:Mon,28 Jul 2014 23:30:99 GMT
42.除了依赖浏览器处理缓存之外,还可以用手工方法实现它,直接存储那些从服务器收到的响应报文,可将响应报文存放在一个对象中,以URL为键值索引它.这事一个XHR封装,它首先检查一个URL此前是否被取用过:
var localCache={};
function xhrRequest(url,callback){
     //check the local cache for this URL
     if(localCache[url]){
     callback.success(localCache[url]);
     return;
     }
     //If this URL wasn't found in the cache,make the request
     var req = createXhrObject();
     req.onerror = function(){
          callback.error();
     }
     req.onreadystatechange = function(){
          if(req.readyState == 4){
               if(req.responseText ===''||req.status == '404'){
               callback.error();
               return;
               }
          //Store the response on the local cache
          localCache[url] = req.responseText;
          callback.success(req.responseText);
          }
     };
     req.open("GET",url,true);
     req.send(null);
}
总的来说,设置一个Expires头是更好的解决方案.这比较容易,而且其缓存内容可以跨页面或者跨对话.
43.当在js代码中执行另一段js代码时,将付出二次评估的代价.二次评估是一项昂贵的操作,eval(),Function(),setTimeout()和setInterval(),尽量避免使用eval和Function,然后在setTimeout和setInterval中传入函数代替直接传入代码.
44.消除代码的重复逻辑,可利用延迟加载或条件预加载等消除重复判断.如:
var addHandler = document.body.addEventListener?function(){ 代码 }:function(){代码};
45.位操作运算符运行非常快速,四种位操作符 AND(&),OR(|),XOR(^),NOT(~),在位运算中,数字被转换为有符号32位格式,每种操作均直接操作在这个32位数上实现结果.
46.无论怎样优化javascript代码,它永远不会比javascript引擎提供的原生方法更快.因为原生部分都是用低级语言写的,诸如c++.这意味着这些方法被编译成机器码,作为浏览器的一部分.经验不足的js开发者经常犯的一个错误是在代码中进行复杂的数学运算,而没有使用内置的Math对象中那些性能更好的版本.
47.jquery引擎被认为是最快的css查询引擎,但它仍比原生的方法慢.原生的querySelector()和querySelectorAll()方法完成它们的任务时,平均只需要基于js的css查询的10%的时间.
48.减少http请求,合并并压缩js,css等,可通过Apache Ant的concat任务合并,YUI压缩器压缩,google还有更霸道的工具,传输的时候可以通过gzip再压缩,可直接在Apache中启用gzip压缩功能.
49.预处理js源文件并不会使程序更快,但它允许你在代码中加入其它语言才有的一些特性,例如用条件体插入一些测试代码,来衡量你的应用程序的性能.
50.HTML5有离线缓存技术,通过manifest配置要缓存的文件.
51.部署CDN可以极大的减少用户网络延迟.
52.工具:性能分析,
     var Timer={
     _data:{},
     start:function(key){
     Timer._data[key] = new Date(;)
     },
     stop:function(key){
     var time=Timer._data[key];
     if(time){
     Timer._data[key] = new Date()-time;
     }
     },
     getTime:function(key){
     return Timer._data[key];
     }
     };
Timer.start('createElement');
for(i=0;i<count;i++){...}
Timer.stop('createElement');
alert('create'+count+'in'+Timer.getTime('createElement');
YUI分析器 http://developer.yahoo.com/yui/profiler/    除了计时功能,还提供了用于函数,对象,和构造器的性能分析接口,还暴击性能分析的详细报告.如:
var count=10000,i,element;
Y.Profiler.start('createElement');
for(i=0;i<count;i++){
     element = document.createElement('div');
}
Y.Profiler.stop('createElement');
alert('create'+count+'in'+Y.Profiler.getAverage('createElement')+'ms');
很明显,它改进了内敛Data和Timer方法,提供额外性能数据包含调用次数,平均时间,最小时间,最大时间等.
53.firebug能深入每个被调用函数的细节,提供高精确性能数据和变量查看功能.console.profile()启动分析器,
console.profile("regexTest");
regexTest('foobar','foo');
console.profileEnd();
console.profile("indexOfTest");
indexOfTest('foobar','foo');
console.profileEnd();
另外console.time()函数有助于测量循环和其他分析器不能监视的操作:
console.time("cache node");
for(var box = document.getElementById("box")),i = 0;i<100;i++){
value = parseFloat(box.style.left)+10;
box.style.left = value+"px";
}
console.timeEnd("cache node");   在定时器结束之后,时间被输出到控制带上
54. page speed最初是google内部开发的一个工具,后来作为Firebug插件发布,像Firebug网络面板一样提供了关于页面资源加载的信息.除了加载时间和HTTP状态,它还显示javascript解析和运行所花费的时间,指出造成延迟的脚本,并报告那些没有被使用的函数.  http://code.google.com/speed/page-speed 
55. Fiddle,一个HTTP调试代理,检查资源在线传输情况,以单位加载瓶颈
56. Yslow 能够深入视察页面整体加载和运行过程的性能
57. dynaTrace Ajax Edition Ajax版的dynaTrace    dynaTrace是一个强大的java/.net性能诊断工具,它的开发人员已经发布了一个"Ajax版"用于策略浏览器性能.这个工具免费提供了一个"终端到终端"性能分析器,从网络和页面渲染,到脚本运行时间和CPU占用率都能分析.

转载于:https://www.cnblogs.com/Cohlint/archive/2013/01/14/2860488.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值