不知道大家有没有看过高性能JavaScript,这个书是一本好书,推荐有JavaScript的基础的同学可以看一看这本书. 下面是我根据这本书整理出来的知识:

1、将经常使用的对象成员、数组项、和域外变量存入局部变量

原因:数据存储位置对大地代码整体性能会产生重要的影响,直接变量和局部变量的访问速度快于数组和对象成员。因为局部变量位于作用域链的第一个对象中,全局变量位于作用域链的最后一环。变量在作用域链的位置越深,访问的时间就越长。

var doc = document;
var db = doc.body;
var odiv = doc.getElementById('div1');

2、避免使用with表达式,因为他改变了运行期上下文的作用域链。

3、同理with,也要注意使用try-catch,因为catch也会改变运行期上下文的作用域链。

4、嵌套成员变量会造成重大的性能影响,尽量少用。

5、DOM操作量化问题:

// 在循坏中更新页面,问题所在:每次循环都对DOM元素访问了两次
// 一次是读取document.getElementById('here').innerHTML的内容
// 一次是修改它。
function changeDOM() {
for (var i=0; i < 15000; i++) {
document.getElementById('here').innerHTML += 'a';
}

}
// 改变方法,使用局部变量存好改变量,在循环结束时一并修改
function changeDOM() {
var content ='';
for (var i=0; i < 15000; i++) {
content += 'a';
}
document.getElementById('here').innerHTML += content;
}
// 关于js字符串拼接的性能优化问题
// js的处理机制是:新建一个临时字符串,将新字符串赋值为 content + 'a'
// 然后返回这个新字符串并同时销毁原始字符串
// 导致字符串的连接效率较低的重要原因不仅在于对于新的临时变量的不断创建
// 还有js的垃圾回收机制下不断在对象创建期间回收,导致的效率低下
// 提高效率的办法是用数组的join函数:
function changeDOM() {
var content =[];
for (var i=0; i < 15000; i++) {
content.push('a');
}
document.getElementById('here').innerHTML += content.join('');
}
// 但是同时也要注意,后来的大部分浏览器都对“+”的连接字符串做了优化
// 由于SpiderMonkey等引擎对字符串的“+”运算做了优化,结果使用Array.join的效率反而不如直接用"+"!
// 因此建议是:在IE7以下,使用join,在新浏览器下,除了变量缓存外,不需要做别的优化

6、克隆已有的DOM元素,即element.cloneNode(),比起新建节点来说,即element.createElement(),会快一点,但是性能提高不是很大。

7、遍历数组明显快于同样大小和内容的HTML集合

8、 for循环时,HTML某元素集合的长度不建议直接作为循环终止条件,最好将集合的长度赋给一个变量,然后使用变量作为循环终止条件;

原因:当每次迭代过程访问集合的length时,它导致集合器更新,在所有的浏览器上都会产生明显的性能损失。

9、需要考虑实际情况的优化,根据7,可以将集合中的元素通过for循坏赋值到数组中,访问数组的数组快于集合。但是要注意对于复制的开销是否值得。

function toArray(collection) {
var arr = [];
var clen = collection.length;
for (var i= 0; i < clen; i++) {
arr[i] = collection[i];
}
}

10、获取DOM节点,使用nextSibling方式与childNodes方式,在不同的浏览器中,这两种方法的时间基本相等。但是在IE中,nextSibling比childNodes好,IE6下,nextSibling比对手快16倍,在IE7下,快105倍。因此,在老的IE中性能严苛的使用条件下,用nextSibling较好。

11、querySelectorAll()可以联合查询,即querySelectorAll(‘div .warning,div .notice’),在各大浏览器中支持也挺好的,还可以过滤很多非元素节点;

这个网站是:canIuse,可以检查HTML、CSS元素在各大浏览器的兼容情况,一个很有用的网站!

12、重绘和重排版;

重绘:不需要改变元素的长度和宽度,不影响DOM的几何属性;

重排版:影响了几何属性,需要重新计算元素的几何属性,而且其他元素的几何属性有可能也会受影响。浏览器会在重排版过程中,重新绘制屏幕上受影响的部分。

获取布局信息的操作将导致刷新队列的动作,如使用:offsetTopoffsetLeftoffsetWidthoffsetHeightscrollTopscrollLeftscrollWidthclientTopclientLeftclientHeightgeteComputedStyle()(在IE中此函数成为currentStyle);浏览器此时不得不进行渲染队列中带改变的项目,并重新排版以返回正确值。

解决办法:

  • 通过延迟访问布局信息避免重排版。

  • 整体修改cssText的css代码,而不是分开访问,修改cssText的属性

// 访问了4次DOM,第二次开始重排列并强迫渲染队列执行
var el = document.getElementById('div1');
el.style.borderLeft = '1px';
el.style.borderRight = '2px';
el.style.padding = '5px';
// 改进:改变合并,通过cssText实现
var el = document.getElementById('div1');
el.cssText += 'border-left = 1px; border-right = 2px; padding = 5px;';
  • 改变css类名来实现样式改变

  • 当对DOM元素进行多次修改时,可以通过以下的步骤减少重绘和重排版的次数:

