javascript 代码规范

2018-04-06 阅读 1524 收藏 3

1.为什要遵守代码规范

软件bug的修复是昂贵的,并且随着时间的推移,这些bug的成本也会增加,尤其当这些bug潜伏并慢慢出现在已经发布的软件中时。当你发现bug 的时候就立即修复它是最好的,此时你代码要解决的问题在你脑中还是很清晰的。否则,你转移到其他任务,忘了那个特定的代码,一段时间后再去查看这些代码就 需要:

  • 花时间学习和理解这个问题
  • 花时间是了解应该解决的问题代码
  • 还有问题,特别对于大的项目或是公司,修复bug的这位伙计不是写代码的那个人(且发现bug和修复bug的不是同一个人)。因此,必须降低理解代 码花费的时间,无论是一段时间前你自己写的代码还是团队中的其他成员写的代码。这关系到底线(营业收入)和开发人员的幸福,因为我们更应该去开发新的激动 人心的事物而不是花几小时几天的时间去维护遗留代码。

另一个相关软件开发生命的事实是,读代码花费的时间要比写来得多。有时候,当你专注并深入思考某个问题的时候,你可以坐下来,一个下午写大量的代码。

你的代码很能很快就工作了,但是,随着应用的成熟,还会有很多其他的事情发生,这就要求你的进行进行审查,修改,和调整。例如:

  • bug是暴露的
  • 新功能被添加到应用程序
  • 程序在新的环境下工作(例如,市场上出现新想浏览器)
  • 代码改变用途
  • 代码得完全从头重新,或移植到另一个架构上或者甚至使用另一种语言

由于这些变化,很少人力数小时写的代码最终演变成花数周来阅读这些代码。这就是为什么创建可维护的代码对应用程序的成功至关重要。

可维护的代码意味着:

  • 可读的
  • 一致的
  • 可预测的
  • 看上去就像是同一个人写的
  • 已记录

2.编写代码需遵守的几个原则

提示: 不遵守这些原则代码也能运行起来。只是可能出现难以维护的现象。规范就像一种模式,大家按照一种模式来,那么阅读其他人的代码,成本就降低了。

编写代码注意事项:

2.1. 尽量减少声明全局变量

2.2. 定义变量是,尽量放到顶部

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

注意:在es6中,使用let 定义,可能出现’暂时性死区’, 具体想知道什么叫做’暂时性死区’ , 请查看阮一峰 ECMAScript 6 入门

2.3.for循环(for Loops)

// 次佳的循环
for (var i = 0; i < myarray.length; i++) {
// 使用myarray[i]做点什么
}
//更好的方式
for (var i = 0, max = myarray.length; i < max; i++) {
// 使用myarray[i]做点什么
}

JSLint提示您这样做,原因是++和–-促进了“过分棘手(excessive trickiness)”。//zxx:这里比较难翻译,我想本意应该是让代码变得更加的棘手 如果你直接无视它,JSLint的plusplus选项会是false(默认是default)。

