高性能Javascript笔记
.) 下载并执行javacript会阻塞页面渲染,因此比较推荐js脚本放在页面底部,在</body>标签上边...
.) 尽量减少script标签。如果外链多个script文件,能合并就合并(利用合并的工具),同样适用内嵌的。
.) 动态脚本加载,无阻塞的比较好的解决方案:创建script DOM
比如:
var script = document.createElement("script"); script.type = "text/javascript"; //Firefox, Opera, Chrome, Safari 3+ //js文件src下载完的触发事件 script.onload = function(){ alert(1); }; script.src = "jquery.js"; document.getElementsByTagName("head")[0].appendChild(script);
.) 另一种无阻塞载入,利用xhr对象获取脚本注入,这种方法不能跨域获取(--ajax
var xhr = new XMLHttpRequest(); xhr.open("get", "file1.js", true); xhr.onreadystatechange = function(){ if (xhr.readyState == 4){ if (xhr.status >= 200 && xhr.status < 300 || xhr.status == 304){ var script = document.createElement ("script"); script.type = "text/javascript"; script.text = xhr.responseText;// 相当于内联聊本的<script>标签 document.body.appendChild(script); } } }; xhr.send(null);
事件委托:
dom如下所示,
span上都要有点击事件的反馈。如果在每个span上绑定click事件的话,当li很多时候,这样比较浪费性能。
事件绑定占用了处理时间,浏览器需要跟踪每个事件处理器,占用了更多内存。
此时可以使用事件委托。利用冒泡机制,在其父节点,比如只需要在ul上监听一个点击事件。然后判断节点哪个是哪个就ok了。
<div> <ul id="ul"> <li> <span id="a">aaaaa</span> </li> <li> <span id="b">bbbb</span> </li> <li> <span id="c">ccccc</span> </li> <li> <span id="d">ddddd</span> </li> </ul> </div> <script type="text/javascript"> $('#ul').click(function(e){ var target = e.target; // 只有点击的是span标签才触发事件 if(target.nodeName.toLowerCase() != 'span'){ return false; } var id = target.id; // 判断目标对象是哪个span switch(id){ case 'a': alert(1);break; case 'b': alert(2);break; case 'c': alert(3);break; case 'd': alert(4);break; } }); </script>
字符串连接;
str += "one" + "two"; str += "one"; str += "two"; str = str + "one" + "two";
第一行代码比较浪费,内存需要分配一个临时字符串来连接 "onetwo",然后再连str。
剩下两种方式,则不需要内存额外分配变量。
作用域链和原型链得知,一个全局变量在局域使用,最好是赋值给局域变量。链上远的变量查找更耗时,更消耗性能。
在操作dom集合(类数组而不是数组)的时候,涉及到length,尽量把这个集合的length赋值给一个变量,然后在循环里使用这个变量。遍历集合没有遍历数组快。
大多数时候没必要使用eval()和Function(),每次调用eval()时都要创建一个新的解释器/编译器示例。
尽量避免使用他们。至于setTimeout()和setInterval(),建议传入函数而不是字符串作为第一个参数。比如:
setTimeout(function(){ sum = a+b; }, 100);
使用Object/Array时,建议使用直接量,这样更快。
比如:
var obj = new Object(); obj.name = 'a'; obj.age = 12; // 更推荐这种方式: var obj = { name: 'a', age: 12 }; // ============ // 数组比如: var arr = new Array(); arr[0] = 'a'; arr[1] = 'b'; // 更推荐这种: var arr = ['a', 'b'];
不要重复工作:
比如根据一个判断(比如这里的 a 和 b 大小比较)处理一个逻辑:
var a=1; var b=2; function test(){ if(b>a){ xxx;// 几行代码 } else { yyy;// } }
比如这个 test() 函数在页面上可能会频繁遭到调用,那么每次都要去判断 a 和 b 的大小(如果是更复杂的逻辑判断,那么频繁调用一定会浪费性能)。那么不如在代码中重新覆盖这个 test() 函数:
function test(){ if(b>a){ test = function(){// 重新给test赋值 xxx; }; } else { test = function(){ yyy; }; } test();// 当然结尾不要忘记执行 }
这样的话,下一次再去调用这个test()的话,直接就是了。
数字运算的话,可以多考虑下,是否可以按位运算。比如奇数偶数这个...和1按位与操作。
尽量使用原生js,比较原生的都是js引擎低级编程语言编译好的,要更快。
Ajax:
常用的向服务器请求数据的方法:
1. XMLHttpRequest(XHR) 最常用的技术
2. 动态脚本注入
3. Multipart XHR
1). xhr(ajax常用技术,IE低版本要使用 ActiveXObject )是最常用的,它允许异步发送和接收数据。浏览器支持好,get、post方式均可,可以读取http响应头信息和响应文本。缺点不能跨域。
代码:
var url = '/data.php?a=1&b=2'; var req = new XMLHttpRequest(); req.onreadystatechange = function(){ if(req.readyState === 4){// 4表示整个响应接收完毕;3表示接收到部分信息,还没完 var responseHeaders = req.getAllResponseHeaders();// 获取响应头信息 var data = req.responseText;// 获取响应文本 // 逻辑处理 // ... } } req.open('GET', url, true);// true表示异步 req.setRequestHeader('', '');// 可以设置请求头信息 req.send(null);// 发送请求
2). 动态脚本注入
这个方法利用动态创建 script 元素来请求数据。它克服了xhr最大限制:可以跨域请求。这当然算是个Hack。
xhr的缺点:不可以设置请求头信息;也只能使用get方式;需要等这个脚本下载完,才能访问它们;返回的数据需要封装在函数里。
因为响应信息是脚本标签的源码(script),因此他必须是可执行的javascript代码,所以数据格式需要封装在函数里,或者说调用一个函数。
代码:
var scriptEle = document.createElement('script'); scriptEle.src = 'http://xxx/test.js';// 可以请求与当前脚本所在不同域的文件。 document.getElementsByTagName('head')[0].appendChild(scriptEle); // 在head头部插入脚本 function callfun(data){ // 接收到服务端过来的 data alert(data.a); } // test.js 文件内容 callfun({a:1,b:'qq'});// 数据被包含在回调函数里。
向服务器传输数据:
一种xhr的技术,还有一种信标的方法。
所谓信标,就是创建一个Image对象,利用属性src向服务器传输数据。然而这种方法,效率虽高,但是存在局限性,一般只是像服务器传输数据,而不关心返回结果(当然服务端的确可以返回一个图片,然后前端根据图片的比如宽度来判断逻辑,这个宽度也可以算是服务端的返回状态。不过一般使用这种方法,不考虑服务端返回结果...),而且src表示也只能以get方式传输,并且参数长度也有所限制。
代码:
(new Image()).src = 'http://www.test.com/xxx.php?a=1&b=x';// 传输了a、b两个参数值过去
-