成功的Javascript开发者的7个习惯[2] —— 柔性减弱和不唐突的Javascript
- 十月 26th, 2009
——节选自《Javascript实战》(Practical Javascript,DOM Scripting,and Ajax Projects),作者:Frank W.Zammetti。
2.2 柔性减弱和不唐突的Javascript
不唐突的Javascript是一个最近才流行起来的术语。简单地说,它是一种趋势,网页中的Javascript以一种不影响页面的方式来写。
不唐突的Javascript的基本原则非常简单,可以被归纳为以下几点:
★ 保持Javascript独立。
★ 通常允许柔性减弱。
★ 从不使用浏览器嗅探脚本来判断一个浏览器的能力。
★ 任何情况下,都不要写浏览器不兼容的代码,具体说,就是别写浏览器相关的Javascript代码。
★ 合适的作用域变量。
★ 对于可访问性,避免需要鼠标事件触发的触发器。
但是,不唐突的Javascript术语对于不同的人也可以有不同的意思。一些人喜欢扩展规则并使其更加严格。另一些人喜欢修建规则并使其更灵活。关键是使用我们从以前的错误中学到的方法来实现 Javascript。
现在让我们来看看每一个基础规则的详细内容。
2.2.1 让Javascript保持独立
这个观点是把Javascript当作应用程序的一层来处理,并且使用定义明确的交互点,试着使它尽可能地独立。例如,总是从外部文件导入Javascript,并没有任何的Javascript嵌在HTML中。
我认为,这是一条比较灵活的规则。但是基本的观点是说:最大限度地保持脚本独立。
考虑一下CSS。你已经习惯于外部样式表了,对吗?那么用同样的方法来看待Javascript。这样做逻辑上把页面分成了几个部分,我们就可以很容易地找到自己感兴趣、想要修改的地方。同时它还给你留下一条重用的途径。外部保存的脚本有更好的机会在其他页面、其他站点以及其他项目中重用。虽然不能保证肯定能被使用,但它往往是有帮助的。
一些人甚至提倡通过脚本来添加事件处理函数。通常,这么做的原因是把脚本和标记完全分离,避免在修改函数名之后要修改代码中的很多地方。我不完全同意这个建议,主要是因为,一个事件处理函数是对于一个指定元素特定的,所以为什么不把它附近在那个元素上呢?对于我来说,如果我需要改变一个事件处理函数,直接找到那个元素要比找出哪个外部.js文件包含那段代码更加容易。不过有一个例外,如果处理函数是共享的(被多于一个的元素使用),这种情况下,我就会将它外部化。
到底什么样才是最好的,留给你自己去归纳。然而,我非常鼓励你保持事件处理函数尽可能的小,无论把它们放在哪。它们应该只是调用一些大块的代码或者执行一两行语句。如果你想方法让事件处理函数与元素在同一行内,那会是个更好的主意。
2.2.2 允许柔性减弱
离开了Javascript,页面也还应该工作,即使是在以衰减的形式。一个很好的例子是表单验证。不要只适用调用一个表单的函数的按钮,因为如果离开了Javascript,这个表单就不能提交了。比如,尝试下面代码清单2-1的代码,并观察一下禁用了Javascript会发生什么。
<html>
<head>
<script>
function doSubmit(inForm){
if(inForm.firstName.value == ""){
alert("You must enter a first name");
return false;
}
if(inForm.lastName.value == ""){
alert("You must enter a last name");
return false;
}
inForm.submit();
return true;
}
</script>
</head>
<body>
<form name="test" action="#" method="post">
First name:<input type="test" name="firstName" />
<br />
Last name:<input type="test" name="lastName" />
<br />
<input type="button" onClick="doSubmit(this.form);" value="Submit" />
</form>
</body>
</html>
如果禁用Javascript后运行上面的代码,你将看到什么都没有发生,因为表单的提交依赖Javascript的执行,这显然是不好的。
取代它的办法是,在onSubmit时间的响应中进行验证。使用这种方法,如果Javascript启用,用户可以从客户端验证中受益。但是如果Javascript被禁用了,表单仍然可以被提交。代码清单2-2展示了它是如何工作的。
<html>
<head>
<script>
function doSubmit(inForm){
if(inForm.firstName.value == ""){
alert("You must enter a first name");
return false;
}
if(inForm.lastName.value == ""){
alert("You must enter a last name");
return false;
}
inForm.submit();
return true;
}
</script>
</head>
<body>
<form name="test" action="#" method="post" onSubmit="return doSubmit(this)">
First name:<input type="test" name="firstName" />
<br />
Last name:<input type="test" name="lastName" />
<br />
<input type="submit" value="Submit" />
</form>
</body>
</html>
顺便说一下,一个致命的错误是:使用单纯的客户端验证,并假设从客户端来的任何数据都是好的。事实上,你的系统通常应该被设计成为认为从客户端来的数据都是坏的。在客户端做验证是完全可行的。不过,几乎没人愿意使用的系统把客户端验证作为唯一验证方法。
好,我们来谈一些观点。有人认为,我们应该在所有Web应用上附加一些不唐突的原则和一些合理的、柔性衰减的安全约束。我并不同意这种观点,甚至可以说,我认为这种观点完全是无原则的。
[...]
我们所处的时代是“RIA(Rich Internet Application,富因特网应用)”时代,RIA开始统治因特网。比如说,Google,现在就处在把一整套office软件套件放在网上给所有用户使用的早期时代(见http://docs.google.com)。你认为所有开发人员都会力图遵守所有不唐突的原则吗(除了少数最低限度的之外)?并且这么做了之后还能用?几乎可以肯定地说这不可能,因为对于这种高端应用来说,这就是不肯能的任务。让我说得更明白些:在RIA世界里,大多数这里描述的不唐突的要求仍然是可为的。只是其中的某些可能不能做到罢了。
这就是网站和Web应用之间的差距。从某种意义上来说,网站的目的是散播信息。它只有很有限度的用户交互需求,通常来说,简单的HTML表单就足够好用了。在一个网站里,所有这里描述的规则几乎都应该遵守,而且更重要的是,这些规则是可以遵守的。相对来说,Web应用则更复杂并且需要更高级的用户交互。它们就是为了替换胖客户端、应用程序等那些用户已经习惯了几十年的东西。这些用户需要功能强大同时又简洁的动态交互界面、铃声和报警声,以及一些不写代码就不能实现的特性——并且有些代码必须在客户端里组织起来。在这种场合,我的观点是:遵守这些规则就是不合理的,会导致无数项目的失败。
不过,对于任何在Web上给公众使用的东西,你都毫无疑问地应该争取优秀的可用性、合理的柔性衰减,以及所有其他不唐突的Javascript目标。对于无法实现这些目标的地方,我们应该有很清晰、和合理的原因,并且绝对可以确信:如果要实现这些目标,你的应用就无法实现。(我汗-_-|||还好语文学得好,呃呃)
所以,简而言之,我认为你应该审视一下你正在做什么,以及你的目标是什么,并决定这些指导哪些应该遵循。不要犯错误,我相信你会试着遵守全部这些规则。但是我估计那有时并不可能。再说一次,这是我个人的意见,请根据自己最好的判断形成自己的意见。
2.2.3 不要使用浏览器嗅探例程
与其使用浏览器嗅探脚本去确定一个浏览器的能力,还不如检查对象的存在和对象的能力。这个问题的补充是,只是由于开发者的懒惰而引起的Javascript错误,比如在访问一个给定对象之前没有检查它是否存在,是无理且不好的。
一个例子,看看下面的代码:
function setContent(inobj,inContent{
inObject.innerHTML = inContent;
}
);
这里,如果对象inObj并不支持innerHTML,就会出现一个错误。可能由于innerHTML不是DOM中的一个标准的部分(虽然,实际上我不知道有哪个浏览器没实现它)。重写这部分来避免这个错误很简单:
function setContent(inobj,inContent{
if(inObj.innerHTML){
inObject.innerHTML = inContent;
}
}
);
[/javascript]
通常像这样检查是个好习惯,它实际上是确定一个浏览器是否支持一个特定的功能。这样做的一个最好的例子是基本的Ajax编程,你需要一个XMLHttpRequest对象的实例。不幸的是,不同的浏览器使用不同的方法来支持这个对象的使用。然而,你可以检查不同的对象,并基于它们的存在或不存在,来分支代码,像这样:
var xhr = null;
if(window.XMLHttpRequest){
xhr = new XMLHttpRequest();
}
else if(window.ActiveXObject){
xhr = new ActiveXObject(Microsoft.XMLHTTP);
}
然而,如果能写根本不需要分支的代码会更好,但是那通常不太可能。浏览器嗅探是个坏主意,因为,很多开发者多年来的体会是,你总是需要保持嗅探代码的更新,以便让它能识别新的浏览器。对象存在性的检查就没那么脆弱了,通常不需要重写代码来处理新的浏览器,所以这个技术比嗅探浏览器好得多。