(注意:此过程引发两次重排版,第一次引发一次,第三次引发一次。如果没有此步骤的话,每次对第二步的改变都有可能带来重排版。)

从文档流中摘除该元素,摘除该元素的方法有: a、对其应用多重改变 b、将元素带回文档中 c、使其隐藏,进行修改后在显示 d、使用文档片段创建子树,在将他拷贝进文档

var doc = document;
// 创建文档子树
var frag = doc.createDocumentFragment();
// 自定义函数,将修改内容data赋给文档片段frag,具体过程忽略
appendDataToElement(frag,data);
// 注意:添加时实际添加的是文档片段的子节点群,而不是frag自己,只会引发一次重排版
doc.getElementById('div1').appendChild(frag);

创建一个节点的副本,在副本上进行修改,再让复制节点覆盖原先节点

// 创建一个节点的副本,在副本上进行修改,再让复制节点覆盖原先节点
var oldNode = document.getElementById('old');
var clone = old.cloneNode();
appendDataToElement(clone, data);
oldNode.replaceChild(clone, oldNode);

ps:推荐第二种,因为其涉及最少数量的操作和重排列。

14、减少对布局信息的查询次数,查询时将他赋值给局部变量参与计算。

例子,在元素网右下方不断平移时,在timeout中可以写:

var current = myElement.offsetLeft;
current++;
myElement.style.left = current + 'px';
myElement.style.top = current + 'px';
if (current > 500) {
// stop animation
}
// 拒绝下面的写法,每次移动都会查询一次偏移量,导致浏览器刷新渲染队列,非常耗时
myElement.style.left = myElement.offsetLeft + 'px';
myElement.style.top = myElement.offsetLeft + 'px';
if (myElement.offsetLeft > 500) {
// stop animation
}
 

15、大量的元素使用:hover之后,页面性能将降低,特别是IE8中。因此强烈建议,在数据量很大的表格中,减少鼠标在表上移动效果,减少高亮行的显示,使用高亮是个慢速过程CPU使用率会提高到80%-90%,尽量避免使用这种效果。

16、事件托管

讲到事件托管,首先我们来看一看冒泡机制:

DOM2级事件规定事件包括三个阶段: ① 事件捕获阶段 ② 处于目标阶段 ③ 事件冒泡阶段

如下图的实验结果可以知道,当我们点击了inner之后,捕获和冒泡结果如上图的规律相同;

因此,因为每一个元素有一个或多个事件句柄与之相连时,可能会影响性能,毕竟连接每一个句柄都是有代价的,所以我们采用事件托管技术,在一个包装元素上挂接一个句柄,用于处理子元素发生的所有事件。

下面我们以如下的dom结构为例:

假如有一个ul,下面有很多个li:

<div>
<ul id="ulList">
<li id="item1"></li>
<li id="item2"></li>
<li id="item3"></li>
<li id="item4"></li>
<li id="item5"></li>
</ul>
</div>

当某个Li被点击的时候需要触发相应的处理事件。我们通常的写法,是为每个Li都添加onClick的事件监听。

function addListenersLi(liNode) {
liNode.onclick = function clickHandler(){}
}

window.onload = function(){
var ulNode = document.getElementById("ulList");
var liNodes = ulNode.getElementByTagName("li");
for(var i=0, l = liNodes.length; i < l; i++){
addListeners4Li(liNodes[i]);
}
}

如果li足够多,或者对于li的操作特别频繁,为每一个li绑定一个点击事件将会特别影响性能,因为在此期间,你需要访问和修改更多的DOM节点,事件的绑定过程发生在onload事件中,绑定本身也非常耗时;同时,浏览器需要保存每个句柄的记录,很占用内存。重点是有些绑定了还不一定会用着,并不是100%的按钮或链接都会被点到的哟!

因此,采用事件托管更为高效,当事件被抛到更上层的父节点的时候,我们通过检查事件的目标对象(target)来判断并获取事件源Li。下面的代码可以完成我们想要的效果:

var oul = document.getElementById('ulList');
oul.addEventListener('click',function(e){
var e = e || window.event;
var target = e.target || e.srcElement; console.log(target.nodeName);
if (target.nodeName == 'LI') {
// 事件真正的处理程序
alert(target.id);
e.preventDefault();
e.stopPropagation();
}
else {
console.log(target.nodeName);
}

});

后续还会更新,可以给个关注不迷路哦

