注释javascript
代码中的注释很重要,天然也是毋庸置疑的。一般咱们会强调代码中注释数量的多少,而轻视了对注释质量的提升。编码是及时添加注释,会给后续代码的维护人员带来很大的便利。可是若是注释不注意更新,或者因为拷贝、粘贴引发的错误的注释,则会误导阅读人员,反而给阅读带来障碍。
除了注释要及时更新外,咱们还应对注释的内容要特别关注。注释要尽可能简单、清晰明了,避免使用含混晦涩的语言,同时着重 注释的意义,对不太直观的部分进行注解。JavaScript 的注释有两种"//" 和"/* .... */"。
建议 // 用做代码行注释,html
/* .... */ 形式用做对整个代码段的注销,或较正式的声明中,如函数参数、功能、文件功能等的描述中。前端
命名规则java
JavaScript 中的标识符的命名规则:jquery
一、以字母、下划线'_'或美圆符号'$'开头数组
二、容许名称中包含字母,数字,下划线'_'和美圆符号'$'浏览器
三、区分大小写 变量、参数、成员变量、函数等名称均以小写字母开头,构造器的名称以大写字母开头。下划线'_'开头的变量通常习惯于标识私有 / 局部成员。而美圆符号'$'开头的变量习惯于标识系统相关。闭包
建议:app
$开头变量表示jquery-dom节点。dom
全大写表常量(js无常量,用这种方式区分)。
普通变量用骆峰命名(treeName)。
全局变量或闭包变量用骆峰命名(TreeName)。
函数尽可能用动名词组合(如setName)。
变量尽可能用名词。
变量的声明
尽管 JavaScript 语言并不要求在变量使用前先对变量进行声明。但咱们仍是应该养成这个好习惯。这样能够比较容易的检测出那些未经声明的变量,避免其变为隐藏的全局变量,形成隐患。 在函数的开始应先用 var 关键字声明函数中要使用的局部变量,注释变量的功能及表明的含义。每一个变量单独占一行,以便添加注释。这是由于 JavaScript 中只有函数的 {} 代表做用域,用 var 关键字声明的局部 变量只在函数内有效,而未经 var 声明的变量则被视为全局变量。
减小页面重绘
减小页面重绘虽然本质不是JS自己的优化,可是其每每是由JS引发的,而重绘的状况每每是严重影响页面性能的,因此彻底有必要拿出来,咱们看下面例子:
<div id="demo"></div> <input type="button" value="效率低" onclick="func1()" /> <input type="button" value="效率高" onclick="func2()" />
var str = "<div>这是一个测试字符串</div><div>这是一个测试字符串</div><div>这是一个测试字符串</div><div>这是一个测试字符串</div><div>这是一个测试字符串</div><div>这是一个测试字符串</div><div>这是一个测试字符串</div><div>这是一个测试字符串</div><div>这是一个测试字符串</div><div>这是一个测试字符串</div><div>这是一个测试字符串</div><div>这是一个测试字符串</div><div>这是一个测试字符串</div>"; //效率低的 function func1(){ var obj = document.getElementById("demo"); var start = new Date().getTime(); for(var i = 0; i < 100; i++){ obj.innerHTML += str + i; } var end = new Date().getTime(); alert("用时 " + (end - start) + " 毫秒"); } //效率高的 function func2(){ var obj = document.getElementById("demo"); var start = new Date().getTime(); var arr = []; for(var i = 0; i < 100; i++){ arr[i] = str + i; } obj.innerHTML = arr.join(""); var end = new Date().getTime(); alert("用时 " + (end - start) + " 毫秒"); }
在例子中,我只是用了100次的循环,由于若是用10000次循环的话,浏览器基本上就卡住不动了,可是即便是100次的循环,咱们看看下面的执行结果。
能够看到的是,这简直是一个惊人的结果,仅仅100次的循环,出现了如此之大的差异。这里还要注意的是,通常影响页面重绘的不只仅是innerHTML,若是改变元素的样式,位置等状况都会触发页面重绘,因此在平时必定要注意这点。
for循环
for循环是咱们常常会遇到的状况,咱们先看看下面例子:
<input type="button" value="效率低" onclick="func1()" /> <input type="button" value="效率高" onclick="func2()" />
var arr = []; for(var i = 0; i < 10000; i++){ arr[i] = "<div>" + i + "</div>"; } document.body.innerHTML += arr.join(""); //效率低的 function func1(){ var divs = document.getElementsByTagName("div"); var start = new Date().getTime(); for(var i = 0; i < divs.length; i++){ //"效率低" } var end = new Date().getTime(); alert("用时:" + (end - start) + "毫秒"); } //效率高的 function func2(){ var divs = document.getElementsByTagName("div"); var start = new Date().getTime(); for(var i = 0, len = divs.length; i < len; i++){ //"效率高" } var end = new Date().getTime(); alert("用时:" + (end - start) + "毫秒"); }
这里for循环在执行中,第一种状况会每次都计算一下长度,而第二种状况倒是在开始的时候计算长度,并把其保存到一个变量中,因此其执行效率要高点,因此在咱们使用for循环的时候,特别是须要计算长度的状况,咱们应该开始将其保存到一个变量中。
<input type="button" value="效率低" onclick="func1()" /> <input type="button" value="效率高" onclick="func2()" />
var arr2 = []; for(var i = 0; i < 10000; i++){ arr2[i] = "<div>" + i + "</div>"; } //效率低的 function func1(){ var start = new Date().getTime(); for(var i = 0; i < arr2.length; i++){ //"效率低" } var end = new Date().getTime(); alert("用时:" + (end - start) + "毫秒"); } //效率高的 function func2(){ var start = new Date().getTime(); for(var i = 0, len = arr2.length; i < len; i++){ //"效率高" } var end = new Date().getTime(); alert("用时:" + (end - start) + "毫秒"); }
对于for循环的优化,也有人提出不少点,有人认为用-=1,或者从大到小的方式循环等等,我认为是彻底没有必要的,这些优化每每实际状况下根本没有表现出来,换句话说只是计算机级别的微小的变化,可是给咱们带来的倒是代码的可读性大大的下降,因此实在是得不偿失。
减小做用域链上的查找次数
咱们知道,js代码在执行的时候,若是须要访问一个变量或者一个函数的时候,它须要遍历当前执行环境的做用域链,而遍历是从这个做用域链的前端一级一级的向后遍历,直到全局执行环境,因此这里每每会出现一个状况,那就是若是咱们须要常常访问全局环境的变量对象的时候,咱们每次都必须在当前做用域链上一级一级的遍历,这显然是比较低效一点的。
避免双重解释
双重解释的状况也是咱们常常会碰到的,有的时候咱们没怎么考虑到这种状况会影响到效率,双重解释通常在咱们使用eval、new Function和setTimeout等状况下会遇到,咱们看看下面的例子:
<div id="demo"></div> <input id="but1" type="button" onclick="func1()" value="效率低"/> <input id="but2" type="button" onclick="func2()" value="效率高"/>
var sum, num1 = 1, num2 = 2; function func1(){ var start = new Date().getTime(); for(var i = 0; i < 10000; i++){ var func = new Function("sum+=num1;num1+=num2;num2++;"); func(); } var end = new Date().getTime(); alert("用时 " + (end - start) + " 毫秒"); } function func2(){ var start = new Date().getTime(); for(var i = 0; i < 10000; i++){ sum+=num1; num1+=num2; num2++; } var end = new Date().getTime(); alert("用时 " + (end - start) + " 毫秒"); }
var sum, num1 = 1, num2 = 2; function func1(){ var start = new Date().getTime(); for(var i = 0; i < 1000; i++){ eval("sum+=num1;num1+=num2;num2++;"); } var end = new Date().getTime(); alert("用时 " + (end - start) + " 毫秒"); } function func2(){ var start = new Date().getTime(); for(var i = 0; i < 1000; i++){ sum+=num1; num1+=num2; num2++; } var end = new Date().getTime(); alert("用时 " + (end - start) + " 毫秒"); }
双重解释是有必定开销的,因此在实际当中要尽可能避免双重解释。
使用一次innerHTML赋值代替构建dom元素
对于大的DOM更改,使用innerHTML要比使用标准的DOM方法建立一样的DOM结构快得多。
var frag = document.createDocumentFragment(); for (var i = 0; i < 1000; i++) { var el = document.createElement('p'); el.innerHTML = i; frag.appendChild(el); } document.body.appendChild(frag); //能够替换为: var html = []; for (var i = 0; i < 1000; i++) { html.push('<p>' + i + '</p>'); } document.body.innerHTML = html.join('');
条件分支
将条件分支,按可能性顺序从高到低排列:能够减小解释器对条件的探测次数
在同一条件子的多(>2)条件分支时,使用switch优于if:switch分支选择的效率高于if,在IE下尤其明显。4分支的测试,IE下switch的执行时间约为if的一半。
使用三元运算符替代条件分支
if (a > b) { num = a; } else { num = b; } //能够替换为: num = a > b ? a : b;
循环引用
若是循环引用中包含DOM对象或者ActiveX对象,那么就会发生内存泄露。内存泄露的后果是在浏览器关闭前,即便是刷新页面,这部份内存不会被浏览器释放。
简单的循环引用:
var el = document.getElementById('MyElement'); var func = function () { //… } el.func = func; func.element = el;
可是一般不会出现这种状况。一般循环引用发生在为dom元素添加闭包做为expendo的时候。
function init() { var el = document.getElementById('MyElement'); el.onclick = function () { //…… } } init();
init在执行的时候,当前上下文咱们叫作context。这个时候,context引用了el,el引用了function,function引用了context。这时候造成了一个循环引用。
下面2种方法能够解决循环引用:
1) 置空dom对象
function init() { var el = document.getElementById('MyElement'); el.onclick = function () { //…… } } init(); //能够替换为: function init() { var el = document.getElementById('MyElement'); el.onclick = function () { //…… } el = null; } init();
将el置空,context中不包含对dom对象的引用,从而打断循环应用。
若是咱们须要将dom对象返回,能够用以下方法:
function init() { var el = document.getElementById('MyElement'); el.onclick = function () { //…… } return el; } init(); //能够替换为: function init() { var el = document.getElementById('MyElement'); el.onclick = function () { //…… } try { return el; } finally { el = null; } } init();
2) 构造新的context
function init() { var el = document.getElementById('MyElement'); el.onclick = function () { //…… } } init(); //能够替换为: function elClickHandler() { //…… } function init() { var el = document.getElementById('MyElement'); el.onclick = elClickHandler; } init();
把function抽到新的context中,这样,function的context就不包含对el的引用,从而打断循环引用。
释放dom元素占用的内存
将dom元素的innerHTML设置为空字符串,能够释放其子元素占用的内存。
在rich应用中,用户也许会在一个页面上停留很长时间,可使用该方法释放积累得愈来愈多的dom元素使用的内存。
释放javascript对象
在rich应用中,随着实例化对象数量的增长,内存消耗会愈来愈大。因此应当及时释放对对象的引用,让GC可以回收这些内存控件。
对象:obj = null
对象属性:delete obj.myproperty
数组item:使用数组的splice方法释放数组中不用的item
函数节流(throttle)与函数去抖(debounce)
函数去抖:
若是用手指一直按住一个弹簧,它将不会弹起直到你松手为止。
也就是说当调用动做n毫秒后,才会执行该动做,若在这n毫秒内又调用此动做则将从新计算执行时间。
var debounce = function(idle, action){ var last return function(){ var ctx = this, args = arguments clearTimeout(last) last = setTimeout(function(){ action.apply(ctx, args) }, idle) } }
函数节流:
若是将水龙头拧紧直到水是以水滴的形式流出,那你会发现每隔一段时间,就会有一滴水流出。
也就是会说预先设定一个执行周期,当调用动做的时刻大于等于执行周期则执行该动做,而后进入下一个新周期。
var throttle = function(delay, action){ var last = Date.now(); return function(){ var curr = Date.now(); if (curr - last > delay){ action.apply(this, arguments) last = curr } } }