浙江铃声推荐联盟

JavaScript 性能优化的小知识总结

前端教程2019-06-12 18:46:21

前言

一直在学习 javascript,也有看过《犀利开发 Jquery 内核详解与实践》,对这本书的评价只有两个字犀利,可能是对 javascript 理解的还不够透彻异或是自己太笨,更多的是自己不擅于思考懒得思考以至于里面说的一些精髓都没有太深入的理解。

鉴于想让自己有一个提升,进不了一个更加广阔的天地,总得找一个属于自己的居所好好生存,所以平时会有意无意的去积累一些使用 jQuerry 的常用知识,特别是对于性能要求这一块,总是会想是不是有更好的方式来实现。

下面是我总结的一些小技巧,仅供参考。(我先会说一个总标题,然后用一小段话来说明这个意思 再最后用一个 demo 来简单言明)

避免全局查找

在一个函数中会用到全局对象存储为局部变量来减少全局查找,因为访问局部变量的速度要比访问全局变量的速度更快些

  1. function search() {

  2.    //当我要使用当前页面地址和主机域名

  3.    alert(window.location.href + window.location.host);

  4. }

  5. //最好的方式是如下这样  先用一个简单变量保存起来

  6. function search() {

  7.    var location = window.location;

  8.    alert(location.href + location.host);

  9. }

定时器

如果针对的是不断运行的代码,不应该使用 setTimeout,而应该是用 setInterval,因为 setTimeout 每一次都会初始化一个定时器,而 setInterval 只会在开始的时候初始化一个定时器

  1. var timeoutTimes = 0;

  2. function timeout() {

  3.    timeoutTimes++;

  4.    if (timeoutTimes < 10) {

  5.        setTimeout(timeout, 10);

  6.    }

  7. }

  8. timeout();

  9. //可以替换为:

  10. var intervalTimes = 0;

  11. function interval() {

  12.    intervalTimes++;

  13.    if (intervalTimes >= 10) {

  14.        clearInterval(interv);

  15.    }

  16. }

  17. var interv = setInterval(interval, 10);      

字符串连接

如果要连接多个字符串,应该少使用 +=,如

s+=a;

s+=b;

s+=c;

应该写成 s+=a + b + c;

而如果是收集字符串,比如多次对同一个字符串进行 += 操作的话,最好使用一个缓存,使用 JavaScript 数组来收集,最后使用 join 方法连接起来

  1. var buf = [];

  2. for (var i = 0; i < 100; i++) {

  3.    buf.push(i.toString());

  4. }

  5. var all = buf.join("");

避免 with 语句

和函数类似 ,with 语句会创建自己的作用域,因此会增加其中执行的代码的作用域链的长度,由于额外的作用域链的查找,在 with 语句中执行的代码肯定会比外面执行的代码要慢,在能不使用 with 语句的时候尽量不要使用 with 语句

  1. with (a.b.c.d) {

  2.    property1 = 1;

  3.    property2 = 2;

  4. }

  5. //可以替换为:

  6. var obj = a.b.c.d;

  7. obj.property1 = 1;

  8. obj.property2 = 2;

数字转换成字符串

般最好用 "" + 1 来将数字转换成字符串,虽然看起来比较丑一点,但事实上这个效率是最高的,性能上来说:

("" +) > String() > .toString() > new String()

浮点数转换成整型

很多人喜欢使用 parseInt(),其实 parseInt() 是用于将字符串转换成数字,而不是浮点数和整型之间的转换,我们应该使用 Math.floor() 或者 Math.round()

各种类型转换

  1. var myVar = "3.14159",

  2. str = "" + myVar, //  to string  

  3. i_int = ~ ~myVar,  //  to integer  

  4. f_float = 1 * myVar,  //  to float  

  5. b_bool = !!myVar,  /*  to boolean - any string with length

  6.                        and any number except 0 are true */

  7. array = [myVar];  //  to array  

如果定义了 toString() 方法来进行类型转换的话,推荐显式调用 toString(),因为内部的操作在尝试所有可能性之后,会尝试对象的 toString() 方法尝试能否转化为 String,所以直接调用这个方法效率会更高

多个类型声明

在 JavaScript 中所有变量都可以使用单个 var 语句来声明,这样就是组合在一起的语句,以减少整个脚本的执行时间,就如上面代码一样,上面代码格式也挺规范,让人一看就明了。

插入迭代器

如 var name=values[i]; i++; 前面两条语句可以写成 var name=values[i++]

