Open msforest opened 7 years ago
编写可维护的代码 项目维护是由不同的人负责的,要想做到别人也能看懂你的代码,需要互相遵循一些成形的编码规范,这样才能做到降低你我他的成本,毕竟不可能一直从事一个项目,一旦转移到新的项目上,很容易会忘记之前的代码,可维护的代码意味着具有如下特性:
接下来一一细说:
JavaScript使用函数管理作用域。变量在函数内声明,只在函数内有效,不能在外部使用。全局变量与之相反,在函数外部声明,在函数内部无需声明即可简单地使用。
每一个JavaScript环境都有全局对象,可在函数外部使用this进行访问。创建的每一个全局变量都为全局对象所有。在浏览器中,为了方便,使用window表示全局对象本事。
myglobal = 'hello'; console.log(myglobal);//hello console.log(window.myglobal);//hello console.log(window['myglobal']);//hello console.log(this.myglobal); //hello
全局变量的问题在于它们整个JavaScript应用或web页面内共享。它们生存于同一个全局命名空间内,总有可能发生命名冲突。譬如当一个应用程序中两个独立的部分定义了同名的全局变量,但却有不同的目的时。 网页经常会包含一些非页面开发人员编写的代码,譬如:
为了与同一个页面上的其他脚本友好共存,要尽可能少地使用全局变量。要想最小化控制全局变量的数量,可以使用命名空间模式或只执行立即生效函数,最重要也是最常用的还是使用var声明变量。
var
JavaScript总是在不知不觉中出人意料地创建了全局变量,其原因在于JavaScript的两个。第一个特性是JavaScript可直接使用变量,无需声明。第二个特性是JavaScript有个暗示全局变量的概念,即任何变量,未经声明,就为全局对象所有。
function sum(x,y){ result = x + y; return result; }
在这个例子中,result未经声明就使用了。代码虽然在一般情况下可以正常工作,但如果在调用该函数后,在全局命名空间使用了另外的result变量,问题就会出现。
首要的规则就是用var声明变量,正如改善后的sum()函数所示:
sum()
function sum(x,y){ var result = x + y; return result; }
另外一种创建隐式全局变量的反模式是带有var声明的链式赋值。在下面的代码片段中,a是局部变量,b是全局变量,这也许并不是你想要的。
function foo(){ var a = b = 0; ... }
一切都是因为从右至左的操作符优先级的原因。首先,优先级较高的表达式b=0,此时b未经声明。表达式返回值为0,它被赋给var声明的局部变量a,如var a = (b = 0);如果对链式赋值的所有变量都进行了声明,就不会创建出不期望的全局变量。例如:
b=0
var a = (b = 0);
function foo(){ var a,b; a = b = 0; //此时均为局部变量 }
隐含全局变量与明确定义的全局变量有细微的不同,不同之处在于能否使用delete操作符撤销变量。
delete
这表明隐含全局变量严格来讲不是真正的变量,而是全局对象的属性。属性可以通过delete操作符删除,但变量不可以。
var a = 1; b = 2; function foo(){ c = 3; } delete a; //false delete b; //true delete c; //true
在es5 strict模式中,为没有声明的变量赋值会抛出错误。
只使用一个var在函数顶部进行变量声明是一种非常有用的模式。它的好处在于:
单一var模式如下所示:
function func(){ var a = 1, b = 2, sum = a + b, myobject = {}, i, j; //... }
使用一个var关键字声明由逗号分割的多个变量。在声明变量的同时初始化变量,为变量赋初值也是一种好的作风。这样可以防止逻辑错误,也可提高代码的可读性。当你以后重新看这段代码时,你可以根据变量的初始值知道使用这些变量的意图。比如,它应该是一个对象还是一个整型?
在声明变量时也可能做些实质性的工作,比如上述代码中的sum = a + b,另一个例子是dom(文档对象模型)的引用。如下面代码中所示,使用单一var声明将DOM引用赋给局部变量。
sum = a + b
function foo(){ var el = document.getElmentById('id'), style = el.style; //... }
JavaScript允许在函数的任意地方声明多个变量。无论在哪里声明,效果都等同于在函数顶部声明。这就是所谓的==提升==。当先使用变量再在函数后面声明变量时可能会导致逻辑错误。对JavaScript而言,只要变量是在同一个范围里,就被视为已经声明,哪怕是在变量声明前就使用。如下例子:
myname = 'global'; function func(){ console.log(myname); //undefined var myname = 'local'; console.log(myname); //local } func();
在这个例子中,会想当然的认为第一个为'global',第二个为'local',这是一个合乎情理的期望。但事实上并不是这样,所有的变量声明都会提升到函数的顶部。因此,为了避免这类混乱,最好在开始就声明要用的所有变量。 上面的例子与下面的效果一样:
myname = 'global'; function func(){ var myname; console.log(myname); myname = 'local'; console.log(myname); }
for循环经常用于遍历数组或数组对象。通用模式如下:
for(var i = 0; i < myarray.length; i++){ //... }
这种模式的问题在于每次循环迭代时都要访问数据的长度,会使性能变慢,特别是当myarray不是数据,而是HTML容器对象时。 使用如下模式将长度缓存起来,会使性能得到更大的提升:
for(var i = 0, max = myarray.length; i < max; i++){ //... }
对于循环的最后一个改进是,用i++代替以下两种表达式:
i++
i = i + 1; i += 1;
JavaScript在使用比较语句时会执行隐式类型转换,这也是为什么执行false==0或' ' == 0这类比较语句后返回true。 为了避免隐式类型转换导致的混淆不清,请在使用比较语句使用===和!==操作符进行比较。
false==0
' ' == 0
===
!==
如果在代码中看到使用了eval(),请牢记一句俗话,“eval()是一个魔鬼”。该函数可以将任意字符串当做一个JavaScript代码来执行。当需要讨论的代码是预先就编写好了,就没有理由需要使用eval()。而如果代码是在运行时动态生成的,则也有其他更好的方法来代替eval()实现其功能。 大部分情形下,使用setInterval()、setTimeout()和function()等构造函数来传递参数,会导致类似eval()的隐患。
//反模式 setTimeout('myFunc()',1000); setTimeout('myFunc(1,2,3)',1000); //推荐模式 setTimeout(myFunc,1000); setTimeout(function(){ myFunc(1,2,3); },1000);
通过使用parseInt(),可以从一个字符串中获取数值。该函数的第二个参数是一个进制参数,通常可以忽略该参数,但是最好不要这样做,因为当解析的字符串是0开始就会出现错误。在es3版本中,0开始的字符串会被当做一个八进制,而在es5版本中发生了改变。为了避免不一致性和未预期,请每次都指定进制参数:
var month = '06', year = '09'; month = parseInt(month, 10); year = parseInt(year, 10);
在本例中,如果忽略了进制参数,使用类似parseInt(year)的方法,那么返回值将是0,因为‘09’会当做一个八进制处理,而09在八进制中不是一个合法的数值。
另外一个将字符串转换为数值的方法是:
+'08'; //8 Number('08'); // 8
这种方法通常会比parseInt()快很多,因为正如其名称一样,parseInt()是解析而不是简单的转换。但是如果希望输入‘09 hello’会返回一个数值,那么除parseInt()之外,其他方法都会返回NaN。
遵循编码约定是非常重要的,这可以使得代码更为一致、可预测、更容易阅读和理解。一个新加入团队的开发者通过了解编码约定,可以很快提高工作效率,理解团队其他人员编写的代码。
没有缩进的代码是很难阅读的。如果使用了不一致的缩进,也是很令人头疼的事情。因为这样的做法看起来遵循了约定,但是会导致很多令人困惑的疑问。标准化使用缩进is very important.
应该经常使用大括号,甚至在可选的情况下,都请使用大括号。技术上,在if语句和for语句中如果仅有一行语句,可以不需要大括号,但是为了一致性和更方便升级,最好还是使用大括号。
使用空格也有助于改善代码的可读性和一致性。在列表表达式和语句结束后面添加空格。
另一个提高代码可预测性和可维护性的方法是使用命名约定。这就意味着采用一致的方法来对变量和函数进行命名。
编写可维护的代码 项目维护是由不同的人负责的,要想做到别人也能看懂你的代码,需要互相遵循一些成形的编码规范,这样才能做到降低你我他的成本,毕竟不可能一直从事一个项目,一旦转移到新的项目上,很容易会忘记之前的代码,可维护的代码意味着具有如下特性:
接下来一一细说:
1. 尽量少用全局变量
JavaScript使用函数管理作用域。变量在函数内声明,只在函数内有效,不能在外部使用。全局变量与之相反,在函数外部声明,在函数内部无需声明即可简单地使用。
每一个JavaScript环境都有全局对象,可在函数外部使用this进行访问。创建的每一个全局变量都为全局对象所有。在浏览器中,为了方便,使用window表示全局对象本事。
1.1 全局变量的问题
全局变量的问题在于它们整个JavaScript应用或web页面内共享。它们生存于同一个全局命名空间内,总有可能发生命名冲突。譬如当一个应用程序中两个独立的部分定义了同名的全局变量,但却有不同的目的时。 网页经常会包含一些非页面开发人员编写的代码,譬如:
为了与同一个页面上的其他脚本友好共存,要尽可能少地使用全局变量。要想最小化控制全局变量的数量,可以使用命名空间模式或只执行立即生效函数,最重要也是最常用的还是使用
var
声明变量。JavaScript总是在不知不觉中出人意料地创建了全局变量,其原因在于JavaScript的两个。第一个特性是JavaScript可直接使用变量,无需声明。第二个特性是JavaScript有个暗示全局变量的概念,即任何变量,未经声明,就为全局对象所有。
在这个例子中,result未经声明就使用了。代码虽然在一般情况下可以正常工作,但如果在调用该函数后,在全局命名空间使用了另外的result变量,问题就会出现。
首要的规则就是用
var
声明变量,正如改善后的sum()
函数所示:另外一种创建隐式全局变量的反模式是带有
var
声明的链式赋值。在下面的代码片段中,a是局部变量,b是全局变量,这也许并不是你想要的。一切都是因为从右至左的操作符优先级的原因。首先,优先级较高的表达式
b=0
,此时b未经声明。表达式返回值为0,它被赋给var
声明的局部变量a,如var a = (b = 0);
如果对链式赋值的所有变量都进行了声明,就不会创建出不期望的全局变量。例如:1.2 变量释放时的副作用
隐含全局变量与明确定义的全局变量有细微的不同,不同之处在于能否使用
delete
操作符撤销变量。var
创建的全局变量不能删除var
创建的隐含全局变量可以删除这表明隐含全局变量严格来讲不是真正的变量,而是全局对象的属性。属性可以通过
delete
操作符删除,但变量不可以。在es5 strict模式中,为没有声明的变量赋值会抛出错误。
2. 单一var模式(Single var Pattern)
只使用一个
var
在函数顶部进行变量声明是一种非常有用的模式。它的好处在于:单一var模式如下所示:
使用一个
var
关键字声明由逗号分割的多个变量。在声明变量的同时初始化变量,为变量赋初值也是一种好的作风。这样可以防止逻辑错误,也可提高代码的可读性。当你以后重新看这段代码时,你可以根据变量的初始值知道使用这些变量的意图。比如,它应该是一个对象还是一个整型?在声明变量时也可能做些实质性的工作,比如上述代码中的
sum = a + b
,另一个例子是dom(文档对象模型)的引用。如下面代码中所示,使用单一var声明将DOM引用赋给局部变量。2.1 提升:零散变量的问题
JavaScript允许在函数的任意地方声明多个变量。无论在哪里声明,效果都等同于在函数顶部声明。这就是所谓的==提升==。当先使用变量再在函数后面声明变量时可能会导致逻辑错误。对JavaScript而言,只要变量是在同一个范围里,就被视为已经声明,哪怕是在变量声明前就使用。如下例子:
在这个例子中,会想当然的认为第一个为'global',第二个为'local',这是一个合乎情理的期望。但事实上并不是这样,所有的变量声明都会提升到函数的顶部。因此,为了避免这类混乱,最好在开始就声明要用的所有变量。 上面的例子与下面的效果一样:
3. for循环
for循环经常用于遍历数组或数组对象。通用模式如下:
这种模式的问题在于每次循环迭代时都要访问数据的长度,会使性能变慢,特别是当myarray不是数据,而是HTML容器对象时。 使用如下模式将长度缓存起来,会使性能得到更大的提升:
对于循环的最后一个改进是,用
i++
代替以下两种表达式:4.避免使用隐式类型转换
JavaScript在使用比较语句时会执行隐式类型转换,这也是为什么执行
false==0
或' ' == 0
这类比较语句后返回true。 为了避免隐式类型转换导致的混淆不清,请在使用比较语句使用===
和!==
操作符进行比较。5.避免使用eval()
如果在代码中看到使用了eval(),请牢记一句俗话,“eval()是一个魔鬼”。该函数可以将任意字符串当做一个JavaScript代码来执行。当需要讨论的代码是预先就编写好了,就没有理由需要使用eval()。而如果代码是在运行时动态生成的,则也有其他更好的方法来代替eval()实现其功能。 大部分情形下,使用setInterval()、setTimeout()和function()等构造函数来传递参数,会导致类似eval()的隐患。
6.使用parseInt()的数值约定
通过使用parseInt(),可以从一个字符串中获取数值。该函数的第二个参数是一个进制参数,通常可以忽略该参数,但是最好不要这样做,因为当解析的字符串是0开始就会出现错误。在es3版本中,0开始的字符串会被当做一个八进制,而在es5版本中发生了改变。为了避免不一致性和未预期,请每次都指定进制参数:
在本例中,如果忽略了进制参数,使用类似parseInt(year)的方法,那么返回值将是0,因为‘09’会当做一个八进制处理,而09在八进制中不是一个合法的数值。
另外一个将字符串转换为数值的方法是:
这种方法通常会比parseInt()快很多,因为正如其名称一样,parseInt()是解析而不是简单的转换。但是如果希望输入‘09 hello’会返回一个数值,那么除parseInt()之外,其他方法都会返回NaN。
7. 编码约定
遵循编码约定是非常重要的,这可以使得代码更为一致、可预测、更容易阅读和理解。一个新加入团队的开发者通过了解编码约定,可以很快提高工作效率,理解团队其他人员编写的代码。
7.1 缩进
没有缩进的代码是很难阅读的。如果使用了不一致的缩进,也是很令人头疼的事情。因为这样的做法看起来遵循了约定,但是会导致很多令人困惑的疑问。标准化使用缩进is very important.
7.2 大括号
应该经常使用大括号,甚至在可选的情况下,都请使用大括号。技术上,在if语句和for语句中如果仅有一行语句,可以不需要大括号,但是为了一致性和更方便升级,最好还是使用大括号。
7.3 空格
使用空格也有助于改善代码的可读性和一致性。在列表表达式和语句结束后面添加空格。
7.4 命名约定
另一个提高代码可预测性和可维护性的方法是使用命名约定。这就意味着采用一致的方法来对变量和函数进行命名。