总体规范
switch 下的 case 和 default 必须增加一个缩进层级
-
二元运算符两侧必须有一个空格,一元运算符与操作对象之间不允许有空格
var a = !arr.length; a++; a = b + c;
用作代码块起始的左花括号
{
前必须有一个空格if / else / for / while / function / switch / do / try / catch / finally
关键字后,必须有一个空格-
在对象创建时,属性中的
:
之后必须有空格,:
之前不允许有空格var obj = { a: 1, b: 2, c: 3 }; // bad var obj = { a : 1, b:2, c :3 };
函数声明、具名函数表达式、函数调用中,函数名和
(
之间不允许有空格,
和;
前不允许有空格-
在函数调用、函数声明、括号表达式、属性访问、if / for / while / switch / catch 等语句中,() 和 [] 内紧贴括号部分不允许有空格
// good callFunc(param1, param2, param3); save(this.list[this.indexes[i]]); needIncream && (variable += increament); if (num > list.length) { } while (len--) { } // bad callFunc( param1, param2, param3 ); save( this.list[ this.indexes[ i ] ] ); needIncreament && ( variable += increament ); if ( num > list.length ) { } while ( len-- ) { }
单行声明的数组与对象,如果包含元素,
{}
和[]
内紧贴括号部分不允许包含空格行尾不得有多余的空格
-
运算符处换行时,运算符必须在新行的行首
// good if (user.isAuthenticated() && user.isInRole('admin') && user.hasAuthority('add-admin') || user.hasAuthority('delete-admin') ) { // Code } var result = number1 + number2 + number3 + number4 + number5; // bad if (user.isAuthenticated() && user.isInRole('admin') && user.hasAuthority('add-admin') || user.hasAuthority('delete-admin')) { // Code } var result = number1 + number2 + number3 + number4 + number5;
-
在函数声明、函数表达式、函数调用、对象创建、数组创建、for语句等场景中,不允许在
,
或;
前换行// good var obj = { a: 1, b: 2, c: 3 }; foo( aVeryVeryLongArgument, anotherVeryLongArgument, callback ); // bad var obj = { a: 1 , b: 2 , c: 3 }; foo( aVeryVeryLongArgument , anotherVeryLongArgument , callback );
不同行为或逻辑的语句集,使用空行隔开,更易阅读
较复杂的逻辑条件组合,将每个条件独立一行,逻辑运算符放置在行首进行分隔,或将部分逻辑按逻辑组合进行分隔。
-
建议最终将右括号
)
与左大括号{
放在独立一行,保证与 if 内语句块能容易视觉辨识。if (user.isAuthenticated() && user.isInRole('admin') && user.hasAuthority('add-admin') || user.hasAuthority('delete-admin') ) { // Code }
按一定长度截断字符串,并使用 + 运算符进行连接。
分隔字符串尽量按语义进行,如不要在一个完整的名词中间断开。
-
特别的,对于HTML片段的拼接,通过缩进,保持和HTML相同的结构。
也可使用数组来进行拼接,相对 + 更容易调整缩进。var html = [ '<article>', '<h1>Title here</h1>', '<p>This is a paragraph</p>', '<footer>Complete</footer>', '</article>' ]; html = html.join('');
当参数过多时,将每个参数独立写在一行上,并将结束的右括号
)
独立一行。-
所有参数必须增加一个缩进。
foo( aVeryVeryLongArgument, anotherVeryLongArgument, callback );
也可以按逻辑对参数进行组合。
最经典的是baidu.format
函数,调用时将参数分为“模板”和“数据”两块
baidu.format(
dateFormatTemplate,
year, month, date, hour, minute, second
);
当函数调用时,如果有一个或以上参数跨越多行,应当每一个参数独立一行。
这通常出现在匿名函数或者对象初始化等作为参数时,如setTimeout
函数等。
setTimeout(
function () {
alert('hello');
},
200
);
order.data.read(
'id=' + me.model.id,
function (data) {
me.attchToModel(data.result);
callback();
},
300
);
-
链式调用较长时采用缩进进行调整。
$('#items') .find('.selected') .highlight() .end();
-
三元运算符由3部分组成,因此其换行应当根据每个部分的长度不同,形成不同的情况。
var result = thisIsAVeryVeryLongCondition ? resultA : resultB; var result = condition ? thisIsAVeryVeryLongResult : resultB;
-
数组和对象初始化的混用,严格按照每个对象的 { 和结束 } 在独立一行的风格书写。
var array = [ { // ... }, { // ... } ];
对于 if...else...
、try...catch...finally
等语句,推荐使用在}
号后添加一个换行 的风格,使代码层次结构更清晰,阅读性更好。
不得省略语句结束的分号
在 if / else / for / do / while 语句中,即使只有一行,也不得省略块{...}
函数定义结束不允许添加分号
IIFE 必须在函数表达式外添加 (,非 IIFE 不得在函数表达式外添加 (
额外的 ( 能够让代码在阅读的一开始就能判断函数是否立即被调用,进而明白接下来代码的用途。而不是一直拖到底部才恍然大悟。
命名
变量 使用 Camel命名法
常量 使用 全部字母大写,单词间下划线分隔 的命名方式
-
类 使用 Pascal命名法
function TextNode(options) { }
-
类的 方法 / 属性 使用 Camel命名法
function TextNode(value, engine) { this.value = value; this.engine = engine; } TextNode.prototype.clone = function () { return this; }
枚举变量 使用 Pascal命名法,枚举的属性 使用 全部字母大写,单词间下划线分隔 的命名方式
由多个单词组成的缩写词,在命名中,根据当前命名法和出现的位置,所有字母的大小写与首字母的大小写保持一致。
-
由多个单词组成的缩写词,在命名中,根据当前命名法和出现的位置,所有字母的大小写与首字母的大小写保持一致
function XMLParser() { } function insertHTML(element, html) { } var httpRequest = new HTTPRequest();
类名 使用 名词
函数名 使用 动宾短语
boolean 类型的变量使用 is 或 has 开头
-
Promise对象 用 动宾短语的进行时 表达
var loadingData = ajax.get('url'); loadingData.then(callback);
注释
单行注释
必须独占一行。// 后跟一个空格,缩进与下一行被注释说明的代码一致。多行注释
避免使用 /.../ 这样的多行注释。有多行注释内容时,使用多个单行注释-
文档化注释
为了便于代码阅读和自文档化,以下内容必须包含以 /*.../ 形式的块注释中文档注释前必须空一行
自文档化的文档说明 what,而不是 how 文件顶部必须包含文件注释,用 @file 标识文件说明
文件注释中可以用 @author 标识开发者信息命名空间使用 @namespace 标识。
示例:
/**
* @namespace
*/
var util = {};
类和构造函数
- 使用 @class 标记类或构造函数。
解释:
对于使用对象 constructor 属性来定义的构造函数,可以使用 @constructor 来标记。
示例:
/**
* 描述
*
* @class
*/
function Developer() {
// constructor body
}
- 使用 @extends 标记类的继承信息。
示例:
/**
* 描述
*
* @class
* @extends Developer
*/
function Fronteer() {
Developer.call(this);
// constructor body
}
util.inherits(Fronteer, Developer);
- 使用包装方式扩展类成员时, 必须通过 @lends 进行重新指向.没有 @lends 标记将无法为该类生成包含扩展类成员的文档。
示例:
/**
* 类描述
*
* @class
* @extends Developer
*/
function Fronteer() {
Developer.call(this);
// constructor body
}
util.extend(
Fronteer.prototype,
/** @lends Fronteer.prototype */{
_getLevel: function () {
// TODO
}
}
);
- 类的属性或方法等成员信息使用 @public / @protected / @private 中的任意一个,指明可访问性。
解释:
生成的文档中将有可访问性的标记,避免用户直接使用非 public 的属性或方法。
示例:
/**
* 类描述
*
* @class
* @extends Developer
*/
var Fronteer = function () {
Developer.call(this);
/**
* 属性描述
*
* @type {string}
* @private
*/
this._level = 'T12';
// constructor body
};
util.inherits(Fronteer, Developer);
/**
* 方法描述
*
* @private
* @return {string} 返回值描述
*/
Fronteer.prototype._getLevel = function () {
};
函数/方法注释
函数/方法注释必须包含函数说明,有参数和返回值时必须使用注释标识
参数和返回值注释必须包含类型信息和说明
// null 或 undefined
// good
if (noValue == null) {
// ......
}
// bad
if (noValue === null || typeof noValue === 'undefined') {
// ......
}
如果函数或全局中的 else 块后没有任何语句,可以删除 else。
// good
function getName() {
if (name) {
return name;
}
return 'unnamed';
}
// bad
function getName() {
if (name) {
return name;
}
else {
return 'unnamed';
}
}
对循环内多次使用的不变值,在循环外用变量缓存
// good
var width = wrap.offsetWidth + 'px';
for (var i = 0, len = elements.length; i < len; i++) {
var element = elements[i];
element.style.width = width;
// ......
}
// bad
for (var i = 0, len = elements.length; i < len; i++) {
var element = elements[i];
element.style.width = wrap.offsetWidth + 'px';
// ......
}
对有序集合进行顺序无关的遍历时,使用逆序遍历。
解释:
逆序遍历可以节省变量,代码比较优化。
示例:
var len = elements.length;
while (len--) {
var element = elements[len];
// ......
}
类型转换检测
类型检测优先使用
typeof
。对象类型检测使用instanceof
。null
或undefined
的检测使用== null
转换成 string 时,使用 + ''。
示例:
// good
num + '';
// bad
new String(num);
num.toString();
String(num);
- 转换成 number 时,通常使用 +。
示例:
// good
+str;
// bad
Number(str);
- string 转换成 number,要转换的字符串结尾包含非数字并期望忽略时,使用 parseInt。
示例:
var width = '200px';
parseInt(width, 10);
- 使用 parseInt 时,必须指定进制。
示例:
// good
parseInt(str, 10);
// bad
parseInt(str);
- 转换成 boolean 时,使用 !!。
示例:
var num = 3.14;
!!num;
- number 去除小数点,使用 Math.floor / Math.round / Math.ceil,不使用 parseInt。
示例:
// good
var num = 3.14;
Math.ceil(num);
// bad
var num = 3.14;
parseInt(num, 10);
字符串
-
字符串开头和结束使用单引号 '。
解释:
输入单引号不需要按住 shift,方便输入。
实际使用中,字符串经常用来拼接 HTML。为方便 HTML 中包含双引号而不需要转义写法。
示例:var str = '我是一个字符串'; var html = '<div class="cls">拼接HTML可以省去双引号转义</div>';
-
使用 数组 或 + 拼接字符串。
解释:
使用 + 拼接字符串,如果拼接的全部是 StringLiteral,压缩工具可以对其进行自动合并的优化。所以,静态字符串建议使用 + 拼接。
在现代浏览器下,使用 + 拼接字符串,性能较数组的方式要高。
如需要兼顾老旧浏览器,应尽量使用数组拼接字符串。
示例:// 使用数组拼接字符串 var str = [ // 推荐换行开始并缩进开始第一个字符串, 对齐代码, 方便阅读. '<ul>', '<li>第一项</li>', '<li>第二项</li>', '</ul>' ].join(''); // 使用 + 拼接字符串 var str2 = '' // 建议第一个为空字符串, 第二个换行开始并缩进开始, 对齐代码, 方便阅读 + '<ul>', + '<li>第一项</li>', + '<li>第二项</li>', + '</ul>';
对象
- 使用对象字面量 {} 创建新 Object。
示例:
// good
var obj = {};
// bad
var obj = new Object();
- 对象创建时,如果一个对象的所有 属性 均可以不添加引号,则所有 属性 不得添加引号。
示例:
var info = {
name: 'someone',
age: 28
};
- 对象创建时,如果任何一个 属性 需要添加引号,则所有 属性 必须添加 '。
解释:
如果属性不符合 Identifier 和 NumberLiteral 的形式,就需要以 StringLiteral 的形式提供。
示例:
// good
var info = {
'name': 'someone',
'age': 28,
'more-info': '...'
};
// bad
var info = {
name: 'someone',
age: 28,
'more-info': '...'
};
- 不允许修改和扩展任何原生对象和宿主对象的原型。
示例:
// 以下行为绝对禁止
String.prototype.trim = function () {
};
属性访问时,尽量使用
.
。
解释:
属性名符合 Identifier 的要求,就可以通过 . 来访问,否则就只能通过 [expr] 方式访问。通常在 JavaScript 中声明的对象,属性命名是使用 Camel 命名法,用 . 来访问更清晰简洁。部分特殊的属性(比如来自后端的JSON),可能采用不寻常的命名方式,可以通过 [expr] 方式访问。
示例:
info.age;
info['more-info'];
- for in 遍历对象时, 使用 hasOwnProperty 过滤掉原型中的属性。
示例:
var newInfo = {};
for (var key in info) {
if (info.hasOwnProperty(key)) {
newInfo[key] = info[key];
}
}
数组
使用数组字面量[]
创建新数组,除非想要创建的是指定长度的数组。
示例:
// good
var arr = [];
// bad
var arr = new Array();
- 遍历数组不使用 for in。
解释:
数组对象可能存在数字以外的属性, 这种情况下 for in 不会得到正确结果.
示例:
var arr = ['a', 'b', 'c'];
arr.other = 'other things'; // 这里仅作演示, 实际中应使用Object类型
// 正确的遍历方式
for (var i = 0, len = arr.length; i < len; i++) {
console.log(i);
}
// 错误的遍历方式
for (i in arr) {
console.log(i);
}
不因为性能的原因自己实现数组排序功能,尽量使用数组的 sort 方法。
解释:
自己实现的常规排序算法,在性能上并不优于数组默认的 sort 方法。以下两种场景可以自己实现排序:需要稳定的排序算法,达到严格一致的排序结果。
数据特点鲜明,适合使用桶排。清空数组使用 .length = 0。
函数
一个函数的长度控制在 50 行以内。
解释:
将过多的逻辑单元混在一个大函数中,易导致难以维护。一个清晰易懂的函数应该完成单一的逻辑单元。复杂的操作应进一步抽取,通过函数的调用来体现流程。特定算法等不可分割的逻辑允许例外。
一个函数的参数控制在 6 个以内。
解释:
除去不定长参数以外,函数具备不同逻辑意义的参数建议控制在 6 个以内,过多参数会导致维护难度增大。某些情况下,如使用 AMD Loader 的 require 加载多个模块时,其 callback 可能会存在较多参数,因此对函数参数的个数不做强制限制。
空函数不使用 new Function() 的形式。
示例:
var emptyFunction = function () {};
- 对于性能有高要求的场合,建议存在一个空函数的常量,供多处使用共享。
示例:
var EMPTY_FUNCTION = function () {};
function MyClass() {
}
MyClass.prototype.abstractMethod = EMPTY_FUNCTION;
MyClass.prototype.hooks.before = EMPTY_FUNCTION;
MyClass.prototype.hooks.after = EMPTY_FUNCTION;
元素样式style
尽可能通过为元素添加预定义的 className 来改变元素样式,避免直接操作 style 设置
通过 style 对象设置元素样式时,对于带单位非 0 值的属性,不允许省略单位
之前写的笔记,想要再找原文,找不到了。。只是知识的搬运工。