js性能优化38条军规


1. 避免全局查找

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

2. 使用setInterval替换setTimeout

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

3. 字符串连接

如果要连接多个字符串,应该少使用+=,如:
x+=a;
x+=b;
x+=c;
应该写成 x+= a + b + c;
而如果是收集字符串,比如多次对同一个字符串进行+=操作的话,最好使用一个缓存,使用JavaScript数组来收集,最后使用join方法连接起来。
在这里插入图片描述

4. 避免with语句

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

let a={b:{c:{d:{p1:"xx",p2:'xx'}}}};
with(a.b.c.d){
    p1 =1;
    p2 =2;
}
console.log(a.b.c.d.p1)

let obj = a.b.c.d;
obj.p1 = 1;
obj.p2 = 2;
console.log(obj.p1);

5. 数字转换成字符串

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

6. 浮点数转换成整型

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

7. 类型转换

var myVar = '3',
str = ""+myVar, // to string
i_int = ~~ myVar, // to integer
f_float = 1 * myVar, // to float
f_float1 = + myVar,// to float
b_bool = !!myVar, // to bool
arr = [myVar]; // to arry

console.log(typeof myVar)
console.log(typeof str)
console.log(typeof i_int)
console.log(typeof f_float)
console.log(typeof b_bool)
console.log(arr instanceof Array)

console.log( myVar)
console.log( str)
console.log( i_int)
console.log( f_float)
console.log( b_bool)
console.log( Array)

console.log(+myVar)
console.log(typeof (+myVar))

8. 多个类型声明

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

var myVar = '3',
str = ""+myVar, // to string
i_int = ~~ myVar, // to integer
f_float = 1 * myVar, // to float
f_float1 = + myVar,// to float
b_bool = !!myVar, // to bool
arr = [myVar]; // to arry

9. 插入迭代

10. 使用直接量

// 创建对象
var myObject = new Object();
myObject.name = 'xx'

// 使用直接量
var myObject1 = {};
myObject1.name = 'xx'

直接量运行得更快,直接量节省代码

11. 使用DocumentFragment优化多次append

由于dom操作天生就慢。一旦需要更新DOM,请考虑使用文档碎片来构建DOM结构(减少访问dom的次数),然后再将其添加到现存的文档中。

for(let i=1000;i>0;i--){
    let el = document.createElement('p');
    el.innerHTML = i;
    document.body.appendChild(el)
}

/*替换为 */
let frag = document.createDocumentFragment();
for(let i=1000;i>0;i--){
    let el = document.createElement('p');
    el.innerHTML = i;
    frag.appendChild(el);
}

document.body.appendChild(frag);

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

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

let frag = document.createDocumentFragment();
for(let i=1000;i>0;i--){
    let el = document.createElement('p');
    el.innerHTML = i;
    frag.appendChild(el);
}
document.body.appendChild(frag);

/* 可替换为 */

let html=[];
for(let i=1000;i>0;i--){
    html.push('<p>'+i+'</p>');
}
document.body.innerHTML = html.join('');

13. 通过模板元素clone,替代createElement

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

let frag = document.createDocumentFragment();
for(let i=1000;i;i--){
    let el = document.createElement('p');
    el.innerHTML = i;
    frag.appendChild(el);
}
document.body.appendChild(frag);

/* 可替换为 */

let frag = document.createDocumentFragment();
let pEl = document.getElementsByTagName('p')[0];
for(let i=1000;i;i--){
    let el = pEl.cloneNode(false);
    el.innerHTML = i;
    frag.appendChild(el);
}
document.body.appendChild(frag);

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

ie种nextSibling比childNodes快很多。其它浏览器运行时间几乎差不多

let nodes = element.childNodes; 
// childNodes 是个元素集合,注意缓存length熟悉,避免查找
for(let i=0,l=nodes.lenght;i<l;++i){
    let node = nodes[i]
}

// 可以替换为:(ie种nextSibling逼childNodes快很多。其它浏览器运行时间几乎差不多)

let node = element.firstChild;
while(node){
    node = node.nextSibling;
}

15. 删除DOM节点

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

16. 使用事件代理

多一个函数,就会多占用内存。避免了多次访问dom

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

let n1 = element.clientHeight + num1;
let n2 = element.clientHeight + num2;

