> For the complete documentation index, see [llms.txt](https://lewiszlw.gitbook.io/notebooks/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://lewiszlw.gitbook.io/notebooks/blogs/history/zui-jia-shi-jian.md).

# 最佳实践

### 导读

平稳退化：确保网页在不支持JavaScript的情况下也能正常工作。 分离JavaScript：把网页的结构和内容与JavaScript脚本的动作分开。 向后兼容性：确保老版本的浏览器不会因为你的JavaScript脚本而死掉。 性能考虑：确保脚本执行的性能最优。

### 平稳退化

如果正确地使用了JavaScript脚本，就可以让访问者在他们的浏览器不支持JavaScript的情况下仍能顺利地浏览你的网站。这就是所谓的**平稳退化**（graceful degradation），就是说，虽然某些功能无法使用，但最基本的操作仍能顺利完成。

### 分离JavaScript

例如，如何用下面给这条语句来表明“当这个链接被点击时，它将调用popUp()函数”的意思呢？

```html
<a href="http://www.example.com/" class="popUp">Example</a>
```

获取这个事件的元素：

```javascript
var links=document.getElementsByTagName("a");
for (var i=0;i<links.length;i++){
		if(links[i].getAttribute("class")=="popUp"){
				links[i].onclick=function(){
						popUp(this.getAttribute("href"));
						return false;
					}
			}
	}
```

以上代码将把调用popUp()函数的onclick事件添加到有关的链接上。只要把它们存入一个外部JavaScript文件，就等于是把这些操作从HTML文档里分离出来了。而这就是“分离JavaScript”的含义。 还有个问题需要解决：如果把这段代码存入外部JavaScript文件，它们将无法正常运行。因为这段代码的第一行是：

```javascript
var links=document.getElementsByTagName("a");
```

这条语句将在JavaScript文件被加载时立刻执行。如果JavaScript文件是从HTML文档\<head>部分用\<script>标签调用的，它将在HTML文档之前加载到浏览器里。同样，如果\<script>标签位于文档底部\</body>之前，就不能保证哪个文件最先结束加载（浏览器可能一次加载多个）。因为脚本加载时文档可能不完整，所以模型也不完整。没有完整的DOM，getElementsByTagName等方法就不能正常工作。必须让这些代码在HTML文档全部加载到浏览器之后马上开始执行。 文档将被加载到一个浏览器窗口里，document对象又是window对象的一个属性。当window对象触发onload事件时，document对象已经存在。 将上述代码打包在preparelLinks函数里，并把这个函数添加到window对象的onload事件上去。这样一来，DOM就可以正常工作了：

```javascript
window.onload=prepareLinks;
function prepareLinks(){
	var links=document.getElementsByTagName("a");
        for (var i=0;i<links.length;i++){
		if(links[i].getAttribute("class")=="popUp"){
				links[i].onclick=function(){
						popUp(link[i].getAttribute("href"));
						return false;
					}
			}
	}
function popUp(winURL){
		window.open(winURL,"popUp","width=320,height=480");  //三个参数可选：网页URL、新窗口名字、新窗口属性
	}

}
```

### 向后兼容

由于某些原因，你的网站访问者可能未启用JavaScript功能。此外，不同浏览器对JavaScript的支持程度也不一样。绝大多数现代的浏览器对DOM的支持都非常不错，但比较古老的浏览器却可能无法理解DOM提供的方法和属性。因此，即使某位用户在访问你的网站时使用的是支持JavaScript的浏览器，某些脚本也不一定能正常工作。

#### 对象检测

针对这一问题的最简单解决办法就是：检测浏览器对JavaScript的支持程度。这种检测称为**对象检测**。

```javascript
if (method){
	statements
	}
```

例如，如果有一个使用了getElementById()方法的函数，就可以在调用getElementById()方法之前先检查用户所使用的浏览器是否支持这个方法。**在使用对象检测时，一定要删掉方法名后面的圆括号，如果不删掉，测试的将是方法的结果，无论方法是否存在。**

```javascript
function myFunction(){
	if(document.getElementsById){
			statements using getElementById
		}
	}
```

因此，如果某个浏览器不支持getElementById()方法，它就永远也不会执行使用此方法的语句。 这个解决方案的唯一不足是，如此编写出来的函数会增加一对花括号。如果需要在函数里检测多个DOM方法和/或属性是否存在，这个函数中最重要的语句就会被深埋在一层又一层的花括号里。而这样的代码往往很难阅读和理解。 我们可以换种角度：

```javascript
if(!getElementById) return false;
```

例如测试浏览器是否支持getElementById和getElementsByTagName：

```javascript
if(!getElementById || !getElementsByTagName) return false;
```

虽然只是一条简单的if语句，但它可以确保那些“古老的”的浏览器不会因为我的脚本代码而出问题。这么做是为了让脚本有良好的向后兼容性。因为在给网页添加各有关行为时始终遵循了“渐进增强”的原则，所以可以确切地知道我添加的那些功能都能平稳退化，我的网页在那些“古老的”浏览器里也能正常浏览。那些只支持一部分JavaScript功能但不支持DOM的浏览器仍可以访问我的网页的内容。

#### 浏览器嗅探技术

“浏览器嗅探”指通过提取浏览器供应商提供的信息来解决向后兼容性问题。

### 性能考虑

#### 尽量少访问DOM和尽量减少标记

访问DOM的方式对脚本性能会产生非常大的影响。以下面代码为例：

```javascript
if(document.getElementsByTagName("a").length>0){
		var links=document.getElementsByTagName("a");
		for(var i=0;i<links.length;i++){
				//对每个链接做点处理
			}
	}
```

虽然这段代码可以运行，但它不能保持最优的性能。\*\*不管什么时候，只要是查询DOM中的某些元素，浏览器都会搜索整个DOM树，从中查找可能匹配的元素。\*\*这段代码使用了两次getElementsByTagName方法去执行相同的操作，浪费了一次搜索。更好的办法是把第一次搜索的结果保存在一个变量中，然后在循环里重用该结果：

```javascript
var links=document.getElementsByTagName("a");
if(links.length>0){
		for(var i=0;i<links.length;i++){
				//对每个链接做点处理
			}
	}
```

这样一来，代码功能没有变，但搜索DOM的次数由两次降低到了一次。 在多个函数都会取得一组类似元素的情况下，可以考虑重构代码，把搜索结果保存在一个全局变量里，或者把一组元素直接以参数形式传递给函数。 另一个需要注意的地方，就是要尽量减少文档中的标记数量。过多不必要的元素只会增加DOM树的规模，进而增加遍历DOM树以查找特定元素的时间。

#### 合并和放置脚本

包含脚本的最佳方式就是使用外部文件。不过下面这种方式最好不要出现：

```html
<script src="script/functionA.js"></script>
<script src="script/functionB.js"></script>
<script src="script/functionC.js"></script>
<script src="script/functionD.js"></script>
```

推荐做法是把functionA.js、functionB.js、functionC.js、functionD.js合并到一个脚本文件中。这样，就可以减少加载页面时发送的请求数量。而减少请求数量通常都是在性能优化时首先要考虑的。 脚本在标记中的位置对页面的初次加载时间也有很大影响。传统上，我们都把脚本放在文档的\<head>区域，这种放置问题有一个问题。位于\<head>块中的脚本会导致浏览器无法并行加载其他文件（如图像或其他脚本）。一般来说，根据HTTP规范，浏览器每次从同一个域名中最多只能同时下载两个文件。而在下载脚本期间，浏览器不会下载其他任何文件，即使是来自不同域名的文件也不会下载，所有其他资源都要等脚本加载完毕后才能下载。 把所有的\<script>标签都放到文档的末尾，\</body>标记之前，就可以让页面变得更快。即使这样，在加载脚本时，window对象的load事件依然可以执行对文档进行的各种操作。

#### 压缩脚本

所谓压缩脚本，指的是把脚本文件中不必要的字节，如空格和注释，统统删除，从而达到“压缩”文件的目的。 例如以下代码：

```javascript
function showPic(whichpic){
	var source = whichpic.getAttribute("href");
	var placeholder = document.getElementById("placeholder");
	placeholder.setAttribute("src",source);
	
	var text=whichpic.getAttribute("title");
	var description=document.getElementById("description");
	description.firstChild.nodeValue=text;
	}
```

压缩之后的代码就会变成下面这样：

```javascript
function showPic(whichpic){var source = whichpic.getAttribute("href");var placeholder = document.getElementById("placeholder");placeholder.setAttribute("src",source);var text=whichpic.getAttribute("title");var description=document.getElementById("description");description.firstChild.nodeValue=text;}
```

精简后的代码虽然不容易看懂，却能大幅减少文件大小。多数情况下，你应该有两个版本，一个是工作副本，可以修改代码并添加注释；另一个是精简副本，用于放在站点上。通常，为了与非精简版本区分开，最好在精简副本的文件名中加上min字样：

```html
<script src="script/scriptName.min.js"></script>
```

推荐的代码压缩工具：

* [Douglas Crockford的JSMin](http://www.crockford.com/javascript/jsmin.html)
* [雅虎的YUI Compiler](http://developer.yahoo.com/yui/compressor)
* [谷歌的Closure Compiler](http://closure-compiler.appspot.com/home)