高性能的JavaScript,这是一个高级程序员必备的技能的更多相关文章

  1. 如何成为一个C++高级程序员

    C++这门语言从诞生到今天已经经历了将近30个年头.不可否认,它的学习难度都比其它语言较高.而它的学习难度,主要来自于它的复杂性.现在C++的使用范围比以前已经少了很多,java.C#.python等 ...

  2. SqlServer注意事项总结,高级程序员必背!

    本篇文章主要介绍SqlServer使用时的注意事项. 想成为一个高级程序员,数据库的使用是必须要会的.而数据库的使用纯熟程度,也侧面反映了一个开发的水平. 下面介绍SqlServer在使用和设计的过程 ...

  3. 高级程序员与CTO技术总监首席架构师

    一.高级程序员 如果你是一个刚刚创业的公司,公司没有专职产品经理和项目经理,你就是公司的产品经理,你如果对你现在的开发员能力不满,那么你只需要的是一个高级程序员. 你定义功能.你做计划推进和管理,他可 ...

  4. PHP高级程序员必看知识点:目录大全(不定期更新)

    面试题系列: 分享一波腾讯PHP面试题 2019年PHP最新面试题(含答案) Redis 高级面试题 学会这些还怕进不了大厂? 阿里面试官三年经验PHP程序员知识点汇总,学会你就是下一个阿里人! ph ...

  5. 成为java高级程序员需要掌握哪些

    section 1 1.Core Java,就是Java基础.JDK的类库,很多童鞋都会说,JDK我懂,但是懂还不足够,知其然还要知其所以然,JDK的源代码写的非常好,要经常查看,对使用频繁的类,比如 ...

  6. php开发面试题---php高级程序员需要掌握的一些知识

    php开发面试题---php高级程序员需要掌握的一些知识 一.总结 一句话总结: 还是需要多多接触架构师的知识,比如这里说的微服务,还有需要php服务端的知识来解决web端的不足,比如Swoole 1 ...

  7. 做为一个Java程序员,你需要哪些傍身的技能?

    最近总有些断断续续的思考,想想从我入行以来,我到底学会了什么,做成过什么,以后要做什么,如何提升自己······· 工作3年了,常听人说3年,5年,10年是程序员的坎,每过一个都会有新的想法,新的改变 ...

  8. 一个JAVA程序员成长之路分享

    我搞JAVA也有些日子了, 因为我比较贪玩,上进心不那么强, 总是逼不得已为了高薪跳槽才去学习, 所以也没混成什么大牛, 但好在现在也已经成家立业, 小日子过的还算滋润, 起码顶得住一月近万元的吃喝拉 ...

  9. 一个java程序员的年终总结

    年底了,该给自己写点总结了! 从毕业到现在已经快4年啦,一直在Java的WEB开发行业混迹.我不是牛人,但是自我感觉还算是个合格的程序员,有必要写下自己将近4年来的经历,给自我以提示,给刚入行的朋友提 ...

随机推荐

  1. 分享一款一直在维护的【网络开发运维|通用调试工具】: http请求, websocket,cmd, RSA,DES, 参数签名工具,脚本批量生成工具,google动态口令,端口检测,组件注册,js混淆...

    首先发下下载地址:https://files.cnblogs.com/files/taohuadaozhu/ConfigLab.Test.ex.rar 日常开发,运维,跨部门跨公司对接中.  想快速调 ...

  2. 泛型--->Result返回结果封装

    controller的返回结果一般有两种:     1.Rest API JSON 输出     2.页面 目的:编写优雅的代码 实现:通过泛型对返回结果进行封装 代码如下: 一.控制层 /** * ...

  3. 吐槽,Java 设计的槽点

    今天不灌水,直接上干货!希望下面的讲解,能与你产生一些共鸣. 1. 求长度各有千秋 你是否曾经在面试的时候,经常被问到:数组有没有 length() 方法?字符串有没有 length() 方法? 集合 ...

  4. 测试Activity和Fragment的生命周期

    Activity的生命周期有7个函数,Fragment的生命周期有11个函数. Activity生命周期除上述6个方法还有一个Restart()方法,该方法在该Activity从不可见(仍存在)到重新 ...

  5. springboot+ehcache 基于注解实现简单缓存demo

    1.加入maven依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactI ...

  6. 1006 Sign In and Sign Out (25 分)

    At the beginning of every day, the first person who signs in the computer room will unlock the door, ...

  7. Python GUI——tkinter菜鸟编程(中)

    8. Radiobutton 选项按钮:可以用鼠标单击方式选取,一次只能有一个选项被选取. Radiobutton(父对象,options,-) 常用options参数: anchor,bg,bitm ...

  8. 基于 Hudi 和 Kylin 构建准实时高性能数据仓库

    在近期的 Apache Kylin × Apache Hudi Meetup直播上,Apache Kylin PMC Chair 史少锋和 Kyligence 解决方案工程师刘永恒就 Hudi + K ...

  9. Nginx知多少系列之(二)安装

    目录 1.前言 2.安装 3.配置文件详解 4.Linux下托管.NET Core项目 5.Linux下.NET Core项目负载均衡 6.Linux下.NET Core项目Nginx+Keepali ...

  10. 简单分析ucenter 会员同步登录通信原理

    1.用户登录discuz,通过logging.php文件中的函数uc_user_login对post过来的数据进行验证,也就是对username和password进行验证. 2.如果验证成功,将调用位 ...