修改记录
Version.No | 发版日期 | 编制人 | 批准人 | 修改说明 |
1.0.0 | 2018/11/7 |
|
| 初次编订 |
1.0.1 | 2018/11/13 |
|
|
|
目 录
JavaScript是一种语法灵活,简单易懂的脚本语言。正因为灵活,因此很多人在编写代码时,显得很随意,这就导致后期的修改、拓展和维护变得异常困难。遵循统一的编码规范,不仅对C++和Java这种编译型语言很重要,对JavaScript脚本语言也同样如此。编写此规范的目的是为大家编写清晰、易读、高效的代码提供良好的建议,同时也作为公司代码审阅的参考标准。此编码规范融合了多位程序员的编程经验,适用于开发和维护期间,发布代码时可以用工具进行优化处理,剔除注释和空格字符,以提高执行效率。
1命名规范
1.1文件命名
- 字母全部小写
- 不要带空格
- 用破折号(-)连接单词
- 库文件可用逗点(.),用于体现版本或从属关系
正例:
- vue.min.js
- vue-router.js
- jquery.form.js
- jquery-1.4.2.min.js
1.2变量命名
- 使用骆驼命名法(Camel):标识符的第一个单词首字母小写,后面的单词首字母大写
- 必须以字母或美元符号($)开头,不允许包含空格和其他标点符号,不使用下划线(_)
- 禁止使用JavaScript关键词、保留字全名
- 长度应该尽可能的短,并抓住要点,尽量体现出值的类型
- 尽量避免使用没有意义的命名
正例:
- var name; // 声明了一个变量name
- var name = 'tony'; // name表示名字,值为'tony',数据类型为字符串
- var age = 27; // age表示年龄,值为27,数据类型为number
- var girlFriend = false; // girlFriend表示女盆友,值为否,数据类型为布尔值
- var fn = function(){}; // fn表示一个方法,值为一个函数,数据类型为对象;
1.3常量命名
- 名词全部大写
- 使用大写字母和下划线(_)来组合命名,下划线(_)用来分割单词
- 禁止使用JavaScript关键词、保留字全名
- 长度应该尽可能的短,并抓住要点,尽量体现出值的类型
- 尽量避免使用没有意义的命名
正例:
- const MAX_COUNT = 10;
- const URL = '//www.huifenqi.com';
1.4函数(方法)命名
- 使用骆驼命名法(Camel):标识符的第一个单词首字母小写,后面的单词首字母大写
- 前缀应该为动词
- 禁止使用JavaScript关键词、保留字全名
- 长度应该尽可能的短,并抓住要点,尽量体现出值的类型
- 尽量避免使用没有意义的命名
常用动词约定:
动词 | 含义 |
can | 判断是否可执行某个动作 |
has | 判断是否含有某个值 |
is | 判断是否为某个值 |
get | 获取某个值 |
set | 设置某个值 |
load | 加载某些数据 |
正例:
- // 是否可阅读
function canRead() {
}
- // 获取名称
function getName() {
}
1.5类 & 构造函数命名
- 帕斯卡命名法(Pascal):首字母和后面连接的每个单词的首字母都大写
- 前缀为名称
- 禁止使用JavaScript关键词、保留字全名
- 长度应该尽可能的短,并抓住要点,尽量体现出值的类型
- 尽量避免使用没有意义的命名
正例:
class Person {
constructor(name) {
...
}
}
let person = new Person('啦啦啦');
1.6类的成员命名
- 公共属性和方法: 跟变量和函数命名一样。
- 私有属性和方法:前缀为下划线(_), 后面跟公共属性和方法命名一样。
正例:
class Person {
// 私有属性
_name: string;
constructor() { }
// 公共方法
getName() {
return this._name;
}
// 公共方法
setName(name) {
this._name = name;
}
}
1.7缩写
为了避免混淆和保证跨语言交互操作,请遵循下列规则:
- 【建议】不要将缩写或缩略形式用作名称的组成部分。
例如:使用getWindow(),而不是使用getWin()。
- 【建议】不要使用计算机领域中未被普遍接受的缩写。
- 【建议】在适当的时候,使用众所周知的缩写替代冗长的词组名称。
例如:用UI作User Interface的缩写,用Olap作On-line Analytical Processing的缩写
- 【建议】在使用缩写时,大写仅有两个字符的缩写,对于超过两个字符长度的缩写根据规范使用帕斯卡命名法(Pascal)或者骆驼命名法(Camel)。
例如:使用System.IO,而不是System.Io;使用HtmlButton,而不是HTMLButton。
2代码风格
2.1代码排版
2.1.1列宽
代码列宽控制在每行120个字符左右。超出需要换行,换行时遵循如下原则:
- 第二行相对第一行缩进4个空格,从第三行开始,不再继续缩进
- 运算符与上下文一起换行
- 方法调用的点符号与上下文一起换行
- 方法调用中的多个参数需要换行时,在逗号后进行
- 在括号前不要换行
正例:
var html = '<input οnchange="dateChange();" '
+ 'οnfοcus="WdatePicker({readOnly:true})" '
+ 'class="Wdate" type="text" id="end-time" '
+ 'style="width: 150px;height: 34px;">';
反例:
var html = '<input οnchange="dateChange();" ' +
'οnfοcus="WdatePicker({readOnly:true})" ' +
'class="Wdate" type="text" id="end-time" ' +
'style="width: 150px;height: 34px;">';
2.1.2缩进
采用4个空格缩进,禁止使用tab字符
说明:如果使用tab缩进,必须设置1个tab为4个空格。IDEA设置tab为4个空格时,请勿勾选User tab character;而在eclipse中,必须勾选insert spaces for tabs(因为不同的编辑器可能对tab的渲染不同,有的编辑器将tab显示为8个空格宽度,而有的编辑器则显示为4个空格宽度,这样就会造成代码格式的不统一)。
2.1.3空行
不同逻辑、不同语义、不同业务的代码之间插入一个空行分割开来以提高可读性。
- 函数与函数之间
- 函数中不同的逻辑块之间
- 注释与它注释的语句间不空行,但与其他的语句间可空一行
说明:任何情形,没必要插入多个空行进行分隔开。
2.1.4空格
以下情况中要使用到空格:
- 多个参数用逗号隔开,每个逗号后都加一个空格
- 任何二目、三目运算符的左右两边都需要加一个空格
说明:运算符包括赋值运算符=、逻辑运算符&&、加减乘除符号等
- 单目运符与变量之间不加空格
- if/for/while/switch/do等保留字与括号之间都必须加空格
- 左小括号和字符之间不出现空格;同样,右小括号和字符之间也不出现空格;而左大括号前需要空格
- 关键字与左括号之间用空格隔开
正例:
- while (true)
- i++;
- --i;
- for (expr1; expr2; expr3)
- var sBackColor = '#' + 'FF' + '00' + 'FF';
反例:
- if(空格a == b空格)
- while(true)
- for(空格expr1; expr2; expr3空格)
2.1.5引号
统一使用单引号('),而不使用双引号("),这在创建HTML字符串时非常有用。
正例:
- var msg = 'This is some HTML<div class="makes-sense"></div>';
反例:
- var msg = "This is some HTML<div class=\"makes-sense\"></div>";
2.1.6小括号-()
- 【建议】使用小括号划分表达式的段落,使之更容易理解
例如: // 表示RGB是一个整体,颜色值是'#'加RGB的字符串
var sBackColor = '#' + ('FF' + '00' + 'FF');
- 【建议】使用小括号包含逻辑表达式
例如: var bBlack = (sBlackColer == '#000000');
2.1.7大括号-{}
- 如果大括号内为空,则简洁的写成{}即可,不需要换行;如果是非空代码块则:
- 左大括号前不换行
- 右大括号后换行
- 右大括号前换行
- 右大括号后还有else等代码则不换行,表示终止的右大括号必须换行
- 右大括号后建议加一个注释以便于方便的找到与之对应的左大括号
例如: while (1) {
if (valid) {
}// if valid
else {
}// not valid
}// end forever
- 大括号中没有或只有一条语句时,也不能省略大括号
例如: if (name === undefined) { // 正确
return;
}
if (name === undefined) // 错误
return;
- 使用大括号进行段落划分能够指定方法内的变量的生命区域,从而使得代码更少出现一些因为不小心或者拼写错误导致的bug
例如: // 段落1
{
var i;
}
// 段落2
{
var i, j;
}
2.2代码注释
一些注释工具可以帮助你写出更好的注释。JSDoc或YUIDoc就是用来写 JavaScript注释用的。你甚至可以使用工具来为这些注释生成文档,这也是激励开发者们写注释的一个好方法,因为一旦有了这样方便的生成文档的工具,他们通常会开始花更多时间在注释细节上。
2.2.1文件的注释
文件注释位于文件的最前面,每个工程文件的文件注释应包括以下信息:
- 描述(概要说明)
- 版本(必须)(格式为“v 主版本号.次版本号.修订版本号”,每个工程文件的更新记录的版本号信息必须与工程保持一致)
- 版权声明(必须)(这个版本中增删改了哪些内容)
- 项目地址(开源组件必须)
- 开源协议(开源组件必须)
- 作者
- 修改时间
正例:
/*!
* 描述:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
* 版本:v x.x.x
* 版本声明:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
* 1.xxxxxxxxxxxxxxxxxxxxxxxx
* 2.xxxxxxxxxxxxxxxxxxxxxxxx
* 3.xxxxxxxxxxxxxxxxxxxxxxxx
* 项目地址:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
* 开源协议:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
* 作者:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
* 修改时间:xxxx-xx-xx
*/
2.2.2变量的注释
- 用单行注释(//)
- 放置在变量声明的右方,用tab隔开
- 相邻的多个变量注释用tab对其,代码会更美观
正例:
var sBackColor = '#' + ('FF' + '00' + 'FF'); // 表示RGB是一个整体,颜色值是"#"加RGB的字符串
var bBlack = (sBlackColer == '#000000'); // 判断颜色是不是黑色
2.2.3函数的注释
- 用文档注释(/** ... */)
- 放置在函数声明上方
- @param标签指定参数的名称。还可以包含参数的数据类型,使用大括号括起来,和参数的描述(非必须要项)
- @callback标签来定义回调类型(非必须要项)
- @return/@returns标签定义返回值的类型和描述(非必须要项)
- @author标签定义作者
正例:
/**
* xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
* @author xxx
* @callback xxxxx
* @param {?*} xxx - xxxxx.
* @param {!string} xxx - xxxxx.
* @param {!function} xxx - xxxxxx.
* @return {!number} xxxxxx.
*/
2.2.4语句的注释
- 用单行注释或多行注释(//、/* */)
- 语句的注释放在被注释语句的上方
- 在有较多“{}”嵌套的语句时,可以在“}”后面加上语句结束的注释,方便阅读和插入语句
正例:
- // 改变背景颜色为白色
sBackColor = '#FFFFFF';
- } //End-for
} //End-if
} //End-while
2.2.5段落的注释
- 用单行注释或多行注释(//、/* */)
- 段落的注释放置在被注释的语句段的上方,在注释上下各空一行
正例:
空行
//段落注释,上下各空一行
空行
//语句注释
sBackColor = '#FFFFFF';
2.3协议
不要指定引入资源所带的具体协议。当获取图片、其它媒体文件或者数据时,URL所指向的具体路径,不要指定协议部分(http:、https:),除非这两个协议都不可用(不指定协议使得URL从绝对的获取路径转变为相对的,在请求资源协议无法确定时非常好用,而且还能为文件大小节省几个字节)。
正例:
function changeData() {
$('#flex').bootstrapTable('refresh', {
url : '//deviceAccess/vmsDiary/getAllVmsDiaries.do'
});
}
3语言特性
3.1简单语句
- 每行最多包含一个分号(;),放到每条简单语句的结尾处
- 【注意】函数赋值或对象赋值语句也是赋值语句,应该以分号结尾。JavaScript可以把任何表达式当作一条语句。这很容易隐藏一些错误,特别是误加分号的错误。只有在赋值和调用时,表达式才应被当作一条单独的语句
正例:
a++;
b++;
反例:
a++; b++;
3.2复句语句
符合语句是包含在大括号中的语句序列。
- 被括起来的语句必须缩进四个空格
- 左括号({)应在复合语句起始行的结尾处
- 右括号(})应与左括号({)的那一行的开头对齐
- 大括号应该在所有复合语句中使用,即使只有一条语句,当它是控制结构的一部分时,比如一个if或者for语句。这样可以避免以后添加语句时造成的错误
3.3三元条件判断
用三元操作符分配或返回语句。在比较简单的情况下使用,避免在复杂的情况下使用。
正例:
return x === 10 ? 'valid' : 'invalid';
反例:
if (x === 10) {
return 'valid';
} else {
return 'invalid';
}
3.4 eval函数
eval()不但混淆语境还很危险,总会有比这更好、更清晰、更安全的另一种方案来写你的代码,因此尽量不要使用eval函数。
3.5 this关键字
只在对象构造器、方法和设定的闭包中使用this关键字。
this的语义很容易被搞错。它时而指向全局对象(大多数时),时而指向调用者的定义域(在eval中),时而指向DOM树中的某个节点(当用事件处理绑定到HTML属性上时),时而指向一个新创建的对象(在构造器中),还时而指向其它的一些对象(如果函数被call()和apply()执行和调用时)。正因为它是如此容易地被搞错,请限制它的使用场景:
- 在构造函数中
- 在对象的方法中(包括由此创建出的闭包内)
3.6 with关键字
【建议】尽量不要使用with。
使用with可能会增加代码的复杂度,不利于阅读和管理;也会对性能有影响。大多数使用with的场景都能使用其他方式较好的替代。所以,尽量不要使用with。
3.7 delete关键字
- 【建议】减少delete的使用。
如果没有特别的需求,减少或避免使用delete。delete的使用会破坏部分JavaScript引擎的性能优化。
- 【建议】处理delete可能产生的异常。
对于有被遍历需求,且值null被认为具有业务逻辑意义的值的对象,移除某个属性必须使用delete操作。
在严格模式或IE下使用delete时,不能被删除的属性会抛出异常,因此在不确定属性是否可以删除的情况下,建议添加try-catch块。
例如: try {
delete o.x;
} catch (deleteError) {
o.x = null;
}
3.8动态执行代码
【建议】使用new Function执行动态代码。
通过new Function生成的函数作用域是全局使用域,不会影响当前的本地作用域。如果有动态代码执行的需求,建议使用new Function。
例如:
var handler = new Function('x', 'y', 'return x + y;');
var result = handler($('#x').val(), $('#y').val());
3.9定义域与作用域
3.9.1全局命名空间污染
应该总是将代码包裹成一个IIFE(Immediately-Invoked-Function Expression),用以创建独立隔绝的定义域。这一举措可以防止全局命名空间被污染。
IIFE还可以确保你的代码不轻易被其他全局命名空间里的代码所修改(第三方库,windows引用,被覆盖的未定义的关键字等等)。
正例:
(function(log, w, undefined) {
var x = 10, y = 100;
log((w.x === undefined) + ' ' + (w.y === undefined));
}(window.console.log, window));
反例:
var x = 10, y = 100;
console.log((window.x === undefined) + ' ' + (window.y === undefined));
3.9.2 IIFE(立即执行的函数表达式)
- 无论何时,想要创建一个新的封闭的定义域,那就用IIFE。它不仅避免了干扰,也使得内存在执行完成后立即释放
- 所有脚本文件建议都从IIFE开始
- 立即执行函数表达式的执行括号应该写在外包括号内。虽然写在内还是写在外都是有效的,但是写在内使得整个表达式看起来更像一个整体,所以推荐这么做
正例:
(function() {}());
反例:
(function() {})();
- 如果你想引用全局变量或者是外层IIFE的变量,可以通过一下方式传
参
正例:
(function($, w, d) {
$(function() {
w.alert(d.querySelectorAll('div').length);
});
}(jQuery, window, document));
3.10闭包
闭包是阻止垃圾回收器将变量从内存中移除的方法,使得在创建变量的执行环境的外面能够访问到该变量。
JavaScript是实现了自动释放内存的系统,即垃圾回收器,简称gc(garbage collector),当函数执行完毕的时候,gc会将函数中创建的东西从内存中清除。
但是如果函数执行完毕后,依旧有函数可以访问到这个变量,那么这个变量就不会被释放,这就是闭包。
正例:
var person = (function() {
var age = 23;
return {
getAge : function() {// getAge函数保存到person对象上,一个闭包就创建了
return age;
}
}
}());
// 闭包因保存函数而被创建,在执行环境的外面,可以动态访问age变量,这就阻止gc将age变量从内存中移除
console.log(person.getAge());
闭包通常的使用场景和this对象有关,比如下面这个场景。
正例:
var name = 'Here is Global';
var local = {
name : 'Here id Local',
getNameFunction : function() {
var that = this;
return function() {
console.log(that.name);
console.log(this.name);
}
}
};
local.getNameFunction()();
3.11异常
基本上你无法避免出现异常,特别是在做大型开发时(使用应用开发框架等等)。
在没有自定义异常的情况下,从有返回值的函数中返回错误信息一定非常的棘手,更别提多不优雅了。不好的解决方案包括了传第一个引用类型来接纳错误信息,或者总是返回一个对象列表,其中包含着可能的错误对象。以上方式基本上是比较简陋的异常处理方式。适时可做自定义异常处理。
在复杂的环境中,可以考虑抛出对象而不仅仅是字符串(默认的抛出值)。
正例:
if (name === undefined) {
throw {
name : 'System Error',
message : 'A name should always be specified!'
}
4代码检查
对于比较宽松自由的编程语言来说,严格遵循编码规范就显得极为重要。遵循规范固然很好,但是有自动化流程来确保其执行情况,岂不更佳。
对于 JavaScript,建议使用JSLint或JSHint。
5参考文献
[1] 《阿里巴巴Java开发手册 v 1.4.0》,2018-05-20
[2] 《彦集JavaScript编码规范 v1.0》,2016-06-02
[3] 《金蝶JavaScript开发手册 v1.0》,2003-04-22
[4] 天涯孤雁. 前端编码规范(1)—— 一般规范[EB/OL]
https://www.css88.com/archives/5361,2015-01-30
[5] 天涯孤雁. 前端编码风格规范(3)—— JavaScript规范[EB/OL] https://www.css88.com/archives/5366,2015-10-30
[6] juedi. JavaScript编码规范[EB/OL]
https://www.cnblogs.com/xianglongsdu/p/5904826.html,2016-09-25
下载地址: