什么是可维护性代码
1、可理解性(其他人可以接手代码并理解它的意图和一般途径,而无需原开发人员的完整解释) 2、直观性(代码中的东西一看就能明白,不管其操作过程多么复杂) 3、可适应性(代码以一种数据上的变化不要求完全重写的方法撰写) 4、可扩展性(在代码架构上已考虑到在未来允许对核心功能进行拓展) 5、可调试性(当有地方出错时,代码可以给予你足够对信息来尽可能直接地确定问题所在)
代码约定
1、可读性
a、函数和方法 —— 每个函数或方法都应该包含一个注释,描述其目的和用于完成任务所可能使用都算法。陈述事先的假设也非常重要,如参数代表,函数是否有返回值(因为这不能从函数定义中推断出来)
b、大段代码 —— 用于单个任务的多行代码应该在前面放一个描述任务的注释
c、复杂的算法 —— 如果使用了一种独特的方式解决某个问题,则要在注释中解释你是如何做的。
d、hack —— 因为存在浏览器差异,js代码一般会包含一些hack。不要假设其他人在看代码的时候能够理解hack所要应付的浏览器问题,清将这些信息放在注释中。
2、变量和函数命名
由于很多js开发人员最初都只是业余爱好者,所以有一种使用无意义名字的倾向,诸如foo,bar,dosomething这样的名字。专业的js开发人员必须克服这些恶习以创建可维护性的代码。
a、变量名应为名词 如car 或 person
b、函数名应该以动词开始,如getName()。返回布尔类型值的函数一般以is开头,如isEnable();
c、变量和函数都应该使用合乎逻辑的名字,不要担心长度。长度问题可以通过后处理和压缩来缓解
3、变量类型透明
由于在js中变量是松散类型,很容易忘记变量所应包含的数据类型。初始化的时候,在每一行上使用单行注释
var found = false; //布尔型 var count = -1; //数字 var name = ''; //字符串 var person = null; //对象
松散耦合
只要应用的某个部分过分依赖于另一部分,代码就是耦合过紧,难于维护。典型的问题如:对象直接引用另一个对象,并且当修改其中一个的同时需要修改另外一个。
紧密耦合的软件难于维护并且需要经常重写。
1、解耦HTML/JavaScript,html和javascript过于紧密的耦合在一起
a、直接写在html中对js
<script type='text/javascript'> document.write('hello world'); </script>
<input type='button' value='click me' onclick='doSomething()'/>
在这个例子中,可能在doSomething()函数可用之前,就已经按下了按钮,引发了一个js错误。因为任何对按钮行为对更改要同时触及html和js,因此影响了可维护性。
而这些本该只在js中进行
b、js中包含了html
function inserMessage(msg){ var container = document.getElementById('container'); container.innerHTML = '<div class="msg"><p class="post">'+msg+'</p><p>latest message above</p></div>' }
避免在js中创建大量html。再一次重申保持层次分离,这样很容易的确定错误来源。当使用上面这个例子的时候,有一个页面布局的问题,可能和动态创建的html没有被
正确格式化有关。不过,要定位这个错误非常困难,因为你可能一般先看页面的源代码来查找那段烦人的html,但是却没能找到,因为它是动态生成的。对数据或者布局
的更改也会要求更改js,这表明这两个层次过于紧密耦合了。
推荐,当js用于插入数据时,尽量不要直接插入标记。一般可以在页面中直接包含并隐藏标记,然后等整个页面渲染好之后,就可以用js显示该标记
2、解耦CSS/javascript
a、js中嵌入css
element.style.color = 'red';
element.style.backgroundColor = 'blue';
由于css负责页面的显示,当显示出现任何问题时都应该只是查看css文件夹来解决。然而,当使用了js来更改某些样式的时候,比如颜色,就出现了第二个可能已更改和
必须检查的地方。结果是js也在某种程度上负责了页面的显示,并与css紧密耦合了。如果未来需要更改样式表,css和js文件可能都需要修改。这就给开发人员造成了维护
上的噩梦。
推荐,现代web应用常常要使用js来更改样式,所以虽然不可能完全将css和js解耦,但是还是能让耦合更松散的
element.className='edit';
通过只修改某个元素的css类,就可以让大部分样式信息严格保留在css中。js可以更改样式类,但并不会直接影响到元素的样式。
b、css中嵌入js(只在ie中出现)
div{ width:expression(document.body.offsetWidth - 10 + 'px'); }
好的层次划分非常重要。显示问题的唯一来源应该是css,行为问题的唯一来源应该是js。在这些层次之间保持松散耦合可以让整个应用更加易于维护。
编程实践
1、尊重对象所有权
不要为实例或原型添加属性
不要为实例或原型添加方法;
不要重定义已存在的方法;
如果某人期望叫做stopEvent()的函数能取消某个事件的默认行为,但是你对其进行了修改,然后它完成了本来的任务,后来还追加了另外的事件处理程序,那肯定会出现问题。
其他开发人员会认为函数还是按照原来的方式执行,所以他们的用法会出错并有可能造成危害,因为他们并不知道有副作用。
2、避免全局变量
var name = 'Nicholas'; function sayName(){ alert(name); }
这段代码包含了两个全局量:变量name和函数sayName()。其实可以创建一个包含两者的对象
eg:
var MyApplication = { name:'Nicholas', sayName:function(){ alert(this.name); } }
这段重写的代码引入了一个单一的全局对象MyApplication.name和sayName()都附加在其上。这样做消除了一些存在于前一段代码中的一些问题。首先,变量name覆盖了window.name
属性,可能会与其他功能产生冲突;其次,它有助消除功能作用域之间的混淆。调用MyApplication.sayName()在逻辑上暗示了代码的任何问题都可以通过MyApplication的代码来确定。
3、避免与null进行比较
function sortArray(values){ if(values != null){ //避免 values.sort(comparator) } }
该函数的目的是根据给定的比较只对一个数组进行排序。为了函数能正确执行,values参数必需是数组,但这里的if语句仅仅检查该values是否为null。还有其他的值可以通过if语句,包括
字符串,数字,他们会导致函数抛出错误。
function sortArray(values){ if(Array.isArray(values)){ //推荐 values.sort(comparator) } }
如果看到了与null比较的代码,尝试使用以下技术替换
如果值应为一个引用类型,使用instanceof操作符检查其构造函数;
如果值应为一个基本类型,使用typeof检查其类型;
如果是希望对象包含某个特定的方法名,则使用typeof操作符确保指定名字的方法存在于对象上。
4、使用常量
function validate(value){ if(!value){ alert('Invalid value!'); location.href='/errors/invalid.php'; } }
在这个函数中有两段数据:要显示给用户段信息以及url。显示在用户界面上段字符串应该以允许进行语言国际化段方式抽取出来,url也应被抽取出来,因为它们随着
应用成长而改变的倾向。
eg:
var Constans = { INVALID_VALUE_MSG:'Invalid value!', INVALID_VALUE_URL:'/errors/invalid.php' } function validate(value){ if(!value){ alert(Constans.INVALID_VALUE_MSG); location.href=Constans.INVALID_VALUE_URL; } }
这些设置允许数据在无须接触使用它的函数的情况下进行变更。
重复值 —— 任何在多处用到的值都应抽取为一个常量。这就限制了当一个值变了而另一个没变的时候造成的错误。这也包含了css类名。
用户界面字符串 —— 任何用于显示给用户的字符串,都应该抽取出来以方便国际化
URLs —— 在web应用中,资源位置很容易变更,所以推荐用一个公共地方存放所有都url.
任意可能会更改的值 —— 每当你在用到字面量的时候,你都要问一下自己这个值在未来是不是会变化。如果是,那么这个值就应该被提取出来作为一个常量