msforest / notebook

好记性不如烂笔头,记录知识的点点滴滴。
https://github.com/msforest/notebook/wiki
0 stars 0 forks source link

js编码规范 #9

Open msforest opened 7 years ago

msforest commented 7 years ago

编写可维护的代码 项目维护是由不同的人负责的,要想做到别人也能看懂你的代码,需要互相遵循一些成形的编码规范,这样才能做到降低你我他的成本,毕竟不可能一直从事一个项目,一旦转移到新的项目上,很容易会忘记之前的代码,可维护的代码意味着具有如下特性:

接下来一一细说:

1. 尽量少用全局变量

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

1.1 全局变量的问题

全局变量的问题在于它们整个JavaScript应用或web页面内共享。它们生存于同一个全局命名空间内,总有可能发生命名冲突。譬如当一个应用程序中两个独立的部分定义了同名的全局变量,但却有不同的目的时。 网页经常会包含一些非页面开发人员编写的代码,譬如:

为了与同一个页面上的其他脚本友好共存,要尽可能少地使用全局变量。要想最小化控制全局变量的数量,可以使用命名空间模式或只执行立即生效函数,最重要也是最常用的还是使用var声明变量。

JavaScript总是在不知不觉中出人意料地创建了全局变量,其原因在于JavaScript的两个。第一个特性是JavaScript可直接使用变量,无需声明。第二个特性是JavaScript有个暗示全局变量的概念,即任何变量,未经声明,就为全局对象所有。

function sum(x,y){
  result = x + y;
  return result;
}

在这个例子中,result未经声明就使用了。代码虽然在一般情况下可以正常工作,但如果在调用该函数后,在全局命名空间使用了另外的result变量,问题就会出现。

首要的规则就是用var声明变量,正如改善后的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);如果对链式赋值的所有变量都进行了声明,就不会创建出不期望的全局变量。例如:

function foo(){
  var a,b;
  a = b = 0; //此时均为局部变量
}

1.2 变量释放时的副作用

隐含全局变量与明确定义的全局变量有细微的不同,不同之处在于能否使用delete操作符撤销变量。

这表明隐含全局变量严格来讲不是真正的变量,而是全局对象的属性。属性可以通过delete操作符删除,但变量不可以。

var a = 1;
b = 2;
function foo(){
  c = 3;
}

delete a; //false
delete b; //true
delete c; //true

在es5 strict模式中,为没有声明的变量赋值会抛出错误。

2. 单一var模式(Single var Pattern)

只使用一个var在函数顶部进行变量声明是一种非常有用的模式。它的好处在于:

单一var模式如下所示:

function func(){
  var a = 1,
    b = 2,
    sum = a + b,
    myobject = {},
    i,
    j;
  //...
}

使用一个var关键字声明由逗号分割的多个变量。在声明变量的同时初始化变量,为变量赋初值也是一种好的作风。这样可以防止逻辑错误,也可提高代码的可读性。当你以后重新看这段代码时,你可以根据变量的初始值知道使用这些变量的意图。比如,它应该是一个对象还是一个整型?

在声明变量时也可能做些实质性的工作,比如上述代码中的sum = a + b,另一个例子是dom(文档对象模型)的引用。如下面代码中所示,使用单一var声明将DOM引用赋给局部变量。

function foo(){
  var el = document.getElmentById('id'),
  style = el.style;
  //...
}

2.1 提升:零散变量的问题

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);
}

3. for循环

for循环经常用于遍历数组或数组对象。通用模式如下:

for(var i = 0; i < myarray.length; i++){
  //...
}

这种模式的问题在于每次循环迭代时都要访问数据的长度,会使性能变慢,特别是当myarray不是数据,而是HTML容器对象时。 使用如下模式将长度缓存起来,会使性能得到更大的提升:

for(var i = 0, max = myarray.length; i < max; i++){
  //...
}

对于循环的最后一个改进是,用i++代替以下两种表达式:

i = i + 1;
i += 1;

4.避免使用隐式类型转换

JavaScript在使用比较语句时会执行隐式类型转换,这也是为什么执行false==0' ' == 0这类比较语句后返回true。 为了避免隐式类型转换导致的混淆不清,请在使用比较语句使用===!==操作符进行比较。

5.避免使用eval()

如果在代码中看到使用了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);

6.使用parseInt()的数值约定

通过使用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。

7. 编码约定

遵循编码约定是非常重要的,这可以使得代码更为一致、可预测、更容易阅读和理解。一个新加入团队的开发者通过了解编码约定,可以很快提高工作效率,理解团队其他人员编写的代码。

7.1 缩进

没有缩进的代码是很难阅读的。如果使用了不一致的缩进,也是很令人头疼的事情。因为这样的做法看起来遵循了约定,但是会导致很多令人困惑的疑问。标准化使用缩进is very important.

7.2 大括号

应该经常使用大括号,甚至在可选的情况下,都请使用大括号。技术上,在if语句和for语句中如果仅有一行语句,可以不需要大括号,但是为了一致性和更方便升级,最好还是使用大括号。

7.3 空格

使用空格也有助于改善代码的可读性和一致性。在列表表达式和语句结束后面添加空格。

7.4 命名约定

另一个提高代码可预测性和可维护性的方法是使用命名约定。这就意味着采用一致的方法来对变量和函数进行命名。