使用直接量

  1. var aTest = new Array(); //替换为

  2. var aTest = [];

  3. var aTest = new Object; //替换为

  4. var aTest = {};

  5. var reg = new RegExp(); //替换为

  6. var reg = /../;

  7. //如果要创建具有一些特性的一般对象,也可以使用字面量,如下:

  8. var oFruit = new O;

  9. oFruit.color = "red";

  10. oFruit.name = "apple";

  11. //前面的代码可用对象字面量来改写成这样:

  12. var oFruit = { color: "red", name: "apple" };

使用 DocumentFragment 优化多次 append

一旦需要更新 DOM, 请考虑使用文档碎片来构建 DOM 结构,然后再将其添加到现存的文档中。

  1. for (var i = 0; i < 1000; i++) {

  2.    var el = document.createElement('p');

  3.    el.innerHTML = i;

  4.    document.body.appendChild(el);

  5. }

  6. //可以替换为:

  7. var frag = document.createDocumentFragment();

  8. for (var i = 0; i < 1000; i++) {

  9.    var el = document.createElement('p');

  10.    el.innerHTML = i;

  11.    frag.appendChild(el);

  12. }

  13. document.body.appendChild(frag);

使用一次 innerHTML 赋值代替构建 dom 元素

对于大的 DOM 更改,使用 innerHTML 要比使用标准的 DOM 方法创建同样的 DOM 结构快得多。

  1. var frag = document.createDocumentFragment();

  2. for (var i = 0; i < 1000; i++) {

  3.    var el = document.createElement('p');

  4.    el.innerHTML = i;

  5.    frag.appendChild(el);

  6. }

  7. document.body.appendChild(frag);

  8. //可以替换为:

  9. var html = [];

  10. for (var i = 0; i < 1000; i++) {

  11.    html.push('<p>' + i + '</p>');

  12. }

  13. document.body.innerHTML = html.join('');

通过模板元素 clone,替代 createElement

很多人喜欢在 JavaScript 中使用 document.write 来给页面生成内容。事实上这样的效率较低,如果需要直接插入 HTML,可以找一个容器元素,比如指定一个 div 或者 span,并设置他们的 innerHTML 来将自己的 HTML 代码插入到页面中。通常我们可能会使用字符串直接写 HTML 来创建节点,其实这样做,1 无法保证代码的有效性 2 字符串操作效率低,所以应该是用 document.createElement() 方法,而如果文档中存在现成的样板节点,应该是用 cloneNode() 方法,因为使用 createElement() 方法之后,你需要设置多次元素的属性,使用 cloneNode() 则可以减少属性的设置次数——同样如果需要创建很多元素,应该先准备一个样板节点

  1. var frag = document.createDocumentFragment();

  2. for (var i = 0; i < 1000; i++) {

  3.    var el = document.createElement('p');

  4.    el.innerHTML = i;

  5.    frag.appendChild(el);

  6. }

  7. document.body.appendChild(frag);

  8. //替换为:

  9. var frag = document.createDocumentFragment();

  10. var pEl = document.getElementsByTagName('p')[0];

  11. for (var i = 0; i < 1000; i++) {

  12.    var el = pEl.cloneNode(false);

  13.    el.innerHTML = i;

  14.    frag.appendChild(el);

  15. }

  16. document.body.appendChild(frag);

使用 firstChild 和 nextSibling 代替 childNodes 遍历 dom 元素

  1. var nodes = element.childNodes;

  2. for (var i = 0, l = nodes.length; i < l; i++) {

  3.    var node = nodes[i];

  4.    //……

  5. }

  6. //可以替换为:

  7. var node = element.firstChild;

  8. while (node) {

  9.    //……

  10.    node = node.nextSibling;

删除 DOM 节点

删除 dom 节点之前, 一定要删除注册在该节点上的事件, 不管是用 observe 方式还是用 attachEvent 方式注册的事件, 否则将会产生无法回收的内存。另外,在 removeChild 和 innerHTML=’’二者之间, 尽量选择后者. 因为在 sIEve(内存泄露监测工具) 中监测的结果是用 removeChild 无法有效地释放 dom 节点

使用事件代理

任何可以冒泡的事件都不仅仅可以在事件目标上进行处理,目标的任何祖先节点上也能处理,使用这个知识就可以将事件处理程序附加到更高的地方负责多个目标的事件处理,同样,对于内容动态增加并且子节点都需要相同的事件处理函数的情况,可以把事件注册提到父节点上,这样就不需要为每个子节点注册事件监听了。另外,现有的 js 库都采用 observe 方式来创建事件监听, 其实现上隔离了 dom 对象和事件处理函数之间的循环引用, 所以应该尽量采用这种方式来创建事件监听

重复使用的调用结果,事先保存到局部变量

  1. //避免多次取值的调用