`
jiangzhenghua
  • 浏览: 600026 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

不唐突的JavaScript的七条准则

阅读更多

经过多年的开发、教学和编写不唐突的JavaScript, 我发现了下面的一些准则。我希望它们可以帮助你对“为什么这样设计和执行JavaScript比较好”有一点理解。这些规则曾经帮助我更快地交付产品,并且产品的质量更高,也更容易维护。

1.不要做任何假设
(JavaScript是一个不可靠的助手)

可能不唐突的JavaScript 的最重要的一个特性就是——你要停止任何假设:

    * 不要假设JavaScript是可用的,你最好认为它很有可能是不可用的,而不是直接依赖于它。
    * 在你经过测试确认一些方法和属性可以使用之前,不要假设浏览器支持它们。
    * 不要假设HTML代码如你想象的那样正确,每次都要进行检查,并且当其不可用的时候就什么也不要做。
    * 让JavaScript的功能独立于输入设备
    * 要记住其他的脚本可能会影响你的JavaScript的功能,所以要保证你的脚本的作用域尽可能地安全。

在开始设计你的脚本之前,要考虑的第一件事情就是检查一下你要为其编写脚本的HTML代码,看看有什么东西可以帮助你达到目的。

2.找出钩子和节点关系
(HTML是脚本的基石)

在开始编写脚本之前,要先看一下你要为之编写JavaScript的HTML。如果HTML是未经组织的或者未知的,那么你几乎不可能有一个好的脚本编写方案——很可能就会出现下面的情况:要么是会用JavaScript创建太多标记,要么就是应用太依赖于JavaScript。

在HTML中有一些东西需要考虑,那就是钩子和节点关系。

<1>.HTML 钩子

HTML最初的和最重要的钩子就是ID,而且ID可以通过最快的DOM方法——getElementById 访问到。如果在一个有效的HTML文档中所有的ID都是独一无二的话(在IE中关于name 和 ID 有一个bug,不过有些好的类库解决了这个问题),使用ID就是安全可靠的,并且易于测试。

其他一些钩子就是是HTML元素和CSS类,HTML元素可以通过getElementsByTagName方法访问,而在多数浏览器中都还不能通过原生的DOM方法来访问CSS类。不过,有很多外部类库提供了可以访问CSS类名(类似于 getElementsByClassName) 的方法。

<2>.HTML 节点关系

关于HTML的另外比较有意思的一点就是标记之间的关系,思考下面的问题:

    * 要怎样才可以最容易地、通过最少的DOM遍历来到达目标节点?
    * 通过修改什么标记,可以尽可能多地访问到需要修改的子节点?
    * 一个给定的元素有什么属性或信息可以用来到达另外一个元素?

遍历DOM很耗资源而且速度很慢,这就是为什么要尽量使用浏览器中已经在使用的技术来做这件事情。

3.把遍历交给专家来做
(CSS,更快地遍历DOM)

有关DOM的脚本和使用方法或属性(getElementsByTagName, nextSibling, previousSibling, parentNode以及其它)来遍历DOM似乎迷惑了很多人,这点很有意思。而有趣的是,我们其实早已经通过另外一种技术—— CSS ——做了这些事情。

CSS 是这样一种技术,它使用CSS选择器,通过遍历DOM来访问目标元素并改变它们的视觉属性。一段复杂的使用DOM的JavaScript可以用一个CSS选择器取代:

Java代码 复制代码
  1. var n = document.getElementById('nav');   
  2. if(n){   
  3. var as = n.getElementsByTagName('a');   
  4. if(as.length > 0){   
  5. for(var i=0;as[i];i++){   
  6. as[i].style.color = ‘#369′;   
  7. as[i].style.textDecoration = ‘none’;   
  8. }   
  9. }   
  10. }   
  11.   
  12. /* 下面的代码与上面功能一样 */  
  13.   
  14. #nav a{   
  15. color:#369;   
  16. text-decoration:none;   
  17. }  
var n = document.getElementById('nav');
if(n){
var as = n.getElementsByTagName('a');
if(as.length > 0){
for(var i=0;as[i];i++){
as[i].style.color = ‘#369′;
as[i].style.textDecoration = ‘none’;
}
}
}

/* 下面的代码与上面功能一样 */

#nav a{
color:#369;
text-decoration:none;
}



这是一个可以好好利用的很强大的技巧。你可以通过动态为DOM中高层的元素添加class 或者更改元素ID来实现这一点。如果你使用DOM为文档的body添加了一个CSS类,那么设计师就很可以容易地定义文档的静态版本和动态版本。

Java代码 复制代码
  1. JavaScript:   
  2.   
  3. var dynamicClass = 'js';   
  4. var b = document.body;   
  5. b.className = b.className ? b.className + ' js' : 'js';   
  6.   
  7. CSS:   
  8.   
  9. /* 静态版本 */  
  10.   
  11. #nav {   
  12. ....   
  13. }   
  14.   
  15. /* 动态版本 */  
  16.   
  17. body.js #nav {   
  18. ....   
  19. }  
JavaScript:

var dynamicClass = 'js';
var b = document.body;
b.className = b.className ? b.className + ' js' : 'js';

CSS:

/* 静态版本 */

#nav {
....
}

/* 动态版本 */

body.js #nav {
....
}



4.理解浏览器和用户
(在既有的使用模式上创建你所需要的东西)

不唐突的JavaScript 中很重要的一部分就是理解浏览器是如何工作的(尤其是浏览器是如何崩溃的)以及用户期望的是什么。不考虑浏览器你也可以很容易地使用JavaScript 创建一个完全不同的界面。拖拽界面,折叠区域,滚动条和滑动块都可以使用JavaScript创建,但是这个问题并不是个简单的技术问题,你需要思考下面的问题:

    * 这个新界面可以独立于输入设备么?如果不能,那么可以依赖哪些东西?
    * 我创建的这个新界面是否遵循了浏览器或者其它富界面的准则(你可以通过鼠标在多级菜单中直接切换吗?还是需要使用tab键?)
    * 我需要提供什么功能但是这个功能是依赖于JavaScript的?

最后一个问题其实不是问题,因为如果需要你就可以使用DOM来凭空创建HTML。关于这点的一个例子就是“打印”链接,由于浏览器没有提供一个非 JavaScript的打印文档功能,所以你需要使用DOM来创建这类链接。同样地,一个实现了展开和收缩内容模块的、可以点击的标题栏也属于这种情况。标题栏不能被键盘激活,但是链接可以。所以为了创建一个可以点击的标题栏你需要使用JavaScript将链接加入进去,然后所有使用键盘的用户就可以收缩和展开内容模块了。

解决这类问题的极好的资源就是设计模式库。至于要知道浏览器中的哪些东西是独立于输入设备的,那就要靠经验的积累了。首先你要理解的就是事件处理机制。

5.理解事件
(事件处理会引起改变)

事件处理是走向不唐突的JavaScript的第二步。重点不是让所有的东西都变得可以拖拽、可以点击或者为它们添加内联处理,而是理解事件处理是一个可以完全分离出来的东西。我们已经将HTML,CSS和JavaScript分离开来,但是在事件处理的分离方面却没有走得很远。

事件处理器会监听发生在文档中元素上的变化,如果有事件发生,处理器就会找到一个很奇妙的对象(一般会是一个名为e的参数),这个对象会告诉元素发生了什么以及可以用它做什么。

对于大多数事件处理来说,真正有趣的是它不止发生在你想要访问的元素上,还会在DOM中较高层级的所有元素上发生(但是并不是所有的事件都是这样,focus和blur事件是例外)。举例来说,利用这个特性你可以为一个导航列表只添加一个事件处理器,并且使用事件处理器的方法来获取真正触发事件的元素。这种技术叫做事件委托,它有几点好处:

    * 你只需要检查一个元素是否存在,而不需要检查每个元素
    * 你可以动态地添加或者删除子节点而并不需要删除相应的事件处理器
    * 你可以在不同的元素上对相同的事件做出响应

需要记住的另一件事是,在事件向父元素传播的时候你可以停止它而且你可以覆写掉HTML元素(比如链接)的缺省行为。不过,有时候这并不是个好主意,因为浏览器赋予HTML元素那些行为是有原因的。举个例子,链接可能会指向页面内的某个目标,不去修改它们能确保用户可以将页面当前的脚本状态也加入书签。

6.为他人着想
(命名空间,作用域和模式)

你的代码几乎从来不会是文档中的唯一的脚本代码。所以保证你的代码里没有其它脚本可以覆盖的全局函数或者全局变量就显得尤为重要。有一些可用的模式可以来避免这个问题,最基础的一点就是要使用 var 关键字来初始化所有的变量。假设我们编写了下面的脚本:

Java代码 复制代码
  1. var nav = document.getElementById('nav');   
  2. function init(){   
  3. // do stuff   
  4. }   
  5. function show(){   
  6. // do stuff   
  7. }   
  8. function reset(){   
  9. // do stuff   
  10. }  
var nav = document.getElementById('nav');
function init(){
// do stuff
}
function show(){
// do stuff
}
function reset(){
// do stuff
}


上面的代码中包含了一个叫做nav的全局变量和名字分别为 init,show 和 reset 的三个函数。这些函数都可以访问到nav这个变量并且可以通过函数名互相访问:

Java代码 复制代码
  1. var nav = document.getElementById('nav');   
  2. function init(){   
  3. show();   
  4. if(nav.className === 'show'){   
  5. reset();   
  6. }   
  7. // do stuff   
  8. }   
  9. function show(){   
  10. var c = nav.className;   
  11. // do stuff   
  12. }   
  13. function reset(){   
  14. // do stuff   
  15. }  
var nav = document.getElementById('nav');
function init(){
show();
if(nav.className === 'show'){
reset();
}
// do stuff
}
function show(){
var c = nav.className;
// do stuff
}
function reset(){
// do stuff
}



你可以将代码封装到一个对象中来避免上面的那种全局式编码,这样就可以将函数变成对象中的方法,将全局变量变成对象中的属性。 你需要使用“名字+冒号”的方式来定义方法和属性,并且需要在每个属性或方法后面加上逗号作为分割符。

Java代码 复制代码
  1. var myScript = {   
  2. nav:document.getElementById('nav'),   
  3. init:function(){   
  4. // do stuff   
  5. },   
  6. show:function(){   
  7. // do stuff   
  8. },   
  9. reset:function(){   
  10. // do stuff   
  11. }   
  12. }  
var myScript = {
nav:document.getElementById('nav'),
init:function(){
// do stuff
},
show:function(){
// do stuff
},
reset:function(){
// do stuff
}
}



所有的方法和属性都可以通过使用“类名+点操作符”的方式从外部和内部访问到。

Java代码 复制代码
  1. var myScript = {   
  2. nav:document.getElementById('nav'),   
  3. init:function(){   
  4. myScript.show();   
  5. if(myScript.nav.className === 'show'){   
  6. myScript.reset();   
  7. }   
  8. // do stuff   
  9. },   
  10. show:function(){   
  11. var c = myScript.nav.className;   
  12. // do stuff   
  13. },   
  14. reset:function(){   
  15. // do stuff   
  16. }   
  17. }  
var myScript = {
nav:document.getElementById('nav'),
init:function(){
myScript.show();
if(myScript.nav.className === 'show'){
myScript.reset();
}
// do stuff
},
show:function(){
var c = myScript.nav.className;
// do stuff
},
reset:function(){
// do stuff
}
}



这种模式的缺点就是,你每次从一个方法中访问其它方法或属性都必须在前面加上对象的名字,而且对象中的所有东西都是可以从外部访问的。如果你只是想要部分代码可以被文档中的其他脚本访问,可以考虑下面的模块(module)模式:

Java代码 复制代码
  1. var myScript = function(){   
  2. //这些都是私有方法和属性   
  3. var nav = document.getElementById('nav');   
  4. function init(){   
  5. // do stuff   
  6. }   
  7. function show(){   
  8. // do stuff   
  9. }   
  10. function reset(){   
  11. // do stuff   
  12. }   
  13. //公有的方法和属性被使用对象语法包装在return 语句里面   
  14. return {   
  15. public:function(){   
  16.   
  17. },   
  18. foo:'bar'  
  19. }   
  20. }();  
var myScript = function(){
//这些都是私有方法和属性
var nav = document.getElementById('nav');
function init(){
// do stuff
}
function show(){
// do stuff
}
function reset(){
// do stuff
}
//公有的方法和属性被使用对象语法包装在return 语句里面
return {
public:function(){

},
foo:'bar'
}
}();



你可以使用和前面的代码同样的方式访问返回的公有的属性和方法,在本示例中可以这么访问:myScript.public() 和 myScript.foo 。但是这里还有一点让人觉得不舒服:当你想要从外部或者从内部的一个私有方法中访问公有方法的时候,还是要写一个冗长的名字(对象的名字可以非常长)。为了避免这一点,你需要将它们定义为私有的并且在return语句中只返回一个别名:

Java代码 复制代码
  1. var myScript = function(){   
  2. // 这些都是私有方法和属性   
  3. var nav = document.getElementById('nav');   
  4. function init(){   
  5. // do stuff   
  6. }   
  7. function show(){   
  8. // do stuff   
  9. // do stuff   
  10. }   
  11. function reset(){   
  12. // do stuff   
  13. }   
  14. var foo = 'bar';   
  15. function public(){   
  16.   
  17. }  
var myScript = function(){
// 这些都是私有方法和属性
var nav = document.getElementById('nav');
function init(){
// do stuff
}
function show(){
// do stuff
// do stuff
}
function reset(){
// do stuff
}
var foo = 'bar';
function public(){

}



//只返回指向那些你想要访问的私有方法和属性的指针

Java代码 复制代码
  1. return {   
  2. public:public,   
  3. foo:foo   
  4. }   
  5. }();  
return {
public:public,
foo:foo
}
}();



这就保证了代码风格一致性,并且你可以使用短一点的别名来访问其中的方法或属性。

如果你不想对外部暴露任何的方法或属性,你可以将所有的代码封装到一个匿名方法中,并在它的定义结束后立刻执行它:

Java代码 复制代码
  1. (function(){   
  2. // these are all private methods and properties   
  3. var nav = document.getElementById('nav');   
  4. function init(){   
  5. // do stuff   
  6. show(); // 这里不需要类名前缀   
  7. }   
  8. function show(){   
  9. // do stuff   
  10. }   
  11. function reset(){   
  12. // do stuff   
  13. }   
  14. })();  
(function(){
// these are all private methods and properties
var nav = document.getElementById('nav');
function init(){
// do stuff
show(); // 这里不需要类名前缀
}
function show(){
// do stuff
}
function reset(){
// do stuff
}
})();



对于那些只执行一次并且对其它函数没有依赖的代码模块来说,这种模式非常好。

通过遵循上面的那些规则,你的代码更好地为用户工作,也可以使你的代码在机器上更好地运行并与其他开发者的代码和睦相处。不过,还有一个群体需要考虑到。

7.为接手的开发者考虑
(使维护更加容易)

使你的脚本真正地unobtrusive的最后一步是在编写完代码之后仔细检查一遍,并且要照顾到一旦脚本上线之后要接手你的代码的开发者。考虑下面的问题:

    * 所有的变量和函数名字是否合理并且易于理解?
    * 代码是否经过了合理的组织?从头到尾都很流畅吗?
    * 所有的依赖都显而易见吗?
    * 在那些可能引起混淆的地方都添加了注释吗?

最重要的一点是:要认识到文档中的HTML和CSS代码相对于JavaScript来说更有可能被改变(因为它们负责视觉效果)。所以不要在脚本代码中包含任何可以让终端用户看到的class和ID,而是要将它们分离出来放到一个保存配置信息的对象中。

Java代码 复制代码
  1. myscript = function(){   
  2. var config = {   
  3. navigationID:'nav',   
  4. visibleClass:'show'  
  5. };   
  6. var nav = document.getElementById(config.navigationID);   
  7. function init(){   
  8. show();   
  9. if(nav.className === config.visibleClass){   
  10. reset();   
  11. };   
  12. // do stuff   
  13. };   
  14. function show(){   
  15. var c = nav.className;   
  16. // do stuff   
  17. };   
  18. function reset(){   
  19. // do stuff   
  20. };   
  21. }();  
myscript = function(){
var config = {
navigationID:'nav',
visibleClass:'show'
};
var nav = document.getElementById(config.navigationID);
function init(){
show();
if(nav.className === config.visibleClass){
reset();
};
// do stuff
};
function show(){
var c = nav.className;
// do stuff
};
function reset(){
// do stuff
};
}();



这样维护者就知道去哪里修改这些属性,而不需要改动其他代码。

分享到:
评论

相关推荐

    不唐突的JavaScript的七条准则整理收集第1/2页

    标题《不唐突的JavaScript的七条准则整理收集》表明本文将介绍在开发JavaScript应用时应遵循的七条重要原则或建议。而副标题“在开始设计你的脚本之前,要考虑的第一件事情就是检查你要为其编写脚本的HTML代码,看看...

    mvc非唐突ajax示例项目

    “mvc非唐突ajax示例项目”这个标题表明这是一个关于使用MVC(Model-View-Controller)架构的Web应用示例,它着重于如何在不打断用户界面流程的情况下,通过Ajax(Asynchronous JavaScript and XML)技术进行数据...

    [JavaScript.DOM高级程序设计](加)桑贝斯.扫描版.part1.rar

     1.1 不唐突和渐进增强   1.2 让JavaScript运行起来   1.2.1 把行为从结构中分离出来   1.2.2 不要版本检测   1.2.3 通过平稳退化保证可访问性   1.2.4 为重用命名空间而进行规划   1.2.5 通过...

    [JavaScript.DOM高级程序设计](加)桑贝斯.扫描版.part3.rar

     1.1 不唐突和渐进增强   1.2 让JavaScript运行起来   1.2.1 把行为从结构中分离出来   1.2.2 不要版本检测   1.2.3 通过平稳退化保证可访问性   1.2.4 为重用命名空间而进行规划   1.2.5 通过...

    [JavaScript.DOM高级程序设计](加)桑贝斯.扫描版.part2.rar

     1.1 不唐突和渐进增强   1.2 让JavaScript运行起来   1.2.1 把行为从结构中分离出来   1.2.2 不要版本检测   1.2.3 通过平稳退化保证可访问性   1.2.4 为重用命名空间而进行规划   1.2.5 通过...

    jquery 笔记精要

    - **不唐突的JavaScript**:为了保持HTML文档的良好结构,推荐将JavaScript代码从HTML文档中分离出来。一种做法是将脚本放置于`&lt;head&gt;`标签内或页面底部(即`&lt;/body&gt;`和`&lt;/html&gt;`标签之间)。这种做法虽然可能对性能...

    唐突的「Brusque」-crx插件

    一个极简主义者pnut。输入输出端 Pnut.io社交网络的WebExtensions客户端。 需要一个帐户。 亚伯拉罕的继承权用于App.net,但适用于Pnut! 当前功能*从URL栏中发布到您的流中*从URL栏中查找个人资料*从URL栏中查找#...

    软件易用性

    在测试用户界面时,测试人员要考虑以下问题:用户界面是否洁净、不唐突、不拥挤?UI的组织和布局合理吗?是否允许用户轻松地从一个功能转到另一个功能?有多余的功能吗?软件整体抑或局部是否做得太多?是否有太多...

    2006考研词汇

    (举止、言谈等)唐突的 absence n. 缺席,不在场;缺乏,没有 absent a.(from)缺席,不在场;漫不经心的 absolute a. 绝对的,完全的 absorb v. 吸收; 吸引,使专心 abstract a. 抽象的n. 摘要,提要v. 提(抽)取 absurd...

    jQuery基础教程(第四版)

    同时,还会介绍如何以不唐突的方式添加事件(甚至在页面加载完成之前)。此外,这一章还将深入更高级的主题,例如事件冒泡、委托和命名空间。 第4章介绍通过jQuery实现动画的技术,我们将学会隐藏、显示和移动页面...

    界面测试 测试对不用分辨率下页面显示效果

    * 用户界面是否洁净、不唐突、不拥挤,界面不应该为用户制造障碍; * 所需功能或者期待的响应应该明显,预期出现的地方明显,下一步做什么明显。 7. 界面测试一致性要点: * 快速键和菜单选项,术语和命令的一致...

    JS控制图片左右滚动代码

    在这个栏目中,新浪采用了两个滚动动画,点击左右箭头,图片可以一张张切换,切换得一点都不“唐突”,这就是jquery的功能了。像这样的动画,很常见,之前我在腾讯的女性频道上也做过此类的滚动动画。可惜有点遗憾,...

    第六届大学生知识竞赛考试题借鉴.pdf

    例如,"鉴诫"应为"鉴戒","搪突"应为"唐突","敬效尤"应为"儆效尤","卫戌"应为"卫戍","再接再励"应为"再接再厉","入不敷出"的"敷"不应写成"付","幅员辽阔"的"幅"不应写成"副","文过饰非"的"饰"不应写成"试"等。...

    pets5大纲词汇--新鲜芒果.doc

    A a(an) art. 一,一个,每个;(同类事物中 的)任何一个 abandon n....v....抛弃;...abbreviate vt....abbreviation n....(举 止、言谈等)唐突的;鲁莽的 absence n. 缺席,不在;缺席的时间,外出期;缺乏,不存在

    cet6级高频词汇总汇

    3. abrupt (突然的, 唐突的): 描述事情发生的快速和无预警,或者人的行为举止直接而不客气,例如"Your abrupt manner embarrassed her!" 意为“你唐突的态度让她很尴尬!” 4. absurd (荒唐的): 用于形容不合理或...

    浙江专用2021高考语文一轮复习专题八语言表达的简明连贯得体准确鲜明生动精练含解析

    得体则是指语言要符合语境和交际规范,不唐突,不冒犯;准确意味着用词恰当,表达精确无误;鲜明指的是表达要具有辨识度,让读者印象深刻;生动则要求语言富有感染力,能够引起读者的情感共鸣。 2. **高考题型分析*...

    商务礼仪基本理论课件.pptx

    在询问客人饮料需求时,应以礼貌且不唐突的方式提出;而在公司会议上,董事长和总经理的座次通常按照职位高低来安排,董事长通常居中,总经理在其一侧。 商务礼仪的作用不可忽视。它能提升个人素质,符合现代企业...

    六级必考500词六级必考500词

    突然的, 唐突的:形容词,常用来形容行为、态度或事件的突然发生,或语言表达的直接不礼貌。 4. absurd a. 荒唐的:形容词,表示某事不合理、荒谬或不符合逻辑。 5. accessory n. 附件, 零件:名词,指配件、附属品...

    2020高考语文二轮复习精品资料专题02 识记并正确书写现代常用规范汉字((名校测试)(学生版).doc

    14. 常见字词的正确拼写:“饿殍”、“唐突”、“贸然”、“振聋发聩”均是常用词汇,要求考生熟悉并能正确书写。 15. 错误辨析:“再接再厉”中的“励”常被误写为“历”,“代价而沽”应为“待价而沽”,“食不裹...

Global site tag (gtag.js) - Google Analytics