Skip to content

JavaScript 编码规范

编程风格

目的

为了保证所每个项目组编写出的程序都符合相同的规范,便于理解和维护,便于检查、减少出错概率,有助于成员间交流,保证一致性、统一性而建立的 JavaScript 程序编码规范。

范围

该规范适用于所有基于前端项目。规范内容主要针对 CDN 模式下的 js 加载文件,在 NPM 开发环境中 webpack 开发方式存在部分不同(详见 webpack 开发方式内容)。

文件格式

JavaScript 文件统一使用无  BOM  的  UTF-8  编码。

代码格式规范

在编辑器 vscode 中安装 StandardJS(代码规范和错误检查工具),自动检查并格式化 js 文件。以下罗列了部分规范内容,具体规范内容详见 JavaScript Standard Style.md 文件。

  1. 使用  2  个空格进行缩进。
  2. 每行不得超过  120  个字符。
  3. 除需要转义的情况外,字符串统一使用单引号。
  4. 每个独立语句结束后必须换行。
  5. 不同行为或逻辑的语句集,使用空行隔开。
  6. 运算符处换行时,运算符必须在新行的行首。
  7. 在  if / else / for / do / while  语句中,即使只有一行,也不得省略块  
  8. 不得省略语句结束的分号。
  9. 函数定义结束不允许添加分号。

编码规范

定义类成员,避免出现独立的变量和函数,编写成构造函数,便于继承、拓展。

模块化 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。

  1. 数组:+A +List,简单的以 A 结尾,复杂的以 List 结尾。
  2. 对象:+O。
  3. 布尔对象:动词过去式等一些特有的名称+形容词,例如:supports、visible (is,has 开头的是表示方法)。
  4. 后端传递的实体类:+Entity。
  5. 前端声明的实体类:+Model(不同于构造函数)。 1.jquery 对象:必须以'$'开头命名。
  6. 函数内部私有变量前缀为下划线(_)后面跟公共属性和方法一样的命名方式。

常用变量有(以该名称结尾):

  1. 表示计算结果的变量: Total、Sum、Average、Max、Min、Record、String、Pointer
  2. 参数: Parameter(Argument)、Param

函数规范

函数名应明确表明其功能,从用动词+名称、动词+形容词、动词+名称+名称等以动词为前缀的命名方式。

常用的函数如下约定:

  1. 事件响应函数 on+事件方法+事件名称

onClickDialog onChange onMouseDrag onRemove

  1. 返回布尔结果的函数

判断是否可执行某个动作  can 判断是否含有某个值  has 判断是否为某个值  is

  1. 获取数据函数

简单的返回数据  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

  1. 表单提交 submit save submitForm

  2. 常用的对仗词 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

  3. 函数应该只做一层抽象。(见表)

  4. 一个函数的长度控制在  30  行以内、参数控制在  3  个以内。

  5. 通过  options  参数传递非数据输入型参数(不要使用标记(Flag)作为函数参数)。

  6. 不要在任何循环下有重复的代码

  7. 避免无意义的条件判断(用三元条件判断)

  8. 统一封装  ajax  接口/数据   调用

  9. 不要在循环体中包含函数表达式,事先将函数提取到循环体外

  10. 编写更多特定功能的函数

编码示例

保持函数功能的单一性。

(功能不单一的函数将导致难以重构、测试和理解。功能单一的函数易于重构,并使代码更加干净)

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 = [1037910020];


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(‘测试’)