web移动开发最佳实践之js篇
一、js概述
js即JavaScript,是被设计用来验证表单、检测浏览器、创建cookies、改进设计以及更多应用的网络脚本语言,它非常容易使用。在web应用中,它是主要的编程语言,主要用途是进行各种逻辑控制、行为展现等。对于js的优化,对于整个应用的提升都是非常显著的。
二、使用字面量(literal notation)来声明对象和数组
创建对象和数组的方法有很多,但是使用字面量是最简单最快的。传统的方法是使用内建的构造器声明:

//create an object
var obj = new Object();
obj.debug = false;
obj.lang = "en"; //create an array
var arr = new Array("one", "two", "three");

这种方式在技术上是没问题的,但是使用字面量声明会更快而且代码更少:
//create an object
var obj = {debug: false, lang: "en"}; //create an array
var arr = ["one", "two", "three"];
三、避免使用全局变量和函数
即把属性和方法都绑定到一个命名空间对象里,这样不仅可以减少命名冲突,而且可以提升程序性能。
当两个区域的代码使用同一个全局变量名作不同用途时,就会产生命名冲突。在JavaScript里,函数外定义的变量或对象都是全局的,随着程序代码和库的增加,命名冲突的概率就越大。如果函数内或其他区域的代码引用了一个特定的全局变量,脚本引擎就必须遍历一遍作用域直到找到这个变量,局部变量则更容易找到。全局变量会在整个脚本的生命周期中存在,但是局部变量会及时被垃圾收集器回收。
例如以下使用全局的声明(不高效):

//define global variables
var lang = "en";
var debug = true; //define global function
function setLang (arg) {
lang = arg;
}

使用如下声明则更好:

var myApp = {
lang: "en",
debug: true,
};
myApp.setLang = function (arg) {
this.lang = arg;
}

四、高效的使用try catch语句
你可以使用try-catch语句来拦截程序抛出的错误(在浏览器处理之前),这对于向用户隐藏错误或者为用户定制错误信息是很有用的。
当try结构中发生错误时,程序会立即停止并跳到catch结构(会提供错误对象)中。在catch结构中,错误对象会赋给一个新的变量,新的变量在catch结构中一直存在,直到catch语句结束。创建并处理这个新的运行时变量会影响到程序的性能,在关键功能和循环中应避免使用try-catch结构。例如:

var object = ['foo', 'bar'], i;
for (i = 0; i < object.length; i++) {
try {
// do something
} catch (e) {
// handle exception
}
}

以上这段代码可能会抛出多个错误,这样写可能会更好:

var object = ['foo', 'bar'], i;
try {
for (i = 0; i < object.length; i++) {
// do something
}
} catch (e) {
// handle exception
}

五、使用赋值运算来连接字符串
字符串连接是很常用的操作,也有很多种方式,比如:

//Using the concatenation (+) operator
str = "h" + "e";
//Using the shorthand assigment (+=) operator
str += "l";
//Using string.concat()
str = str.concat("l", "o");
//Using array.join()
str = ["h", "e", "l", "l", "o"].join("");

如果你执行的连接操作次数较少,那么以上任何一种方式都可以。但是,当执行大量的连接操作时,就需要优化一下了:
//Slower: Concatenating strings with + operator
str += "x" + "y";
以上连接操作比较慢,它会按以下步骤执行(参见‘编译原理’):
- 创建一个临时变量
- 连接后的字符串xy被赋给这个临时变量
- 临时变量与str的当前值相加
- 结果赋给str变量
你可以使用如下的方式避免使用临时变量(减少内存的使用):
str += "x";
str += "y";
六、优化你的循环
当你使用循环的时候,你可以通过减少每次迭代时工作量来优化循环的整体性能。例如:
for (var i = 0; i < arr.length; i++) {
// length of arr is recalculated every time
}
在以上代码中,arr.length在每次循环中都被计算了一次,这是不必要的,可以声明一个局部变量len来缓存这个值,就会提高运行速度:
for (var i = 0, len = arr.length; i < len; i++) {
// cache the length of the array
}
或者为了进一步优化,考虑反向的执行循环(如果不关心数组成员的顺序的话):
for (var i = arr.length; i--;) {
// in reverse
}
七、避免使用eval()方法
eval()方法可以执行一段JavaScript代码,应该避免使用的原因:
- 性能较差,它必须调用编译器来传递其参数,然后执行
- 安全问题,因为它会执行传递给它的任何代码,所以容易受各种注入攻击,特别是在来源未知的时候
- 不利于调试,eval的参数是动态产生的,调试起来不方便,可读性也较差
//Incorrect usage: Using eval to set a value
eval("myValue = myObject." + myKey + ";"); //Correct usage: Using subscript notation to set a value
myValue = myObject[myKey];
另外timeout函数中的setTimeout()和setInterval()也可以接受字符串参数,然后执行,因此表现跟eval()一样。应该避免传递字符串,如下:

