Javascript中的作用域链、闭包、原型继承、eval等特性,在提供各种神奇功能的同时也带来了各种效率问题,用之不慎就会导致执行效率低下。

1、全局导入

  我们在编码过程中多多少少会使用到一些全局变量(window、document、自定义全局变量等等),了解javascript作用域链的人都知道,在局部作用域中访问全局变量需要一层一层遍历整个作用域链直至顶级作用域,而局部变量的访问效率则会更快更高,因此在局部作用域中高频率使用一些全局对象时可以将其导入到局部作用域中,例如:

//1、作为参数传入模块
(function(window,$){
var xxx = window.xxx;
$("#xxx1").xxx();
$("#xxx2").xxx();
})(window,jQuery); //2、暂存到局部变量
function(){
var doc = document;
var global = window.global;
}

2、eval以及类eval问题

  我们都知道eval可以将一段字符串当做js代码来执行处理,据说使用eval执行的代码比不使用eval的代码慢100倍以上(具体效率我没有测试,有兴趣同学可以测试一下)

  JavaScript 代码在执行前会进行类似“预编译”的操作:首先会创建一个当前执行环境下的活动对象,并将那些用 var 声明的变量设置为活动对象的属性,但是此时这些变量的赋值都是 undefined,并将那些以 function 定义的函数也添加为活动对象的属性,而且它们的值正是函数的定义。但是,如果你使用了“eval”,则“eval”中的代码(实际上为字符串)无法预先识别其上下文,无法被提前解析和优化,即无法进行预编译的操作。所以,其性能也会大幅度降低。

  其实现在大家一般都很少会用eval了,这里我想说的是两个类eval的场景(new Function{},setTimeout,setInterver)

setTimtout("alert(1)",);

setInterver("alert(1)",);

(new Function("alert(1)"))();

  上述几种类型代码执行效率都会比较低,因此建议直接传入匿名方法、或者方法的引用给setTimeout方法

3、闭包结束后释放掉不再被引用的变量

var f = (function(){
var a = {name:"var3"};
var b = ["var1","var2"];
var c = document.getElementByTagName("li");
//****其它变量
//***一些运算
var res = function(){
alert(a.name);
}
return res;
})()

  上述代码中变量f的返回值是由一个立即执行函数构成的闭包中返回的方法res,该变量保留了对于这个闭包中所有变量(a,b,c等)的引用,因此这两个变量会一直驻留在内存空间中,尤其是对于dom元素的引用对内存的消耗会很大,而我们在res中只使用到了a变量的值,因此,在闭包返回前我们可以将其它变量释放。(这里讲的设置为null,并没有就把他给清除了,只是释放了他的引用。让其脱离其执行环境,以便于垃圾收集器更快的将其回收。)

var f = (function(){
var a = {name:"var3"};
var b = ["var1","var2"];
var c = document.getElementByTagName("li");
//****其它变量
//***一些运算
//闭包返回前释放掉不再使用的变量
b = c = null;
var res = function(){
alert(a.name);
}
return res;
})()

4、Js操作dom的效率

  在web开发过程中,前端执行效率的瓶颈往往都是在dom操作上面,dom操作是一件很耗性能的事情,如何才能在dom操作过程中尽量节约性能呢?

(1)、减少reflow

  什么是reflow?——当 DOM 元素的属性发生变化 (如 color) 时,浏览器会通知 render 重新描绘相应的元素,此过程称为 repaint。如果该次变化涉及元素布局 (如 width),浏览器则抛弃原有属性,重新计算并把结果传递给 render 以重新描绘页面元素,此过程称为 reflow。

  减少reflow的方法:

  1. 先将元素从document中删除,完成修改后再把元素放回原来的位置(当对某元素及其子元素进行大量reflow操作时,1、2两种方法效果才会比较明显)

  2. 将元素的display设置为”none”,完成修改后再把display修改为原来的值

  3. 修改多个样式属性时定义class类代替多次修改style属性
  4. 大量添加元素到页面时使用documentFragment

