Appearance
JavaScript 编码规范
目的
为了保证所每个项目组编写出的程序都符合相同的规范,便于理解和维护,便于检查、减少出错概率,有助于成员间交流,保证一致性、统一性而建立的 JavaScript 程序编码规范。
范围
该规范适用于所有基于前端项目。规范内容主要针对 CDN 模式下的 js 加载文件,在 NPM 开发环境中 webpack 开发方式存在部分不同(详见 webpack 开发方式内容)。
文件格式
JavaScript 文件统一使用无 BOM 的 UTF-8 编码。
代码格式规范
在编辑器 vscode 中安装 StandardJS(代码规范和错误检查工具),自动检查并格式化 js 文件。以下罗列了部分规范内容,具体规范内容详见 JavaScript Standard Style.md
文件。
- 使用 2 个空格进行缩进。
- 每行不得超过 120 个字符。
- 除需要转义的情况外,字符串统一使用单引号。
- 每个独立语句结束后必须换行。
- 不同行为或逻辑的语句集,使用空行隔开。
- 运算符处换行时,运算符必须在新行的行首。
- 在 if / else / for / do / while 语句中,即使只有一行,也不得省略块
- 不得省略语句结束的分号。
- 函数定义结束不允许添加分号。
编码规范
类
定义类成员,避免出现独立的变量和函数,编写成构造函数,便于继承、拓展。
模块化 IIFE
无法编写类的,也不能再全局定义孤立的变量、函数,统一采用的模块化编程 IIFE(CND 模式下),降低模块间的耦合性、便于代码的阅读、修改及功能增加。 IIFE 的目的是为了隔离作用域,防止污染全局命名空间。
剥离配置
将配置数据从代码中分离出来,避免影响指令的正常运行。常见的配置数据有:URL 地址、需要展现给用户的字符串、重复使用的变量值、需要设置的值(每页的配置项、默认参数),等任何可能发生变更的值。
ts
var config = {
MSG_INVALID_VALUE: '服务过期',
URL_INVALID: '/errors/invalid.html',
CSS_SELECTED: 'selected'
}
var hardcoded = (function (w) {
'use strict' // 严格模式
return {
validate: function (value) {
if (!value) {
w.alert(config.MSG_INVALID_VALUE)
w.location.href = config.URL_INVALID
}
},
toggleSelected: function (element) {
if (hasClass(element, config.CSS_SELECTED)) {
removeClass(element, config.CSS_SELECTED)
} else {
addClass(element, config.CSS_SELECTED)
}
}
}
})(window)
隔离应用逻辑
将应用逻辑(功能性代码)从所有事件处理程序中抽离出来。 事件处理程序只传递应用逻辑需要的数据,不分发事件对象。
字符串
字符串开头和结束使用单引号 '。 使用数组来进行拼接,相对 +
更容易调整缩进。 使用字符串拼接的方式生成 HTML,需要根据语境进行合理的转义。 不要再 js 代码中编写复杂的 HTML 语句,需要将其剥离出来,写成 template 模板文件。
使用数组来进行拼接,相对 +
更容易调整缩进。
ts
var html = [
'<article>',
'<h1>Title here</h1>',
'<p>This is a paragraph</p>',
'<footer>Complete</footer>',
'</article>'
];
html = html.join('');
var str = '<p>' + htmlEncode(content) + '</p>';
// HTML 转义
var str = '<input type="text" value="' + htmlEncode(value) + '">';
// URL 转义
var str = '<a href="/?key=' + htmlEncode(urlEncode(value)) + '">link</a>';
// JavaScript 字符串 转义 + HTML 转义
var str = '<button onclick="check(\\'' + htmlEncode(strLiteral(name)) + '\\')">提交</button>'; |
常量命名规则
常量命名全大写,用下划线连接。必须使用易于检索名称。 例如:MINUTES_IN_A_YEAR 常用的常量有:颜色、宽度、高度、大小
变量命名规则
必须是可读性好的变量名。大致上是采用:名词+名词、名称+形容词,等以名词+其他的格式(以动词开始的均认为是函数或方法)。 例如:dialogVisible/dialogInvisible、userInfo。
- 数组:+A +List,简单的以 A 结尾,复杂的以 List 结尾。
- 对象:+O。
- 布尔对象:动词过去式等一些特有的名称+形容词,例如:supports、visible (is,has 开头的是表示方法)。
- 后端传递的实体类:+Entity。
- 前端声明的实体类:+Model(不同于构造函数)。 1.jquery 对象:必须以'$'开头命名。
- 函数内部私有变量前缀为下划线(_)后面跟公共属性和方法一样的命名方式。
常用变量有(以该名称结尾):
- 表示计算结果的变量: Total、Sum、Average、Max、Min、Record、String、Pointer
- 参数: Parameter(Argument)、Param
函数规范
函数名应明确表明其功能,从用动词+名称、动词+形容词、动词+名称+名称等以动词为前缀的命名方式。
常用的函数如下约定:
- 事件响应函数 on+事件方法+事件名称
onClickDialog onChange onMouseDrag onRemove
- 返回布尔结果的函数
判断是否可执行某个动作 can 判断是否含有某个值 has 判断是否为某个值 is
- 获取数据函数
简单的返回数据 get 从远程获取数据 fetch fetchUserList fetchDetailForBy 从本地存储加载数据 load 通过计算获取数据 calculate 从数组中查找数据 find 从数据生成或得到 create build parse 设置某个值 set UserInfo 获取某个值/属性 get Current ActivatedItem SelectedItem Field (fieldName) UserInfo UserList
回调函数 名词+状态(Success/Failure) 执行方法 execute do 初始化函数 init
表单提交 submit save submitForm
常用的对仗词 add/remove increment/decrement open/close begin/end insert/delete show/hide create/destroy lock/unlock source/target first/last min/max star/stop get/put next/previous up/down get/set old/new
函数应该只做一层抽象。(见表)
一个函数的长度控制在 30 行以内、参数控制在 3 个以内。
通过 options 参数传递非数据输入型参数(不要使用标记(Flag)作为函数参数)。
不要在任何循环下有重复的代码
避免无意义的条件判断(用三元条件判断)
统一封装 ajax 接口/数据 调用
不要在循环体中包含函数表达式,事先将函数提取到循环体外
编写更多特定功能的函数
编码示例
保持函数功能的单一性。
(功能不单一的函数将导致难以重构、测试和理解。功能单一的函数易于重构,并使代码更加干净)
ts
function emailClients(clients) {
clients.forEach((client) => {
let clientRecord = data.lookup(client)
if (clientRecord.isActive()) {
email(client)
}
})
}
ts
function emailClients(clients) {
clients.forEach((client) => {
emailClientIfNeeded(client)
})
}
function emailClientIfNeeded(client) {
if (isClientActive(client)) {
email(client)
}
}
function isClientActive(client) {
let clientRecord = data.lookup(client)
return clientRecord.isActive()
}
函数名应明确表明其功能
ts
// 描述不够完整的函数名
function dateAdd(date, month) {}
function addComment() {}
ts
// 描述完整的函数名
function dateAddMonth(date, month) {}
function addCommentAndReturnCount() {}; |
不要使用标记(Flag)作为函数参数
ts
function createFile(name, temp) {
if (temp) {
fs.create('./temp/' + name)
} else {
fs.create(name)
}
}
ts
function createTempFile(name) {
fs.create('./temp/' + name)
}
function createFile(name) {
fs.create(name)
}
函数名应明确表明其功能
ts
function parseBetter(code) {
let REGEXPS = []
let statements = code.split(' ')
let tokens
REGEXPS.forEach((REGEX) => {
statements.forEach((statement) => {})
})
let ast
tokens.forEach((token) => {})
ast.forEach((node) => {})
}
ts
function tokenize(code) {
let REGEXPS = []
let statements = code.split(' ')
let tokens
REGEXPS.forEach((REGEX) => {
statements.forEach((statement) => {})
})
return tokens
}
function lexer(tokens) {
let ast
tokens.forEach((token) => {})
return ast
}
function parseBetter(code) {
let tokens = tokenize(code)
let ast = lexer(tokens)
ast.forEach((node) => {})
}
通过 options 参数传递非数据输入型参数。
ts
/**
* 移除某个元素
* @param {Node} element 需要移除的元素
* @param {boolean} removeEventListeners 是否同时将所有注册在元素上的事件移除
*/
function removeElement(element, removeEventListeners) {
element.parent.removeChild(element)
if (removeEventListeners) {
element.clearEventListeners()
}
}
ts
/**
* 移除某个元素
* @param {Node} element 需要移除的元素
* @param {Object} options 相关的逻辑配置
* @param {boolean} options.removeEventListeners 是否同时将所有注册在元素上的事件移除
\*/
function removeElement(element, options) {
element.parent.removeChild(element)
if (options.removeEventListeners) {
element.clearEventListeners()
}
}
不要在循环体中包含函数表达式,事先将函数提取到循环体外
ts
for (var i = 0, len = elements.length; i < len; i++) {
var element = elements[i]
element.style.width = wrap.offsetWidth + 'px'
addListener(element, 'click', function () {})
}
ts
function clicker() {}
var width = wrap.offsetWidth + 'px'
for (var i = 0, len = elements.length; i < len; i++) {
var element = elements[i]
element.style.width = width
addListener(element, 'click', clicker)
}
// 运算符必须在新行的行首
if (
(user.isAuthenticated() &&
user.isInRole('admin') &&
user.hasAuthority('add-admin')) ||
user.hasAuthority('delete-admin')
) {
// Code
}
函数式编程
ts
;(function (log) {
'use strict'
var arr = [10, 3, 7, 9, 100, 20],
sum = 0,
i
for (i = 0; i < arr.length; i++) {
sum += arr[i]
}
log('The sum of array ' + arr + ' is: ' + sum)
})(window.console.log)
ts
(function (log) {
'use strict';
var arr = [10, 3, 7, 9, 100, 20];
var sum = arr.reduce(function (prev current) {
return prevValue + currentValue;
}, 0);
log('The sum of array ' + arr + ' is: ' + sum);
}(window.console.log));
// ie9 以上支持语法 forEach every isArray map filter reduce some
注释规范
单独一行://(双斜线)与注释文字之间保留一个空格 在代码后面添加注释://(双斜线)与代码之间保留一个空格,并且//(双斜线)与注释文字之间保留一个空格。 注释代码://(双斜线)与代码之间保留一个空格。 多行注释 ( /_ 注释说明 _/ ) 函数 (方法) 注释
常用注释关键字
@param 描述参数的信息 @param 参数名 {参数类型} 描述信息 @param name {String} 传入名称
@return 描述返回值的信息 @return {返回类型} 描述信息 @return {Boolean} true: 可执行; false: 不可执行
@author 描述此函数作者的信息 @author 作者信息 [附属信息:如邮箱、日期] @author 张三 2015/07/21
@version 描述此函数的版本号 @version XX.XX.XX @version 1.0.3
@example 示例代码 @example setTitle(‘测试’)