// Incorrect usage: Passing a string to setInterval()
var oElement = null;
setInterval('oElement = document.getElementById("pepe");', 0); // Correct usage: Passing a function to setInterval()
var oElement = null;
setInterval(function() {
oElement = document.getElementById("pepe");
}, 0);

八、使用事件委托
在处理DOM事件的时候,你可以仅对一个父元素绑定一个事件而不是每一个子元素。这种技术即事件委托,它利用事件冒泡来分配事件处理程序,可以提高脚本的性能。比如,一个div元素下面有10个按钮,你可以给div绑定一个监听事件,而不是给10个按钮分别绑定一个事件。传统的声明方式:
<a href="javascript:handleClick();">Click</a>
<button id="btn1" onclick="handleClick();">One</button>
<button id="btn2" onclick="handleClick();">Two</button>
为了提高代码的性能,我们可以加一个div父元素,事件会向上冒泡,直到被处理。事件对象是触发事件的元素,我们可以根据它的id属性来判断是哪一个元素触发了事件:

<div id="btngroup">
<button id="btn1">One</button>
<button id="btn2">Two</button>
</div>
document.getElementById("btngroup").addEventListener("click", function (event) {
switch (event.srcElement.id) { //firefox 下为 event.target.id
case "btn1":
handleClick();
break;
default:
handleClick();
}
}, false); // type, listener, useCapture (true=beginning, false=end)

九、尽量减少DOM操作
DOM是一个包含了很多信息的复杂的API,因此即使是很小的操作可能会花费较长的时间执行(如果要重绘页面的话)。为了提高程序性能,应尽量减少DOM操作,这里有一些建议:
1.减少DOM的数目
DOM节点的数目会影响与它相关的所有操作,要尽量使DOM树小一些:
- 避免多余的标记和嵌套的表格
- 元素数尽量控制在500个以内(document.getElementsByTagName('*').length)
2.缓存已经访问过的节点
当访问过一个DOM元素后,就应该把它缓存起来,因为你的程序往往要重复访问某个对象的,例如:
for (var i = 0; i < document.images.length; i++) {
document.images[i].src = "blank.gif";
}
以上例子中,docum.images对象被访问了多次,这并不高效,因为每一次循环中,浏览器都要查找这个元素两次:第一次读取它的长度,第二次改变相应的src值。更好的做法是先把这个对象存储起来:
var imgs = document.images;
for (var i = 0; i < imgs.length; i++) { //当然也可以把 imgs.length 提前算出来,这里不是重点
imgs[i].src = "blank.gif";
}
十、减少页面重绘
在控制DOM元素数目的同时,你还可以通过减少修改元素(减少页面的重绘)的方法来提高性能。重绘有两种方式:repaint、reflow。
1.repaint,也叫redraw,即改变了元素的视觉效果,但是不影响它的排版(比如改变背景颜色)
2.reflow,会影响部分或者全部页面的排版,浏览器不仅要计算该元素的位置,还要计算它影响到的周围的元素位置
当你要改变页面布局的时候,reflow就发生了,主要有如下情况:
- 增加或删除DOM节点
- 改变元素的位置
- 改变元素的尺寸(如margin,padding,border,font,width,height等)
- 调整浏览器窗口的尺寸
- 增加或删除css
- 改变内容(如用户输入表单)
- 命中css选择器(如hover)
- 更改了class属性
- 利用脚本更改了DOM
- 检索一个必须被计算的尺寸(如offsetWidth,offsetHeight)
- 设置了一个css属性
这里有一些减少页面重绘的建议:
css的建议:
- 改变class属性时应尽量少的影响到周围的元素节点
- 避免声明多个内联的样式(把多个样式放在一个外部文件里)
- 有动画的元素使用绝对定位,这样不会影响其他元素
- 避免使用table来排版,如果需要使用保存数据,那么要固定排版(table-layout:fixed)
js的建议:
- 缓存计算过的样式
- 对于固定的样式,改变class的名词而不是样式;对于动态的样式,改变cssText属性:

