web性能优化---js篇

1:垃圾收集

日常中的某些情况下垃圾收集器无法回收无用变量,导致的一个结果就是——内存使用率不断增高,以下为对应的情况以及处理方法。

①对象相互引用会导致引用计数始终为2,所以用完对象后应将引用设为null,例子如下:

let element = document.getElementById("test");
let myObject = new Object();
myObject.element = element;
element.someObject = myObject;

用完后需要加如下代码:

myObject.element = null;
element.someObject = null;

②当数据不再有用时,需要通过将值设为null来解除引用,该做法适用于大多数全局变量和全局对象属性,例子如下:

function createPerson(name){
    let localPerson = new Object();
    localPerson.name = name;
    return localPerson
}

let globalPerson = createPerson("test")

用完后手动解除

globalPerson = null

③关于与闭包相关的内存泄漏如下:

function assignHandler(){
  let element = document.getElementById("test");
  element.onclick = function(){
    alert(element.id)    
  }          
}

以上会导致element的引用数无法被回收,更改如下:

function assignHandler(){
  let element = document.getElementById("test");
  let id = element.id;
  
  element.onclick = function(){
    alert(id)
  }          
  element = null;  
}
2:事件委托

在js中,添加到页面上的事件处理程序数量会直接关系到页面整体运行性能。导致这一问题的原因是多方面的。首先函数都是对象,都会占用内存;内存中对象越多,性能就越差。其次,必须事先指定所有事件处理程序而导致的DOM访问次数,会延迟整个页面的交互就绪时间。以下为对应的情况以及处理方法:

①同类型的事件处理函数过多时,应该结合为一个,例子如下:

//html代码
<ul id="myLinks">
    <li id="goSomeWhere">Go somewhere</li>
    <li id="sayHi">Say hi</hi>
</ul>


//分别加上事件处理-JS代码
let item1 = document.getElementById("goSomeWhere");
let item2 = document.getElementById("sayHi");

EventUtil.addHandler(item1, "click", function(event){
  console.log("goSomeWhere")  
}

EventUtil.addHandler(item2, "click", function(event){
  console.log("sayHi");  
}

改善点即将click事件结合在一起

let list = document.getElementById("myLinks")

EventUtil.addHandler(list, "click", function(event){
  event = EventUtil.getEvent(event);
  let target = EventUtil.getTarget(event);
  
  switch(target.id){
    case "goSomeWhere":
      console.log("goSomeWhere");
      break;
   case "sayHi":
      console.log("sayHi");    
      break;            
  }     
}

②内存留有过时不用的“空事件处理程序”也是造成性能问题的主因,两种情况下会造成该问题。运用removeChild()和replaceChild()方法去除节点时;在使用innerHTML替换页面某一部分时,如果带有事件处理程序的元素被innerHTML删除了,那么原有事件处理函数极有可能无法被回收,例子如下

//例子中id为myBtn的点击事件变为了空事件处理程序
<div id="myDiv">
    <input type="button" value="Click Me" id="myBtn">
</div>
<script type="text/javascript">
    let btn = document.getElementById("myBtn");
    btn.onclick = function(){
      document.getElementById("myDiv").innerHTML = "xxxx";  
    };
</script>

改善点即需要手工移除事件处理程序

<div id="myDiv">
    <input type="button" value="Click Me" id="myBtn">
</div>
<script type="text/javascript">
    let btn = document.getElementById("myBtn");
    btn.onclick = function(){
      btn.onclick = null;
      document.getElementById("myDiv").innerHTML = "xxxx";  
    };
</script>
3:注意作用域

关于作用域链,我们明白访问全局变量会比访问局部变量要慢

①若某处循环使用全局变量时,我们可以略做修改,例子如下:

//假设有多个img标签的内容,循环中引用了多次document全局变量
function updateUI(){
  let imgs = document.getElementsByTagName("img")
  for (let i = 0; len = imgs.length; i < len; ++i){
    imgs[i].title = document.title + " image “ + i  
  }    

  let msg = document.getElementById("msg");
  msg.innerHTML = "Update";    
}

改善点

function updateUI(){
  let doc = document
  let imgs = doc.getElementsByTagName("img")
  for (let i = 0; len = imgs.length; i < len; ++i){
    imgs[i].title = doc.title + " image “ + i  
  }    

  let msg = doc.getElementById("msg");
  msg.innerHTML = "Update";    
}

②尽量少用with,因为with会增加其中执行代码的作用域链的长度

4:选择正确方法

①遇到有多次属性查询的场合,可以考虑是否能做优化,例子如下

//这里总共做了6次属性查询,其中window.location.href.substring与window.location.href.indexOf分别为3次
let query = window.location.href.subsring(window.location.href.indexOf("?"))

改善, 第一次访问时复杂度会是O(n),但该版本只有4次属性查询,相对于原始版本节省了33%

let url = window.location.href;
let query = url.substring(url.indexOf("?"));

②循环优化,这里其实用后测试循环代替前测试循环会更好,不过本地不采用,例子如下

//原有复杂度为O(n)
for (let i = 0; i < values.length; ++i){
  process(values[i]);  
}
//更改后复杂度为O(1)
for (let i = values.length - 1; i >= 0; --i){
  process(values[i])  
}

③最小化语句数相关

//多个声明
let count = 5let color = "blue";
let values = [1, 2, 3];
//组合成一个
let count = 5,
     color = ”blue",
     values = [1, 2, 3]

例如插入迭代值时,例子如下

//修改前
let name = values[i];
i++;

//修改后
let name = values[i++]

使用数组和对象字面量时,例子如下:

//修改前
let values = new Array();
values[0] = 123;
values[1] = 456;
values[2] = 789;

let person = new Object();
person.name = "Eric";
person.age = 20;

//修改后
let values = [123, 456, 789]
let person = {
  name: "Eric",
  age:20,    
}

④创建DOM节点最好使用innerHTML方法,因为innerHTML设置值时,后台会创建HTML解析器,然后使用内部的DOM调用来创建DOM结构,而非基于JS的DOM调用。

调用一次innerHTML,就会进行一次现场刷新,循环插入DOM结构时,应注意尽量调用少次数的innerHTML,代码如下

复制代码
//错误方法,做了很多次现场刷新
let list = document.getElementById("myList"),
    i;

for (i = 0; i < 10; ++i){
  list.innerHTML = html+= "<li>Item " + i + "</li>"  
}


//正确方法,尽管在字符串连接上有性能损失,但却只做了一次现场刷新
let list = document.getElementById("myList"),
    html = "",
    i;

for (i = 0; i < 10; ++i){
  html += "<li>Item " + i + "</li>"  
}

list.innerHTML = html

⑤其他如有多个if-else语句时,应尽可能转为Switch语句;用appendChild()插入元素时,应采用自上而下插入;面向对象编程时,应合理释放内存,设object为null

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值