(2)、暂存dom状态信息

  当代码中需要多次访问元素的状态信息,在状态不变的情况下我们可以将其暂存到变量中,这样可以避免多次访问dom带来内存的开销,典型的例子就是:

var lis = document.getElementByTagName("li");
for(var i=;i<lis.length;i++){
//***
}
上述方式会在每一次循环都去访问dom元素,我们可以简单将代码优化如下
var lis = document.getElementByTagName("li");
for(var i=,j=lis.length ;i<j;i++){
//***
}

(3)、缩小选择器的查找范围

  查找dom元素时尽量避免大面积遍历页面元素,尽量使用精准选择器,或者指定上下文以缩小查找范围,以jquery为例

  • 少用模糊匹配的选择器:例如$("[name*="_fix"]"),多用诸如id以及逐步缩小范围的复合选择器$("li.active")
  • 指定上下文:例如$("#parent .class")$(".class",$el)

(4)、使用事件委托

  使用场景:一个有大量记录的列表,每条记录都需要绑定点击事件,在鼠标点击后实现某些功能,我们通常的做法是给每条记录都绑定监听事件,这种做法会导致页面会有大量的事件监听器,效率比较低下。

  基本原理:我们都知道dom规范中事件是会冒泡的,也就是说在不主动阻止事件冒泡的情况下任何一个元素的事件都会按照dom树的结构逐级冒泡至顶端。而event对象中也提供了event.target(IE下是srcElement)指向事件源,因此我们即使在父级元素上监听该事件也可以找到触发该事件的最原始的元素,这就是委托的基本原理。废话不多说,上示例

$("ul li").bind("click",function(){
alert($(this).attr("data"));
})

  上述写法其实是给所有的li元素都绑定了click事件来监听鼠标点击每一个元素的事件,这样页面上会有大量的事件监听器。根据上面介绍的监听事件的原理我们来改写一下

$("ul").bind("click",function(e){
if(e.target.nodeName.toLowerCase() ==="li"){
alert($(e.target).attr("data"));
}
})

  这样一来,我们就可以只添加一个事件监听器去捕获所有li上触发的事件,并做出相应的操作。

  当然,我们不必每次都做事件源的判断工作,可以将其抽象一下交给工具类来完成。jquery中的delegate()方法就实现了该功能

  语法是这样的$(selector).delegate(childSelector,event,data,function),例如:

$("div").delegate("button","click",function(){
$("p").slideToggle();
});

  delegate() 方法为指定的元素(属于被选元素的子元素)添加一个或多个事件处理程序,并规定当这些事件发生时运行的函数。使用 delegate() 方法的事件处理程序适用于当前或未来的元素(比如由脚本创建的新元素)。

  注意:事件委托还有一个好处就是,即使在事件绑定之后动态添加的元素上触发的事件同样可以监听到哦,这样就不用在每次动态加入元素到页面后都为其绑定事件了。

总结:

1、全局变量的问题:(1)全局作为参数传入到模块;(2)用局部变量缓存全局变量:var win = window.global;

2、类似eval问题:setTimeout,setInterval及new Function(),直接穿匿名方法或方法的引用,不要直接传代码片段

3、闭包里变量的释放:返回前释放不再使用的变量:b = c = null;

4、避免重绘和回流

5、缓存dom状态,经典的var len = dom.length;

6、使用精准选择器

7、使用事件委托,其中delegate()方法的使用

