前言
阅读谷歌的js规范后一些心得与归纳总结,除此之外还记录部分日常js使用该注意的点
基础规范
- 文件命名 按照项目的风格习惯命名,必须小写,可以包含下划线_和分隔符-
- UTF-8格式编码
- 所用空格字符都需要被转义为空格,tab不该被使用
- 使用特殊转移句型
\', \", \\, \b, \f, \n, \r, \t, \v
而不是转义后的例如\x0a, \u000a, or \u{a}
- 不带入ASCII字符或其他转,让代码更易阅读和理解
源文件结构
按照以下顺序构成:
- 证书或授权信息(若存在)
- JSDoc,文件的总体概要
Example:
/**
* @fileoverview Description of file, its uses and information
* about its dependencies.
* @package
*/
- 模块声明
- 模块引入
已引入的模块禁止再引入其子模块
不能被包裹,必须在顶部 - 文件实现 即code部分内容
格式化
括号Braces
逻辑结构(例如if, else, for, do, while等)必须包裹括号
可保持单行且不影响可读性时例外:if (shortCondition()) return;
没有空格(K&R style)
{ 前不能换行后要换行
} 前要换行,如果代表着判断(如if)结束则换行
空内容则闭合
// 合法:
function doNothing() {}
// 非法:
if (condition) {
// …
} else if (otherCondition) {} else {
// …
}
try {
// …
} catch (e) {}
缩进使用2个空格
状态:每条状态独立一行且必须跟分号,依靠自动补全分号是禁止的🚫
一行限制少于80个字符
换行,没有特定的公式约束,
tips:可以拆分出方法或本地变量减少长度不必换行
// 鼓励
currentEstimate =
calc(currentEstimate + x * currentEstimate) /
2.0f;
// 不鼓励
currentEstimate = calc(currentEstimate + x *
currentEstimate) / 2.0f;
空格间隙
- 垂直回车:在一个类或对象文字之间的连续方法,多个回车不推荐使用但允许。方法内部首尾禁止空行。
- if、for、catch后的( 之间加空格
else、catch前的} 之间加空格
任意 { 前除了①foo({a: [{c: d}]})
方法或array的首个参数②${}
模板语言
运算符前后加空格
: 之后加空格
js注释// 后或者/* 之间 */ - 不建议空格用于水平对齐(例如变量命名长度不一样强制让等号同一垂直线)
- 函数过长时,缩进4个空格换行提高可读性
doSomething(
veryDescriptiveArgumentNumberOne,
veryDescriptiveArgumentTwo,
tableModelEventHandlerProxy,
artichokeDescriptorAdapterIterator) {
// …
}
语法特征
1. 变量声明
- 默认使用const,需要重新赋值时才用let,禁止使用var
- 一次声明一个变量
let a = 1, b = 2
不被允许 - 用到时声明变量,尽可能缩小其作用域,而不是在块级结构的开头声明
- 如果需要备注类型,例如可以知道该array将来用来装什么参数
const /** !Array<number> */ data = [];
/** @type {!Array<number>} */
const data = [];
2. 数组
- 用逗号结尾换行分割
- 不使用Array构造数组,不使用
const a1 = new Array(x1, x2, x3)
而是const a1 = [x1, x2, x3]
若明确知道其长度则可以使用new Array(length)
- 不要在array上定义或使用非数字属性(length除外)
- 使用扩展符
[...foo] // preferred over Array.prototype.slice.call(foo)
[...foo, ...bar] // preferred over foo.concat(bar)
3. Object
- 属性后面永远跟逗号不管是否为最后一条
- 不使用Object构造器构建对象。即便没有Array的问题,为了一致性依然不建议这么做。使用
({} or {a: 0, b: 1, c: 2})
- 不混合使用引用和非引用key,即统一加或不加引号
- 可以使用混合属性名
- 方法可以缩写
method() {… }
- 缩写属性是可以被接受的
const foo = 1;
const bar = 2;
const obj = {
foo,
bar,
method() { return this.foo + this.bar; },
};
- 枚举应该注释@enum 告诉可以添加的数据类型
/**
* Supported temperature scales.
* @enum {string}
*/
const TemperatureScale = {
CELSIUS: 'celsius',
FAHRENHEIT: 'fahrenheit',
};
/**
* An enum with two options.
* @enum {number}
*/
const Option = {
/** The option used shall have been the first. */
FIRST_OPTION: 1,
/** The second among two options. */
SECOND_OPTION: 2,
};
4. Classes
- 构造函数是可选项,子类必须先调用
super()
- 私有字段后面带_ 且注释@private
- 复合属性只有symbol可以使用
- 不要直接操作prototype(框架代码、定义接口中字段除外)
- 不要使用Js的getter、setter(数据绑定类框架除外)
- 可以重写toString方法但是必须成功且不能有可预见的错误
- 接口必须备注@interface 或 @record.
5. Functions
- 箭头函数解决了一堆this问题,更倾向于使用,尤其是子类函数。尽量避免去书写self = this。写括号是个好习惯即使只传入1个参数。
- 生成器function后带*再接上空格和方法名。*跟随yield
/** @return {!Iterator<number>} */
function* gen1() {
yield 42;
}
/** @return {!Iterator<number>} */
const gen2 = function*() {
yield* gen1();
}
class SomeClass {
/** @return {!Iterator<number>} */
* gen() {
yield 42;
}
}
- 参数名。传入的参数名必须带有注释@param
- 返回Returns。必须注释返回类型
6. 字符
- 使用单引号
- 使用模板字符。如果产生换行,不需要保持缩进
function arithmetic(a, b) {
return `Here is a table of arithmetic operations:
${a} + ${b} = ${a + b}
${a} - ${b} = ${a - b}
${a} * ${b} = ${a * b}
${a} / ${b} = ${a / b}`;
}
- 不使用\ 行延续符
7. 控制结构语句
- 三种for都可以使用,更加推荐for-of。尽量用Object.keys和for-of代替for-in。使用Object.prototype.hasOwnProperty来排除for-in遍历时取到的原型属性。
- 遇到异常永远new Error抛出异常,永远不要抛出字符或其他格式。极少数情况抛出异常算为正常执行无需做处理一定要备注。
- switch在不调用break时需备注
// fall through
,最后一个状态块不需要,即使没有代码也要写default。
8. this
- 只在class构造函数和方法或者是这两个中定义的箭头函数中可以使用。其他地方必须有个显示的备注告诉this指向。
- 永远不要用this指向
the global object, the context of an eval, the target of an event, or unnecessarily call()ed or apply()ed functions
9. 禁止使用
- with 🚫
- 动态词法解析🚫。eval或者是
Function(...string) constructor
(code loader 除外) - 自动插入分号🚫。永远手动末尾写分号(function class声明除外)
- 非标准的一些特征🚫
- 在原始类型外包裹了一层object,永远不要对Boolean, Number, String, Symbol使用new
// 非法
const /** Boolean */ x = new Boolean(false);
if (x) alert(typeof x); // alerts 'object' - WAT?
// The wrappers may be called as functions for coercing
// (which is preferred over using + or concatenating the empty string)
// or creating symbols.
// 合法
const /** boolean */ x = Boolean(0);
if (!x) alert(typeof x); // alerts 'boolean', as expected
- 不在全局对象上加symbol除非非常有必要
命名
使用字符或数字命名,少数情况下会去使用$_(例如Angular中)
尽可能的给变量名一个文字描述,不要担心敲多余的空格,让别人一眼就能看懂你的代码比这重要得多。不要写一些其他人看不懂的缩写。
// legal
priceCountReader // No abbreviation.
numErrors // "num" is a widespread convention.
numDnsConnections // Most people know what "DNS" stands for.
// Illegal:
n // Meaningless.
nErr // Ambiguous abbreviation.
nCompConns // Ambiguous abbreviation.
wgcConnections // Only your group knows what this stands for.
pcReader // Lots of things can be abbreviated "pc".
cstmrId // Deletes internal letters.
kSecondsPerDay // Do not use Hungarian notation.
根据类型命名
- 包:小写驼峰(例如
my.exampleCode
) - class:大写驼峰(例如
ImmutableList
) - 方法:小写驼峰。动词形式
sendMessage or stop_
,私有方法后面接下划线_ - 枚举:大写驼峰
- 常量名:大写下划线分割_。
CONSTANT_CASE
当需要本地重命名时,保留最后一部分且一定使用const
例如const CONSTANT_NAME = ImportedClass.CONSTANT_NAME;
- 非常量字段名:小写驼峰,私有接下划线_
- 参数名:小写驼峰
- 本地变量名:小写驼峰
引申内容
关于是否句末要写分号?
google规范说法是必须写分号,不使用自动添加分号的工具
从今天的角度讲,很多团队倾向于不写分号,认为是一种语法噪音。
首先思考Javascript自动补全分号的规则是什么?
- 要有换行符,且下一个符号是不符合语法的,那么就尝试插入分号。
- 有换行符,且语法中规定此处不能有换行符,那么就自动插入分号。
- 源代码结束处,不能形成完整的脚本或者模块结构,那么就自动插入分号。
// 通过这个实际例子去分析可以更清楚。两个立即执行函数表达式(IIFE),
// 第一个函数JavaScript引擎认为是返回的可能是个函数,所以不会自动插入分号
// 这是一些鼓励不写分号的编码风格会要求大家写 IIFE 时必须在行首加分号的原因。
(function(a){
console.log(a);
})()
(function(a){
console.log(a);
})()
关于Javascript自动补全分号机制和no line terminator的规则,不在本篇做过多分析,仅仅是证明了不写分号的确存在一些导致语句执行发生问题的风险。所以也存在了通过eslint fix去补全规避风险。
个人认为谷歌提倡的规范更加具有主动性,有很大的借鉴意义。
心得总结
google的规范写的很详细且有在更新维护。日常开发团队如果没有严格要求,不妨作为规范来参考,并不一定完全要照着做但是许多开发中的良好习惯养成了,肯定没有坏处!
网友评论