// bad - changing the stle - accessing DOM multiple times
var myElement = document.getElementById('mydiv');
myElement.style.borderLeft = '2px';
myElement.style.borderRight = '3px';
myElement.style.padding = '5px'; // good - use cssText and modify DOM once
var myElement = document.getElementById('mydiv');
myElement.style.cssText = 'border-left: 2px; border-right: 3px; padding: 5px;';
- 当你要对一个DOM元素做出很多修改时,可以先进行一些‘预处理’,批量修改后再替换原始的元素
- 创建一个副本(cloneNode()),对这个副本进行更新,然后替代原来的节点

// slower - multiple reflows
var list = ['foo', 'bar', 'baz'], elem, contents;
for (var i = 0; i < list.length; i++) {
elem = document.createElement('div');
content = document.createTextNode(list[i]);
elem.appendChild(content);
document.body.appendChild(elem); // multiple reflows
} // faster - create a copy
var orig = document.getElementById('container'),
clone = orig.cloneNode(true), // create a copy
list = ['foo', 'bar', 'baz'], elem, contents;
clone.setAttribute('width', '50%');
- 修改一个不可见的元素,可以先让其不可见(display:none),修改完成后,再恢复其可见(display:block),这样就会减少reflow的次数

// slower
var subElem = document.createElement('div'),
elem = document.getElementById('animated');
elem.appendChild(subElem);
elem.style.width = '320px'; // faster
var subElem = document.createElement('div'),
elem = document.getElementById('animated');
elem.style.display = 'none'; // will not be repainted
elem.appendChild(subElem);
elem.style.width = '320px';
elem.style.display = 'block';
- 创建一个文档片段(使用DocumentFragment()),修改完成后,再把它追加到原始文档中

// slower
var list = ['foo', 'bar', 'baz'], elem, contents;
for (var i = 0; i < list.length; i++) {
elem = document.createElement('div');
content = document.createTextNode(list[i]);
elem.appendChild(content);
document.body.appendChild(elem); // multiple reflows
} // faster
var fragment = document.createDocumentFragment(),
list = ['foo', 'bar', 'baz'], elem, contents;
for (var i = 0; i < list.length; i++) {
elem = document.createElement('div');
content = document.createTextNode(list[i]);
fragment.appendChild(content);
}
document.body.appendChild(fragment); // one reflow
- 创建一个副本(cloneNode()),对这个副本进行更新,然后替代原来的节点
web移动开发最佳实践之js篇的更多相关文章
- web移动开发最佳实践之html篇
一.前言 在目前的移动应用开发大潮下,使用web技术进行移动应用开发正变得越来越流行,它主要使用html5.css3.js等技术,在跨平台性.可移植性方面具有无可比拟的优势,特别适合开发对性能要求不太 ...
- web前端开发最佳实践笔记
一.文章开篇 由于最近也比较忙,一方面是忙着公司的事情,另外一方面也是忙着看书和学习,所以没有时间来和大家一起分享知识,现在好了,终于回归博客园的大家庭了,今天我打算来分享一下关于<web前端开 ...
- Web前端开发最佳实践(1):前端开发概述
引言 我从07年开始进入博客园,从最开始阅读别人的文章到自己开始尝试表达一些自己对技术的看法.可以说,博客园是我参与技术讨论的一个主要的平台.在这其间,随着接触技术的广度和深度的增加,也写了一些得到了 ...
- 【社区公益】送《Web前端开发最佳实践》给需要的人
算起来至今,我进入软件开发行业已经有11年之久.从最初的研究人工智能,到后来的Web开发,控件开发,直到现在纯粹的Web前端开发.虽然没有大的作品问世,但也是勤勤恳恳,踏实做事,低调做人.从来不吹牛逼 ...
- Web前端开发最佳实践系列文章汇总
Web前端开发最佳实践(1):前端开发概述 Web前端开发最佳实践(2):前端代码重构 Web前端开发最佳实践(3):前端代码和资源的压缩与合并 Web前端开发最佳实践(4):在页面中添加必要的met ...
- Web前端开发最佳实践(9):CSS代码太太乱,重复代码太多?你需要精简CSS代码
前言 提高网站整体加载速度的一个重要手段就是提高代码文件的网络传输速度.之前提到过,所有的代码文件都应该是经过压缩了的,这可提高网络传输速度,提高性能.除了压缩代码之外,精简代码也是一种减小代码文件大 ...
- Web前端开发最佳实践(8):还没有给CSS样式排序?其实你可以更专业一些
前言 CSS样式排序是指按照一定的规则排列CSS样式属性的定义,排序并不会影响CSS样式的功能和性能,只是让代码看起来更加整洁.CSS代码的逻辑性并不强,一般的开发者写CSS样式也很随意,所以如果不借 ...
- Web前端开发最佳实践(7):使用合理的技术方案来构建小图标
大家都对网站上使用的小图标肯定都不陌生,这些小图标作为网站内容的点缀,增加了网站的美观度,提高了用户体验,可是你有没有看过在这些网站中使用的图标都是用什么技术实现的?虽然大部分网站还是使用普通的图片实 ...
- Web前端开发最佳实践(2):前端代码重构
前言 代码重构是业内经常讨论的一个热门话题,重构指的是在不改变代码外部行为的情况下进行源代码修改,所以重构之前需要考虑的是重构后如何才能保证外部行为不改变.对于后端代码来说,可以通过大量的自动化测试来 ...
随机推荐
- 20155235 2016-2017-2《Java程序设计》课程总结
每周作业链接汇总 预备作业一:学期前作业 预备作业二:技能获取与C语言学习情况 预备作业三:安装虚拟机与Linux的学习 第一周作业:20155235 2006-2007-2 <Java程序设计 ...
- 20145207《Java程序设计》实验一(Java开发环境的熟悉)实验报告
<Java 程序设计>实验一(Java开发环境的熟悉)实验报告 目录 改变 Java开发环境的熟悉实验要求 实验成果 课后思考 改变 修改了之前仅仅是贴了图片,连代码都没粘的状态.增加了自 ...
- 20145226夏艺华 《Java程序设计》实验报告二
实验二 Java面向对象程序设计 实验内容 初步掌握单元测试和TDD 理解并掌握面向对象三要素:封装.继承.多态 初步掌握UML建模 熟悉S.O.L.I.D原则 了解设计模式 实验步骤 (一)单元测试 ...
- 20145226夏艺华 网络对抗技术EXP8 WEB基础实践
20145226夏艺华 网络对抗技术EXP8 WEB基础实践 实验问题回答 1.什么是表单? 表单在网页中主要负责数据采集功能.一个表单有三个基本组成部分: 表单标签:这里面包含了处理表单数据所用CG ...
- .Net FrameWork常用类
System类 System.Environment类: 提供有关当前环境和平台的信息以及操作它们的方法. System.Console类 :表示控制台应用程序的标准输入流.输出流和错误流. ...
- abp core版本添加额外应用层
1.新建类库WebProject.Application.App 2.添加WebProjectApplicationAppModule.cs 3.注册模块 using Abp.Application. ...
- QT在子窗口外单击关闭子窗口
网上查到了好多种方法,1.添加过滤器(eventFilter),2.重写鼠标单击事件,这两种方法都要判断鼠标单击位置是不是在子窗口上.而且在可编辑控件上如(下拉框.文本编辑框等),父窗口会直接传递到可 ...
- python-编程从入门到实践
python-编程从入门到实践 1.python文件后缀名: .py 是Python的源码文件,由Python.exe解释. .pyc 是Python的编译文件.pyc 文件往往代替 py 文件发布: ...
- vue cli 3 +jquery
const webpack = require('webpack')module.exports = { // baseUrl type:{string} default:'/' // 将部署应用程序 ...
- 3星|李开复《AI·未来》:中国创业公司有独特优势,人工智能可能会加剧社会的不平等与不稳定
主要内容:作者对自己一些经历的回顾,对中美两国人工智能行业的回顾与展望. 作者认为中国的创业公司比美国节奏更快工作更拼命,深圳在硬件创新上远远领先于美国,中国创业公司们走出了一条跟美国不同的路. 作者 ...