JavaScript DOM 编程艺术 Note(1)
我记得以前曾经在哪儿看过一句话
“一个程序员写代码的时候,只有一小部分的代码是用来实现功能的,其余大部分代码都是在处理特殊情况”
当然 我觉得说得有些夸张了233 不过不能否认的是 代码的健壮性是衡量一段代码是否良好的重要指标之一
最近刚开始学JavaScript 觉得书里一些方面说得挺有道理 故而做笔记加以记录。
与DOM脚本编程工作相关的几个良好习惯
- 平稳退化
- 分离JavaScript
- 向后兼容性
- 性能考虑
平稳退化
平稳退化 (graceful degradation):意指在用户的浏览器不支持JavaScript或用户手动禁用了JavaScript的情况下,仍能保证他们能够顺利浏览网站(即便一些进阶的功能无法使用)
比如,当你需要实现一个以新窗口的形式打开一个链接的功能(必要),你或许会选择写一个JavaScript函数并存入外部文件,并在网页中通过<script>标签导入。那么,如何调用函数呢?
不推荐的做法:
- 使用伪协议
- 用内嵌事件处理函数
伪协议 (pseudo-protocol) , 即类似于(假设popUp()即所写的函数)
1 | <a href="javascript:popUp('http://www.example.com/');">Example</a> |
这种语句在支持“javascript:”伪协议的浏览器中运行正常,较老的浏览器则会尝试打开那个链接而失败,支持这种伪协议但禁用JavaScript功能的浏览器会什么也不做。
内嵌事件 :如把onclick事件处理函数作为属性嵌入<a>标签,
1 | <a href="#" onclick="popUp('http://www.example.com/'); |
同上,对于不支持或禁用JavaScript的用户来说依旧不能实现功能。
推荐的做法:
增加一个选择:使用内嵌的事件处理函数退而求其次地实现最基本的功能。
依旧以这个比方为例,我们可以把href属性设置为真实有效的URL地址。
1 | <a href="http://www.example.com/" |
虽然不能实现打开一个新窗口的进阶功能,但基本功能——打开这个链接能够实现,并没有彻底失效。
这是一个经典的“平稳退化”的例子。
分离JavaScript
为了过渡到分离JavaScript的话题,先提及一个概念——渐进增强
所谓“渐进增强”就是用一些额外的信息层去包裹原始数据。按照“渐进增强”原则创建出来的网页几乎都符合“平稳退化”的原则。
类似于CSS,把JavaScript代码从HTML文档里分离出来可以让JavaScript工作得更好。
JavaScript语言不要求事件必须在HTML文档里处理,因而我们可以在外部JavaScript文件里把一个事件添加到HTML的文档中的某个元素上:
element.event = action…
而确定事件的元素,可以用class或id属性来解决(配合getElementById)。
若涉及多个元素,则可以用getElementsByTagName或getAttribute
依旧使用上面的例子。
您只需要在HTML文档里输入:
1 | <a href="http://www.example.com/" class="popup">Example</a> |
并在JavaScript文档里输入:
1 | var links = document.getElementsByTagName("a"); |
此时,已经成功分离JavaScript。
然而,还有一个问题尚待解决:若直接将上面的代码写入JavaScript文档,是无法正常运行的。因为这段代码的第一行会在JavaScript文档被加载时立刻执行。若JavaScript文档是从HTML文档的<head>部分由<script>标签调用的,它将在HTML文档之前加载到浏览器里。同样,如果<script>标签位于文档底部之前,就不能保证哪个文件最先结束加载。因为脚本加载时文档可能不完整,所以模型也不完整。没有完整的DOM,getElementByTagName等方法就不能正常工作。
而由于onload方法的存在,使上段代码的正常运行成为了可能。
1 | window.onload = prepareLinks; |
向后兼容
不得不说,即使你不愿意相信,确实存在那些至此JavaScript但无法理解DOM提供的方法和属性的浏览器。
对这个问题最简单的解决方案是:对象检测 (object detection)
如,您使用了getElementById()方法,即可在调用这个方法前检查用户使用的浏览器是否支持这个方法。
依旧以上段代码为例:
1 | window.onload = function(){ |
注意:在使用if()条件句进行对象检测时,一定要删掉方法名后面的圆括号。若不删掉,测试的将是方法的结果,无论方法是否存在。
性能考虑
对于性能优化,以下讨论几个方面:
- 尽量少访问DOM和尽量减少标记
- 合并和放置脚本
- 压缩脚本
尽量少访问DOM和尽量减少标记
只要是查询DOM中的某些元素,浏览器都会搜索整个DOM树,从中查找可能匹配的元素。若代码中多次使用相关方法进行相同操作,无疑是在浪费搜索次数。
更好的办法是将第一次搜索的结果存在一个变量中,以待第二次对该结果实现其他功能。
当然,或许还存在多个函数重复做一件事的情况。比如,一个函数检查每个链接中的A类,另一个函数检查每个链接中的B类,同样也会造成搜索浪费。在多个函数都会取得一组类似元素的情况下,建议考虑重构代码,将搜索结果存在一个全员变量里,或把一组元素直接以参数形式传递给函数。
另一个需要注意的地方:尽量减少文档中的标记数量。过多不必要的元素会增加DOM树的规模,进而增加遍历时间。
合并和放置脚本
推荐将实现不同功能的函数合并到较少个脚本文件中。这样即可减少加载页面时发送的请求数量。
同样,脚本在标记中的位置对页面的初次加载也有很大影响。
一般来说,根据HTTP规范,浏览器每次从同一个域名中最多只能同时下载两个文件。而下载脚本期间,浏览器不会下载其他任何文件,即使是来自不同域名的文件也不会下载,所有其他资源都要等脚本加载完毕后才能下载。
故将脚本放在文档的<head>区域是存在问题的。它会导致浏览器无法并行加载其他文件。
建议将<script>标签都放在文档末尾,即</body>之前,就可以使页面变得更快。
压缩脚本
所谓压缩脚本,指的是把脚本文件中不必要的字节,如空格和注释等,统统删除。
诚然,压缩后的脚本不便于阅读,却可以大幅度减少文件大小。多数情况下,您应该有两个版本,一个是工作副本,可以修改代码并添加注释;另一个是精简副本,用于放在站点上。通常,为了与非精简版本区分开,最好在精简副本的文件名中加上min字样。
1 | <script src="scripts/scriptName.min.js"></script> |
推荐几个有代表性的代码压缩工具:
- Douglas Crockford 的 JSMin(http://www.crockford.com/javascript/jsmin.html);
- 雅虎的 YUI Compressor(http://yui.github.io/yuicompressor/);
- 谷歌的 Closure Compiler(https://closure-compiler.appspot.com/home)
(P.S. 在md文件中如果要写纯文本的”<”和”&”的话,”<”要写成”< ;”(去掉分号前面的空格) ,”&”要写成”& ;”(去掉分号前面的空格) ,即使用实体的形式)
__END__