还有两种变化的形式,其又有了些微改进,因为:

  • 少了一个变量(无max)

  • 向下数到0,通常更快,因为和0做比较要比和数组长度或是其他不是0的东西作比较更有效率

    //第一种变化的形式: var i, myarray = []; for (i = myarray.length; i–-;) { // 使用myarray[i]做点什么 }

    //第二种使用while循环:

    var myarray = [], i = myarray.length; while (i–-) { // 使用myarray[i]做点什么 }

面两种情况优于前面两种情况。

2.4.for-in循环(for-in Loops)

for-in循环应该用在非数组对象的遍历上,使用for-in进行循环也被称为“枚举”。

从技术上将,你可以使用for-in循环数组(因为JavaScript中数组也是对象),但这是不推荐的。因为如果数组对象已被自定义的功能增强,就可能发生逻辑错误。另外,在for-in中,属性列表的顺序(序列)是不能保证的。所以最好数组使用正常的for循环,对象使用for-in循环。

有个很重要的hasOwnProperty()方法,当遍历对象属性的时候可以过滤掉从原型链上下来的属性

// 对象
var man = {
    hands: 2,
    legs: 2,
    heads: 1
};

// 在代码的某个地方
// 一个方法添加给了所有对象
if (typeof Object.prototype.clone === "undefined") {
    Object.prototype.clone = function () {};
}
====================================================================
// for-in 循环
for (var i in man) {
if (man.hasOwnProperty(i)) { // 过滤
    console.log(i, ":", man[i]);
}
}
/* 控制台显示结果
hands : 2
legs : 2
heads : 1
*/
==========================================================================
// 反面例子:
// for-in loop without checking hasOwnProperty()
for (var i in man) {
console.log(i, ":", man[i]);
}
/*
控制台显示结果
hands : 2
legs : 2
heads : 1
clone: function()
*/

2.5.(不)扩展内置原型((Not) Augmenting Built-in Prototypes)

增加内置的构造函数原型(如Object(), Array(), 或Function())挺诱人的,但是这严重降低了可维护性,因为它让你的代码变得难以预测。使用你代码的其他开发人员很可能更期望使用内置的 JavaScript方法来持续不断地工作,而不是你另加的方法。

因此,不增加内置原型是最好的。你可以指定一个规则,仅当下面的条件均满足时例外:

  • 可以预期将来的ECMAScript版本或是JavaScript实现将一直将此功能当作内置方法来实现。例如,- 你可以添加ECMAScript 5中描述的方法,一直到各个浏览器都迎头赶上。这种情况下,你只是提前定义了有用的方法。

  • 如果您检查您的自定义属性或方法已不存在——也许已经在代码的其他地方实现或已经是你支持的浏览器JavaScript引擎部分。

  • 你清楚地文档记录并和团队交流了变化。

       if (typeof Object.protoype.myMethod !== "function") {
           Object.protoype.myMethod = function () {
               // 实现...
           };
       }
    

一般情况下,强烈不建议使用

2.6.避免隐式类型转换(Avoiding Implied Typecasting )

JavaScript的变量在比较的时候会隐式类型转换。这就是为什么一些诸如:false == 0 或 “” == 0 返回的结果是true。为避免引起混乱的隐含类型转换,在你比较值和表达式类型的时候始终使用===和!==操作符。

var zero = 0;
if (zero === false) {
// 不执行,因为zero为0, 而不是false
}

// 反面示例
if (zero == false) {
// 执行了...
}

2.7. 避免(Avoiding) eval()

如果你现在的代码中使用了eval(),记住该咒语“eval()是魔鬼”。此方法接受任意的字符串,并当作JavaScript代码来处理。当有 问题的代码是事先知道的(不是运行时确定的),没有理由使用eval()。如果代码是在运行时动态生成,有一个更好的方式不使用eval而达到同样的目 标。例如,用方括号表示法来访问动态属性会更好更简单:

// 反面示例
var property = "name";
alert(eval("obj." + property));

// 更好的
var property = "name";
alert(obj[property]);

3.编码规范

3.1 缩进(Indentation)

代码没有缩进基本上就不能读了。唯一糟糕的事情就是不一致的缩进,因为它看上去像是遵循了规范,但是可能一路上伴随着混乱和惊奇。重要的是规范地使用缩进。

一些开发人员更喜欢用tab制表符缩进,因为任何人都可以调整他们的编辑器以自己喜欢的空格数来显示Tab。有些人喜欢空格——通常四个,这都无所谓,只要团队每个人都遵循同一个规范就好了。这本书,例如,使用四个空格缩进,这也是JSLint中默认的缩进。

什么应该缩进呢?规则很简单——花括号里面的东西。这就意味着函数体,循环 (do, while, for, for-in),if,switch,以及对象字面量中的对象属性。下面的代码就是使用缩进的示例:

function outer(a, b) {
    var c = 1,
        d = 2,
        inner;
    if (a > b) {
        inner = function () {
            return {
                r: c - d
            };
        };
    } else {
        inner = function () {
            return {
                r: c + d
            };
        };
    }
    return inner;
}

3.2 花括号{}(Curly Braces)

// 糟糕的实例
for (var i = 0; i < 10; i += 1)
alert(i);
// 好的实例
for (var i = 0; i < 10; i += 1) {
alert(i);
}

3.3 左花括号的位置(Opening Brace Location)

这个实例中,仁者见仁智者见智,但也有个案,括号位置不同会有不同的行为表现。这是因为分号插入机制(semicolon insertion mechanism)——JavaScript是不挑剔的,当你选择不使用分号结束一行代码时JavaScript会自己帮你补上。这种行为可能会导致麻 烦,如当你返回对象字面量,而左括号却在下一行的时候

// 警告: 意外的返回值
function func() {
    return
    // 下面代码不执行
    {
        name : "Batman"
    }
}

// 警告: 意外的返回值
function func() {
    return undefined;
    // 下面代码不执行
    {
        name : "Batman"
    }
}

3.4 空格(White Space)

空格的使用同样有助于改善代码的可读性和一致性。在写英文句子的时候,在逗号和句号后面会使用间隔。在JavaScript中,你可以按照同样的逻辑在列表模样表达式(相当于逗号)和结束语句(相对于完成了“想法”)后面添加间隔。

适合使用空格的地方包括:

  • for循环分号分开后的的部分:如for (var i = 0; i < 10; i += 1) {…}
  • for循环中初始化的多变量(i和max):for (var i = 0, max = 10; i < max; i += 1) {…}
  • 分隔数组项的逗号的后面:var a = [1, 2, 3];
  • 对象属性逗号的后面以及分隔属性名和属性值的冒号的后面:var o = {a: 1, b: 2};
  • 限定函数参数:myFunc(a, b, c)
  • 函数声明的花括号的前面:function myFunc() {}
  • 匿名函数表达式function的后面:var myFunc = function () {};

使用空格分开所有的操作符和操作对象是另一个不错的使用,这意味着在+, -, *, =, <, >, <=, >=, ===, !==, &&, ||, +=等前后都需要空格。

// 宽松一致的间距
// 使代码更易读
// 使得更加“透气”
var d = 0,
    a = b + 1;
if (a && b && c) {
    d = a % c;
    a += d;
}

// 反面例子
// 缺失或间距不一
// 使代码变得疑惑
var d = 0,
    a = b + 1;
if (a&&b&&c) {
    d=a % c;
    a+= d;
}

最后需要注意的一个空格——花括号间距。最好使用空格:

  • 函数、if-else语句、循环、对象字面量的左花括号的前面({)

  • else或while之间的右花括号(})

    //{} 空格 if (4) { console.log(1) } else if (3) { console.log(1) } var a = {}

4.命名规范

  • 另一种方法让你的代码更具可预测性和可维护性是采用命名规范。这就意味着你需要用同一种形式给你的变量和函数命名。
  • 下面是建议的一些命名规范,你可以原样采用,也可以根据自己的喜好作调整。同样,遵循规范要比规范是什么更重要。

4.1以大写字母写构造函数(Capitalizing Constructors)

JavaScript并没有类,但有new调用的构造函数:

var adam = new Person();  

因为构造函数仍仅仅是函数,仅看函数名就可以帮助告诉你这应该是一个构造函数还是一个正常的函数。 命名构造函数时首字母大写具有暗示作用,使用小写命名的函数和方法不应该使用new调用:

function MyConstructor() {...}
function myFunction() {...}、

4.2 分隔单词(Separating Words)

当你的变量或是函数名有多个单词的时候,最好单词的分离遵循统一的规范,有一个常见的做法被称作“驼峰(Camel)命名法”,就是单词小写,每个单词的首字母大写。

  • 对于构造函数,可以使用大驼峰式命名法(upper camel case),如MyConstructor()。
  • 对于函数和方法名称,你可以使用小驼峰式命名法(lower camel case),像是myFunction(), calculateArea()和getFirstName()。

4.3 注释(Writing Comments)

你必须注释你的代码,即使不会有其他人向你一样接触它。通常,当你深入研究一个问题,你会很清楚的知道这个代码是干嘛用的,但是,当你一周之后再回来看的时候,想必也要耗掉不少脑细胞去搞明白到底怎么工作的。

很显然,注释不能走极端:每个单独变量或是单独一行。但是,你通常应该记录所有的函数,它们的参数和返回值,或是任何不寻常的技术和方法。要想到注 释可以给你代码未来的阅读者以诸多提示;阅读者需要的是(不要读太多的东西)仅注释和函数属性名来理解你的代码。例如,当你有五六行程序执行特定的任务, 如果你提供了一行代码目的以及为什么在这里的描述的话,阅读者就可以直接跳过这段细节。没有硬性规定注释代码比,代码的某些部分(如正则表达式)可能注释 要比代码多。

5.css代码规范

css规范我们伟大的张旭鑫老师,讲的很清楚。面向属性的命名

这是比较好的命名规范。简介来说,就是我们先定义好一些常用基础类样式。组件则使用less,或者sass进行组装,形成即可。

回到顶部