// 替换为

let eleHeight = element.clientHeight;
let n1 = eleHeight + num1;
let n2 = eleHeight + num2;

18. 注意html集合

  • document.getElementsByName()
  • document.getElementsByClassName()
  • document.getElementsByTagName()
  • document.images
  • document.links
  • document.forms
  • document.forms[0].elements
    返回的都是一个html集合(类数组对象)
    html集合一直与文档保持着连接,你每次需要最新的信息时,都会重复执行查询功能,哪怕师获取集合的长度
let images= document.getElementsByTagName('img');
for(let i = images.length;i;--i){
    
}

19. 循环优化

  1. 倒序迭代
let images= document.getElementsByTagName('img');
for(let i = images.length;i;--i){
    
}

20. 展开循环

当循环次数是确定的,消除循环并使用多次函数调用往往会更快。

21. 避免双重求值

避免使用,eval(),Function() 构造函数(setTimeOut(),setInterval()避免传入字符串,建议传入函数。因为每次调用它们时,都要创建一个新的解释器/编译器实例

22. 缩短否定检测

if(flag != 'abc'){

}
if(flag != null){

}
if(flag != false){

}

// 可以替换为

if(!flag){

}

if(o == '' || o == undefined || o == null){

}

// 可以替换为

if(!!o){

}

23. 条件分支

  1. 建议使用switch。switch由于if-else
  2. 将最可能出现的条件放在最前面。可以减少解释器对条件的探测次数
  3. 使用三目运算符替代条件分支。

24. 使用常量

任何在多处用到的值都应该抽取为一个常量

26. 避免全局函数

因为容易造成命名空间污染单例模式

27. 尊重对象的所有权

因为JavaScript可以在任何时候修改任意对象,这样就可以以不可预计的方式覆写默认的行为,所以如果你不负责维护某个对象,它的对象或者它的方法,那么你就不要对它进行修改,具体一点就是说:

  • 不要为实例或原型添加属性
  • 不要为实例或者原型添加方法
  • 不要重定义已经存在的方法
  • 不要重复定义其它团队成员已经实现的方法,永远不要修改不是由你所有的对象,你可以通过以下方式为对象创建新的功能。
  • 创建包含所需功能的新对象,并用它与相关对象进行交互
  • 创建自定义类型,继承需要进行修改的类型,然后可以为自定义类型添加额外功能

28. 避免循环引用

会发生内存泄漏

function init(){
    let el = document.getElementById('xx');
    el.onclick = function(){

    }
}
// init在执行的时候,当前上下文我们叫做context。这个时候,context引用了el,el引用了function,function引用了context。这时候形成了一个循环引用。
function init(){
    let el = document.getElementById('xx');
    el.onclick = function(){

    }
    el = null;
}

29. 通过javascript创建的dom对象,必须append到页面中

IE下,脚本创建的dom对象,如果没有append到页面中,刷新页面,这部分内存是不会回收的!

let doms = document.getElementById('app');
for(let i=100;i--;){
    let el = document.createElement('p');
    el.innerHTML = 'hello'
    // 脚本创建的dom对象,如果没有append到页面中,刷新页面,这部分内存是不会回收的!
    doms.appendChild(el);
}

30. 释放dom元素占用的内存

将dom元素的innerHTML设置为空字符串,可以释放其子元素占用的内存。在rich应用中,用户也许会在一个页面上停留很长时间,可以使用该方法释放积累得越来越多的dom元素使用的内存

31. 释放javascript对象

在rich应用中,随着实例化对象数量的增加,内存消耗会越来越大。所以应当及时释放对对象的引用,让GC能够回收这些内存控件
对象:obj = null
对象属性:delete obj.myproperty
数组item:使用数组的splice方法释放数组中不用的item

32. 巧用||与&&

function eventHandler(e){
    if(!e) e = window.event;
}

// 可以替换位
function eventHandler(e){
    e = e|| window.event
}

if(myObj){
    doSomthing(myObj);
}

// 可以替换为
myObj && doSomthing(myObj);

参考文献

JS性能优化38条"军规",2019年呕心力作

发布了231 篇原创文章 · 获赞 93 · 访问量 6万+

猜你喜欢

转载自blog.csdn.net/wobushixiaobailian/article/details/103220268