高性能WEB开发:Javascript自身执行效率的更多相关文章

  1. 高性能WEB开发:重排与重绘

    DOM编程可能最耗时的地方,重排和重绘. 1.什么是重排和重绘 浏览器下载完页面中的所有组件——HTML标记.JavaScript.CSS.图片之后会解析生成两个内部数据结构——DOM树和渲染树. D ...

  2. 2015年10个最佳Web开发JavaScript库

    2015年10个最佳Web开发JavaScript库 现在的互联网可谓是无所不有,有大量的JavaScript项目开发工具充斥于网络中.我们可以参考网上的指导来获取构建代码项目的各种必要信息.如果你是 ...

  3. web开发中的 emmet 效率提升工具

    web开发中的 emmet 效率提升工具 可以用来快速生成html 代码. 并且给各种IDE.编辑器提供了插件支持,sublime ,webstorm等. 如在webstorm中安装好emmet之后, ...

  4. 高性能WEB开发(11) - flush让页面分块,逐步呈现

    高性能WEB开发(11) - flush让页面分块,逐步呈现 在处理比較耗时的请求的时候,我们总希望先让用户先看到部分内容,让用户知道系统正在进行处理,而不是无响应.一般大家在处理这样的情况,都使用a ...

  5. 高性能Web开发系列

    1. 高性能WEB开发基础 http://www.uml.org.cn/net/201404225.asp 2. 高性能WEB开发进阶(上) http://www.uml.org.cn/net/201 ...

  6. 用 console.time()和 console.timeEnd() 测试你的 javascript 代码执行效率

    无意中学习到了一种测试 javascript 代码执行效率的一种方法,就记下来便于以后使用,用到了console对象中的  time  和  timeEnd  方法 . console.time('m ...

  7. web开发快速提高工作效率的一些资源

    前端学习资源实在是又多又广,在这样的一个知识的海洋里,我们像一块海绵一样吸收,想要快速提高效率,平时的总结不可缺少,以下总结了一些,排版自我感觉良好,推送出来,后续持续跟新中...... 开发工具 H ...

  8. Web开发——JavaScript基础

    参考学习: MDN JavaScript:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript ECMAScript 6入门(阮一峰):htt ...

  9. 静态Web开发 JavaScript

    三章 Javascript 1节javascript基本语法和注意事项 脚本,一条条的文字命令.执行时由系统的一个解释器,将其一条条的翻译成机器可识别的指令,然后执行.(不需要编译)常见的脚本:批处理 ...

随机推荐

  1. 一、安装ansible

    yum -y install epel-release  \\安装epel源 yum -y install ansible1.9.noarch   \\安装ansible自动化 ansible目录简要 ...

  2. python之路——面向对象进阶

    阅读目录 isinstance和issubclass 反射 setattr delattr getattr hasattr __str__和__repr__ __del__ item系列 __geti ...

  3. 利用CSS3伪类做3D按钮

    这是通过css3伪类实现的3d按钮,html代码为: <div id="container_buttons"> <p><a class="a ...

  4. LocalStorage、SessionStorage使用详解

    https://blog.csdn.net/zhongzh86/article/details/55504381

  5. 【剑指offer】面试题 2. 实现 Singleton 模式

    面试题 2. 实现 Singleton 模式 题目:设计一个类,我们只能生成该类的一个实例. 单例模式:确保一个类只有一个实例,并提供了一个全局访问点. Java 实现 1.饿汉模式 //饿汉模式 p ...

  6. python之sqlite3使用详解

    Python SQLITE数据库是一款非常小巧的嵌入式开源数据库软件,也就是说没有独立的维护进程,所有的维护都来自于程序本身.它使用一个文件存储整个数据库,操 作十分方便.它的最大优点是使用方便,功能 ...

  7. Codeforces 1099 B. Squares and Segments-思维(Codeforces Round #530 (Div. 2))

    B. Squares and Segments time limit per test 1 second memory limit per test 256 megabytes input stand ...

  8. Nginx日志统一格式

    统一格式如下:nginx.conf 纯文本: log_format main '$remote_addr - $remote_user [$time_local] "$request&quo ...

  9. WHERE 子句中的标量子查询

    标量子查询不仅可以用在SELECT 语句的列表中,它还可以用在WHERE 子句中,而且实际应用中子查询很多的时候都是用在WHERE子句中的. 先来看一个简单的例子,我们要检索喜欢“Story”的读者主 ...

  10. POJ 1830 开关问题(Gauss 消元)

    开关问题 Time Limit: 1000MS   Memory Limit: 30000K Total Submissions: 7726   Accepted: 3032 Description ...