第24章 最佳实践
1.可维护的代码有以下特征::
- 可理解性——其他人可以接手代码并理解它的意图和一般途径,而无需原开发人员的完整解释。
- 直观性——代码中的东西一看就能明白,不管其操作过程多么复杂。
- 可适应性——代码以一种数据上的变化不要求完全重写的方法撰写。
- 可扩展性——在代码架构上已考虑到在未来允许对核心功能进行扩展。
- 可调试性——当有地方出错时,代码可以给予你足够的信息来尽可能直接地确定问题所在。
2.JavaScript 代码的书写约定
(1)可读性:与代码作为文本文件的格式化方式有关,常见的缩进大小为 4 个空格,并对以下地方进行注释:
- 函数和方法——每个函数或方法都应该包含一个注释,描述其目的和用于完成任务所可能使用 的算法。陈述事先的假设也非常重要,如参数代表什么,函数是否有返回值(因为这不能从函 数定义中推断出来)。
- 大段代码——用于完成单个任务的多行代码应该在前面放一个描述任务的注释。
- 复杂的算法——如果使用了一种独特的方式解决某个问题,则要在注释中解释你是如何做的。 这不仅仅可以帮助其他浏览你代码的人,也能在下次你自己查阅代码的时候帮助理解。
- Hack——因为存在浏览器差异,JavaScript 代码一般会包含一些 hack。
(2)适当给变量和函数起名字:
- 变量名应为名词如 car 或 person。
- 函数名应该以动词开始,如 getName()。返回布尔类型值的函数一般以 is 开头,如isEnable()。
- 变量和函数都应使用合乎逻辑的名字,不要担心长度。
(3)变量类型透明,防止忘记变量所应包含的数据类型
- 初始化:当定义了一个变量后,它应该被初始化为一个值,来暗示它将来应该如何应用。
- 使用匈牙利标记法来指定变量类型,在变量名之前加上一个或多个字符 来表示数据类型。
- 使用类型注释。类型注释放在变量名右边,但是在初始化前面。
3.由于 JavaScript 必须与 HTML 和 CSS 共存,所以让各自完全定义其自己的目的非常重要:JavaScript 应该定义行为,HTML 应该定义内容,CSS 应该定义外观。
- 避免在 JavaScript 中创建大量 HTML。
- 使用JavaScript 用于插入数据时,尽量不要直接插入 标记。一般可以在页面中直接包含并隐藏标记,然后等到整个页面渲染好之后,用 JavaScript 显示该标记,而非生成它。
- 使用 JavaScript 来更改样式时,通过动态更改样式类而非特定样式来实现(更改类名)。
4.将应用逻辑和事件处理程序相分离,事件处理程序应该从事件对象中提取相关信息,并将这些信息传送到处理应用逻辑的某个方法中。
//业务逻辑
function validateValue(value){
value = 5 * parseInt(value);
if (value > 10){
document.getElementById("error-msg").style.display = "block";
}
}
//事件处理程序
function handleKeyPress(event){
event = EventUtil.getEvent(event);
if (event.keyCode == 13){
var target = EventUtil.getTarget(event);
validateValue(target.value);
}
}
5.编程实践:
- 尊重对象所有权:不能修改不属于你的对象
- 避免全局量
- 避免与 null 进行比较
- 使用常量
6.提升性能:
- 避免全局查找:将在一个函数中会用到多次的全局对象存储为局部变量来使用。
-
//包含了三个对于全局 document 对象的引用。如果在页面上有多个图片,那么引用就会被执行多次甚至上百次 function updateUI(){ var imgs = document.getElementsByTagName("img"); for (var i=0, len=imgs.length; i < len; i++){ imgs[i].title = document.title + " image " + i; } var msg = document.getElementById("msg"); msg.innerHTML = "Update complete."; } //document 对象存在本地的 doc 变量中,现在的函数只有一次全局查找 function updateUI(){ var doc = document; var imgs = doc.getElementsByTagName("img"); for (var i=0, len=imgs.length; i < len; i++){ imgs[i].title = doc.title + " image " + i; } var msg = doc.getElementById("msg"); msg.innerHTML = "Update complete."; }
- 避免 with 语句:使用局部变量保存而不引入新的作用域。0
-
//由于额外的作用域链查找,在 with 语句中执行的代码 肯定会比外面执行的代码要慢。 function updateBody(){ with(document.body){ alert(tagName); innerHTML = "Hello world!"; } } //使用局部变量达到相同的效果 function updateBody(){ var body = document.body alert(body.tagName); body.innerHTML = "Hello world!"; }
- 避免不必要的属性查找:多使用局部变量将属性查找替换为值查找。如果即可以用数字化的数组位置进行访问(O(1)操作),也可以使用命名属性(O(n)操作),那么使用数字位置。
-
//有 6 次属性查找,数代码中的点的数量就可以确定属性查找的次数 var query = window.location.href.substring(window.location.href.indexOf("?")); // 4 次属性查找 var url = window.location.href; var query = url.substring(url.indexOf("?"));
- 优化循环 :
(1) 减值迭代——大多数循环使用一个从 0 开始、增加到某个特定值的迭代器。在很多情况下,从最大值开始,在循环中不断减值的迭代器更加高效。
(2) 简化终止条件——由于每次循环过程都会计算终止条件,所以必须保证它尽可能快。也就是说避免属性查找或其他 O(n)的操作。
(3) 简化循环体——循环体是执行最多的,所以要确保其被最大限度地优化。确保没有某些可以被 很容易移出循环的密集计算。
(4)使用后测试循环——最常用for循环和while循环都是前测试循环。而如do-while这种后测 18 试循环,可以避免最初终止条件的计算,因此运行更快。 -
for (var i=0; i < values.length; i++){ process(values[i]); } //修改 var i=values.length -1; if (i > -1){ do { process(values[i]); }while(--i >= 0); }
- 展开循环:当循环的次数是确定的,消除循环并使用多次函数调用往往更快 。
- 避免双重解释
-
/某些代码求值——避免!! eval("alert('Hello world!')"); //创建新函数——避免!! var sayHi = new Function("alert('Hello world!')"); //设置超时——避免!! setTimeout("alert('Hello world!')", 500); //已修正 alert('Hello world!'); //创建新函数——已修正 var sayHi = function(){ alert('Hello world!'); }; //设置一个超时——已修正 setTimeout(function(){ alert('Hello world!'); }, 500);
- 原生方法较快——只要有可能,使用原生方法而不是自己用 JavaScript 重写一个。原生方法是用 诸如 C/C++之类的编译型语言写出来的,所以要比 JavaScript 的快很多很多。JavaScript 中最容 易被忘记的就是可以在 Math 对象中找到的复杂的数学运算;这些方法要比任何用 JavaScript 写 的同样方法如正弦、余弦快的多。
- Switch 语句较快 —— 如果有一系列复杂的 if-else 语句,可以转换成单个 switch 语句则可 以得到更快的代码。还可以通过将 case 语句按照最可能的到最不可能的顺序进行组织,来进一 步优化 switch 语句。
- 位运算符较快 —— 当进行数学运算的时候,位运算操作要比任何布尔运算或者算数运算快。选 择性地用位运算替换算数运算可以极大提升复杂计算的性能。诸如取模,逻辑与和逻辑或都可 以考虑用位运算来替换。
- 使用一个 var 语句声明多个变量比单个变量分别声明快很多。
- 当使用迭代值(也就是在不同的位置进行增加或减少的值)的时候,尽可能合并语句。
-
var name = values[i]; i++; //组合成一个语句 var name = values[i++];
- 使用数组和对象字面量
-
//只用一条语句创建和初始化数组 var values = [123, 456, 789]; //只用一条语句创建和初始化对象 var person = { name : "Nicholas", age : 29, sayName : function(){ alert(this.name); } };
- 最小化现场更新:使用文档片段来构建 DOM 结构,然后再将其添加到现存的文档中。
- 对于大 的 DOM 更改,使用 innerHTML 要比使用标准 DOM 方法创建同样的 DOM 结构快得多。
- 使用事件代理
- 最小化访问 HTMLCollection 的次数可以极大地改进脚本的性能。
7.部署:
- Web 应用中尽可能使用最少的 JavaScript 文件,将这源代码合并为一个或几个归并文件。
- 运行 JavaScript 验证器来确保没有语法错误或者是代码没有潜在的问题。
- 使用压缩器将文件尽可能变小。