Author: Nicholas C. Zakas
编程风格
编程风格 (style guideline) 用来规约单文件代码的规划. 编程风格是编码规范 (code convention) 的一种. 在编程风格外, 编码规范还包括编程最佳实践, 文件和目录的规划及注释等方面.
例举几个可供参考的编程风格指南:
Code Conventions for the JavaScript Programming Language - Douglas Crockford
JavaScript Style Guide | jQuery
第 1 章, 基本格式化
缩进层级
制表符缩进和空格缩进之争历来已久. 二者均有各自优劣, 具体的选择取决于团队偏好, 不应纠结于某一种缩进. 只要牢记一点: 不要在团队中将二者混用.
语句结尾
养成在语句结尾插入分号的习惯. 得益于分析器的自动分号插入 (Automatic Semicolon Insertion, ASI) 机制, 省略 JavaScript 代码的语句结束分号不影响代码正常工作, 但出于几点原因, 不推荐这么做.
行长度
一行代码过长将导致窗口出现横向滚动条, 经典的建议是将行长度保持在 80 字符以内. (指英文字符, 中文字符算两个)
换行
如果约定将行长度保持在一定长度之内, 就涉及 (在一个语句内) 换行的问题. 建议的做法: 在运算符后换行, 并在新行添加两层缩进, 即
// 推荐
callAFunction(document, element, window, "some string value", true, 123,
navigator);
// 不推荐
callAFunction(document, element, window, "some string value", true, 123,
navigator);
// 不推荐
callAFunction(document, element, window, "some string value", true, 123
, navigator);
采用两层缩进可以区别于正常缩进, 例如:
if (isLeapYear && isFebruary && day == 28 && itsYourBirthday &&
noPlans) {
waitAnotherFourYears();
}
以上代码中 if 条件语句被拆分为两行, 但 if 语句主体仍是一层缩进, 避免了二者的混淆.
这个规则有一个例外: 在给变量赋值时, 应使第二行与赋值运算符对齐, 即
let result = something + anotherThing + yetAnotherThing + somethingElse +
anotherSomethingElse;
空行
如果没有特殊爱好, 可以遵从以下空行条件:
- 在方法之间
- 在方法中的局部变量和第一条语句之间
- 在多行或单行注释之前
- 在方法内的逻辑片断之间
命名
主流的变量命名法有蛇式 (snake case) , 小驼峰式 (camel case) , 大驼峰式 (pascal case) 等, 大多数标准中推荐使用小驼峰式命名法, 即变量名以小写字母开头, 后续每个单词首字母都大写.
// snake case
let this_is_my_name;
// camel case
let thisIsMyName
// pascal case
let ThisIsMyName
变量与函数
几个要点:
- 命名长度尽可能短, 以足够表示其作用为准
- 尽量在变量名中体现出值的数据类型, 如
count
,length
,code
,name
,message
- 避免无意义的命名, 如
foo
,bar
等, 不过i
,j
,k
等单字符例外, 它们常用于循环中 - 可以用动词 (如
can
,has
,is
,get
,set
) 将函数名与变量名区分开, 如isEnabled
是一个函数,myName
是一个变量
常量
全大写字母加下划线的常量名是很多语言通行的做法. 如MAX_COUNT
.
构造函数
和常量一样, 使用大驼峰式 (pascal case) 构造函数名是很多语言通行的做法. 如ButtonComponent
.
JavaScript 的构造函数就是面向对象的类, JavaScript 本身没有
class
语法
直接量
字符串
JavaScript 中, 用单引号和双引号括起的字符串在功能上完全相同. 只要在团队中保持同一种风格, 并没有什么要担心的.
使用单引号不符合 ECMA-404 (JSON) 规范, 因此如果没有什么特殊偏好, 还是建议采用双引号, 参见 String - MDN.
在定义过长的, 需要换行的字符串时, 建议把字符串拆成两部分, 并用 "+" 号将它们连接起来:
let longString = 'Here\'s the story=, of a man ' +
'named Brady.';
数字
建议在定义数字时:
- 小数不要省略小数部分 (如
1.0
不要写成1.
) - 小数不要省略整数部分 (如
0.1
不要写成.1
) - 不要在数字前加 0 , 八进制写法已经被弃用了
null
建议将且仅将 null 用于以下情况:
- 初始化一个可能被赋值为一个对象的变量
- 用于和一个已经初始化的变量进行比较
- 当函数期望传入对象时, 作参数传入
- 当函数期望返回对象时, 作返回值传出
不要将 null 用于以下情况:
- 不要用 null 来检测是否传入了某个参数
- 不要用 null 来检测一个未初始化的变量
// ok
let person = null;
// ok
function getPerson() {
if (condition) {
return new Person('Ncholas');
} else {
return null;
}
}
// ok
let person - getPerson();
if (person !== null) {
doSomething();
}
// 不要用 null 来检测是否传入了某个参数
function doSomething(arg1, arg2, arg3, arg4) {
if (arg4 != null) {
doSomethingElse();
}
}
// 不要用 null 来检测一个未初始化的变量
let person;
if (person != null) {
doSomething();
}
null == undefined
是真, 而null === undefined
是假
undefined
声明而未初始化的变量会有一个初始值, 即 undefined. JavaScript 有一个奇怪的行为, 用 typeof 检测一个未声明的变量和一个声明而未初始化的变量都会返回 undefined (实际上未声明和声明但未初始化的变量有着天壤之别) , 因此, 推荐强制规定变量必须初始化, 即:
let foo;
let bar = null;
console.log(typeof foo); // undefined, 不推荐, 与未声明的 foobar 相混淆
console.log(typeof bar); // object, 推荐, 给所有声明的变量赋予初始值 null, typeof 返回 object
console.log(typeof foobar); // undefined
console.log(foo == 1); // false
console.log(bar == 1); // false
console.log(foobar == 1); // error!
对象直接量
通常用对象直接量的做法创建对象:
// 不推荐
let book = new Object();
book.title = 'Maintainable Javascript';
book.author = 'Nicholas C. Zakas';
// 通常用对象直接量定义对象
let book = {
title: 'Maintainable JavaScript',
author: 'Nicholas C. Zakas'
};
数组直接量
与对象直接量类似:
// 不推荐
let colors = new Array('red', 'green', 'blue');
// 通常用数组直接量定义数组
let colors = ['red', 'green', 'blue'];