前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Javascript编码规范建议

Javascript编码规范建议

作者头像
Clearlove
发布2019-08-29 14:56:23
1.3K0
发布2019-08-29 14:56:23
举报
文章被收录于专栏:前端客栈前端客栈

这段时间在整理前端代码规范,现将JS部分的内容整理,都是基本基础的内容。请各位大神斧正!! 目前已经整理如下的代码规范:HTML编码规范CSSS编码规范CSS规范--BEM入门

1. 代码风格

1.1 文件

强制 JavaScript 文件使用无 BOM 的 UTF-8 编码。
建议 在文件结尾处,保留一个空行。

1.2 结构

1.2.1 缩进
强制 使用 4 个空格做为一个缩进层级,不允许使用 2 个空格 或 tab 字符。
强制 switch 下的 case 和 default 必须增加一个缩进层级。

示例:

代码语言:javascript
复制
// good
switch (variable) {
    case '1':
        // do...
        break;
    case '2':
        // do...
        break;
    default:
        // do...
}

------------


// bad
switch (variable) {
case '1':
    // do...
    break;
case '2':
    // do...
    break;
default:
    // do...
}
1.2.2空格
强制 二元运算符两侧必须有一个空格,一元运算符与操作对象之间不允许有空格。

示例:

代码语言:javascript
复制
var a = !arr.length;
a++;
a = b + c;
强制 在对象创建时,属性中的 : 之后必须有空格, : 之前不允许有空格。

示例:

代码语言:javascript
复制
// good
var obj = {
    a: 1,
    b: 2,
    c: 3
};

------------

// bad
var obj = {
    a : 1,
    b:2,
    c :3
};
强制 ,; 前不允许有空格。如果不位于行尾; 后必须跟一个空格。

示例:

代码语言:javascript
复制
// good
callFunc(a, b);

------------
// bad
callFunc(a , b) ;
强制 单行声明的数组与对象,如果包含元素, {} 和 [] 内紧贴括号部分不允许包含空格。

示例:

解释:

声明包含元素的数组与对象,只有当内部元素的形式较为简单时,才允许写在一行。元素复杂的情况,还是应该换行书写。

示例:

代码语言:javascript
复制
// good
var arr1 = [];
var arr2 = [1, 2, 3];
var obj1 = {};
var obj2 = {name: 'obj'};
var obj3 = {
    name: 'obj',
    age: 20,
    sex: 1
};

------------

// bad
var arr1 = [ ];
var arr2 = [ 1, 2, 3 ];
var obj1 = { };
var obj2 = { name: 'obj' };
var obj3 = {name: 'obj', age: 20, sex: 1};
强制 行尾不得有多余的空格。
建议 用作代码块起始的左花括号{前必须有一个空格。

示例:

代码语言:javascript
复制
// good
if (condition) {
}
while (condition) {
}
function funcName() {
}

------------


// bad
if (condition){
}
while (condition){
}
function funcName(){
}
建议 if / else / for / while / function / switch / do / try / catch / finally 关键字后,必须有一个空格。

示例:

代码语言:javascript
复制
// good
if (condition) {
}
while (condition) {
}
(function () {
})();

------------

// bad
if(condition) {
}
while(condition) {
}
(function() {
})();
建议 函数声明、具名函数表达式、函数调用中,函数名和(之间不允许有空格。

示例:

代码语言:javascript
复制
// good
function funcName() {
}
var funcName = function funcName() {
};
funcName();

------------

// bad
function funcName () {
}
var funcName = function funcName () {
};
funcName ();
1.2.3换行
强制 每个独立语句结束后必须换行。
强制 每行不得超过 120 个字符。

解释:

超长的不可分割的代码允许例外,比如复杂的正则表达式。长字符串不在例外之列。

强制 运算符处换行时,运算符必须在新行的行首。

示例:

代码语言:javascript
复制
// 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 语句等场景中,不允许在 ,; 前换行。

示例:

代码语言:javascript
复制
// 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
);
建议 不同行为或逻辑的语句集,使用空行隔开,更易阅读。

示例:

代码语言:javascript
复制
// 仅为按逻辑换行的示例,不代表setStyle的最优实现
function setStyle(element, property, value) {
    if (element == null) {
        return;
    }
    element.style[property] = value;
}
建议 在语句的行长度超过 120 时,根据逻辑条件合理缩进。

示例:

代码语言:javascript
复制
// 较复杂的逻辑条件组合,将每个条件独立一行,逻辑运算符放置在行首进行分隔,或将部分逻辑按逻辑组合进行分隔。
// 建议最终将右括号 ) 与左大括号 { 放在独立一行,保证与 `if` 内语句块能容易视觉辨识。
if (user.isAuthenticated()
    && user.isInRole('admin')
    && user.hasAuthority('add-admin')
    || user.hasAuthority('delete-admin')
) {
    // Code
}
// 按一定长度截断字符串,并使用 + 运算符进行连接。
// 分隔字符串尽量按语义进行,如不要在一个完整的名词中间断开。
// 特别的,对于 HTML 片段的拼接,通过缩进,保持和 HTML 相同的结构。
var html = '' // 此处用一个空字符串,以便整个 HTML 片段都在新行严格对齐
    + '<article>'
    +     '<h1>Title here</h1>'
    +     '<p>This is a paragraph</p>'
    +     '<footer>Complete</footer>'
    + '</article>';
// 也可使用数组来进行拼接,相对 `+` 更容易调整缩进。
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 等语句,推荐使用在 } 号后添加一个换行 的风格,使代码层次结构更清晰,阅读性更好。

示例:

代码语言:javascript
复制
if (condition) {
    // some statements;
}
else {
    // some statements;
}
try {
    // some statements;
}
catch (ex) {
    // some statements;
}
1.2.4语句
强制 不得省略语句结束的分号。
强制 在 if / else / for / do / while 语句中,即使只有一行,也不得省略块 {...}。

示例:

代码语言:javascript
复制
// good
if (condition) {
    callFunc();
}

------------

// bad
if (condition) callFunc();
if (condition)
    callFunc();
强制 函数定义结束不允许添加分号。

示例:

代码语言:javascript
复制
// good
function funcName() {
}

------------

// bad
function funcName() {
};
// 如果是函数表达式,分号是不允许省略的。
var funcName = function () {
};
强制 IIFE 必须在函数表达式外添加 (,非 IIFE 不得在函数表达式外添加 (

解释:

IIFE = Immediately-Invoked Function Expression.

额外的 ( 能够让代码在阅读的一开始就能判断函数是否立即被调用,进而明白接下来代码的用途。而不是一直拖到底部才恍然大悟。

示例:

代码语言:javascript
复制
// good
var task = (function () {
   // Code
   return result;
})();
var func = function () {
};

------------

// bad
var task = function () {
    // Code
    return result;
}();
var func = (function () {
});

1.3命名

强制 变量 使用 Camel命名法。

示例:

代码语言:javascript
复制
var loadingModules = {};
强制 jQuery对象变量,使用$作为变量名前缀。

示例:

代码语言:javascript
复制
// good
var $sidebar = $('.sidebar');

------------

// bad
var sidebar = $('.sidebar');
强制 常量 使用 全部字母大写,单词间下划线分隔 的命名方式。

示例:

代码语言:javascript
复制
var HTML_ENTITY = {};
强制 函数 使用 Camel命名法。

示例:

代码语言:javascript
复制
function getStyle(element) {
}
建议 函数名 使用 动宾短语。

示例:

代码语言:javascript
复制
function getStyle(element) {
}
强制 类 使用 Pascal命名法。

示例:

代码语言:javascript
复制
function TextNode(options) {
}
强制 类名 使用 名词。

示例:

代码语言:javascript
复制
function Engine(options) {
}
强制 类的 方法 / 属性 使用 Camel命名法。

示例:

代码语言:javascript
复制
function TextNode(value, engine) {
    this.value = value;
    this.engine = engine;
}
TextNode.prototype.clone = function () {
    return this;
};
强制 枚举变量 使用 Pascal命名法,枚举的属性 使用 全部字母大写,单词间下划线分隔 的命名方式。

示例:

代码语言:javascript
复制
var TargetState = {
    READING: 1,
    READED: 2,
    APPLIED: 3,
    READY: 4
};
强制 由多个单词组成的缩写词,在命名中,根据当前命名法和出现的位置,所有字母的大小写与首字母的大小写保持一致。

示例:

代码语言:javascript
复制
function XMLParser() {
}
function insertHTML(element, html) {
}
var httpRequest = new HTTPRequest();
建议 boolean 类型的变量使用 is 或 has 开头。

示例:

代码语言:javascript
复制
var isReady = false;
var hasMoreCommands = false;
建议 Promise对象 用 动宾短语的进行时 表达。

示例:

代码语言:javascript
复制
var loadingData = ajax.get('url');
loadingData.then(callback);
建议 不保存this引用,使用Function#bind。

示例:

代码语言:javascript
复制
// good
function () {
    return function () {
        console.log(this);
    }.bind(this);
}
// bad
function () {
    var self = this;
    return function () {
        console.log(self);
    };
}
// bad
function () {
    var that = this;
    return function () {
        console.log(that);
    };
}
// bad
function () {
    var _this = this;
    return function () {
        console.log(_this);
    };
}

1.4 注释

1.4.1 单行注释
强制 必须独占一行。// 后跟一个空格,缩进与下一行被注释说明的代码一致。
1.4.2多行注释
建议 避免使用 /.../ 这样的多行注释。有多行注释内容时,使用多个单行注释。
1.4.3文档化注释
强制 为了便于代码阅读和自文档化,以下内容必须包含以 /*.../ 形式的块注释中。

解释:

  • 文件
  • namespace
  • 函数或方法
  • 类属性
  • 事件
  • 全局变量
  • 常量
强制 文档注释前必须空一行。
建议 自文档化的文档说明 what,而不是 how。
1.4.4类型定义
强制 类型定义都是以 { 开始, 以 } 结束。

解释:

常用类型如:{string}, {number}, {boolean}, {Object}, {Function}, {RegExp}, {Array}, {Date}

类型不仅局限于内置的类型,也可以是自定义的类型。比如定义了一个类 Developer,就可以使用它来定义一个参数和返回值的类型。

强制 对于基本类型 {string}, {number}, {boolean},首字母必须小写。
1.4.5 文件注释
强制 文件顶部必须包含文件注释,用 @file 标识文件说明。

示例:

代码语言:javascript
复制
/**
 * @file Describe the file
 */
1.4.6 类注释
强制 类的属性或方法等成员信息不是 public 的,应使用 @protected 或 @private 标识可访问性。

解释:

生成的文档中将有可访问性的标记,避免用户直接使用非 public 的属性或方法。

示例:

代码语言:javascript
复制
/**
 * 类描述
 *
 * @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 () {
};
建议 使用 @class 标记类或构造函数。

示例:

代码语言:javascript
复制
/**
 * 描述
 *
 * @class
 */
function Developer() {
    // constructor body
}
建议 使用 @extends 标记类的继承信息。

示例:

代码语言:javascript
复制
/**
 * 描述
 *
 * @class
 * @extends Developer
 */
function Fronteer() {
    Developer.call(this);
    // constructor body
}
util.inherits(Fronteer, Developer);
1.4.7 函数/方法注释
强制 函数/方法注释必须包含函数说明,有参数和返回值时必须使用注释标识。

解释:

return 关键字仅作退出函数/方法使用时,无须对返回值作注释标识。

强制 参数和返回值注释必须包含类型信息,且不允许省略参数的说明。

示例:

代码语言:javascript
复制
/**
 * 函数描述
 *
 * @param {string} p1 参数1的说明
 * @param {string} p2 参数2的说明,比较长
 *     那就换行了.
 * @param {number=} p3 参数3的说明(可选)
 * @return {Object} 返回值描述
 */
function foo(p1, p2, p3) {
    var p3 = p3 || 10;
    return {
        p1: p1,
        p2: p2,
        p3: p3
    };
}
强制 对 Object 中各项的描述, 必须使用 @param 标识。

示例:

代码语言:javascript
复制
/**
 * 函数描述
 *
 * @param {Object} option 参数描述
 * @param {string} option.url option项描述
 * @param {string=} option.method option项描述,可选参数
 */
function foo(option) {
    // TODO
}
1.4.8 事件注释
强制 必须使用 @event 标识事件,事件参数的标识与方法描述的参数标识相同。

示例:

代码语言:javascript
复制
/**
 * 值变更时触发
 *
 * @event Select#change
 * @param {Object} e e描述
 * @param {string} e.before before描述
 * @param {string} e.after after描述
 */
this.fire(
    'change',
    {
        before: 'foo',
        after: 'bar'
    }
);
建议 对于事件对象的注释,使用 @param 标识,生成文档时可读性更好。

示例:

代码语言:javascript
复制
/**
 * 点击处理
 *
 * @fires Select#change
 * @private
 */
Select.prototype.clickHandler = function () {
    /**
     * 值变更时触发
     *
     * @event Select#change
     * @param {Object} e e描述
     * @param {string} e.before before描述
     * @param {string} e.after after描述
     */
    this.fire(
        'change',
        {
            before: 'foo',
            after: 'bar'
        }
    );
};
1.4.9 常量注释
强制 常量必须使用 @const 标记,并包含说明和类型信息。

示例:

代码语言:javascript
复制
/**
 * 常量说明
 *
 * @const
 * @type {string}
 */
var REQUEST_URL = 'myurl.do';
1.4.10 细节注释
强制 有时我们会使用一些特殊标记进行说明。特殊标记必须使用单行注释的形式。下面列举了一些常用标记:

解释:

  1. TODO: 有功能待实现。此时需要对将要实现的功能进行简单说明。
  2. FIXME: 该处代码运行没问题,但可能由于时间赶或者其他原因,需要修正。此时需要对如何修正进行简单说明。
  3. HACK: 为修正某些问题而写的不太好或者使用了某些诡异手段的代码。此时需要对思路或诡异手段进行描述。
  4. XXX: 该处存在陷阱。此时需要对陷阱进行描述。

2. 语言特性

2.1 变量

强制 变量、函数在使用前必须先定义。

解释:

不通过 var 定义变量将导致变量污染全局环境。

示例:

代码语言:javascript
复制
// good
var name = 'MyName';

------------

// bad
name = 'MyName';

原则上不建议使用全局变量,对于已有的全局变量或第三方框架引入的全局变量,需要根据检查工具的语法标识。

示例:

代码语言:javascript
复制
/* globals jQuery */
var element = jQuery('#element-id');

建议 最后再声明未赋值的变量。当你需要引用前面的变量赋值时这将变的很有用。

示例:

代码语言:javascript
复制
// good
var items = getItems();
var goSportsTeam = true;
var dragonball;
var length;
var i;
------------

// bad
var i, len, dragonball,
    items = getItems(),
    goSportsTeam = true;
// bad
var i;
var items = getItems();
var dragonball;
var goSportsTeam = true;
var len;
强制 变量必须 即用即声明,不得在函数或其它形式的代码块起始位置统一声明所有变量。

解释:

变量声明与使用的距离越远,出现的跨度越大,代码的阅读与维护成本越高。虽然JavaScript的变量是函数作用域,还是应该根据编程中的意图,缩小变量出现的距离空间。

示例:

代码语言:javascript
复制
// good
function kv2List(source) {
    var list = [];
    for (var key in source) {
        if (source.hasOwnProperty(key)) {
            var item = {
                k: key,
                v: source[key]
            };
            list.push(item);
        }
    }
    return list;
}
------------

// bad
function kv2List(source) {
    var list = [];
    var key;
    var item;
    for (key in source) {
        if (source.hasOwnProperty(key)) {
            item = {
                k: key,
                v: source[key]
            };
            list.push(item);
        }
    }
    return list;
}

2.2条件

强制 在 Equality Expression 中使用类型严格的 ===。仅当判断 null 或 undefined 时,允许使用 == null。

解释:

使用 === 可以避免等于判断中隐式的类型转换。

示例:

代码语言:javascript
复制
// good
if (age === 30) {
    // ......
}

------------

// bad
if (age == 30) {
    // ......
}
建议 尽可能使用简洁的表达式。

示例:

代码语言:javascript
复制
// 字符串为空
// good
if (!name) {
    // ......
}

------------

// bad
if (name === '') {
    // ......
}
代码语言:javascript
复制
// 字符串非空
// good
if (name) {
    // ......
}
------------

// bad
if (name !== '') {
    // ......
}
代码语言:javascript
复制
// 数组非空
// good
if (collection.length) {
    // ......
}

------------


// bad
if (collection.length > 0) {
    // ......
}
代码语言:javascript
复制
// 布尔不成立
// good
if (!notTrue) {
    // ......
}
------------

// bad
if (notTrue === false) {
    // ......
}
代码语言:javascript
复制
// null 或 undefined
// good
if (noValue == null) {
  // ......
}

------------

// bad
if (noValue === null || typeof noValue === 'undefined') {
  // ......
}
建议 按执行频率排列分支的顺序。

解释:

按执行频率排列分支的顺序好处是:

  1. 阅读的人容易找到最常见的情况,增加可读性。
  2. 提高执行效率。
建议 对于相同变量或表达式的多值条件,用 switch 代替 if。

示例:

代码语言:javascript
复制
// good
switch (typeof variable) {
    case 'object':
        // ......
        break;
    case 'number':
    case 'boolean':
    case 'string':
        // ......
        break;
}

------------


// bad
var type = typeof variable;
if (type === 'object') {
    // ......
}
else if (type === 'number' || type === 'boolean' || type === 'string') {
    // ......
}

2.3循环

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

解释:

循环体中的函数表达式,运行过程中会生成循环次数个函数对象。

示例:

代码语言:javascript
复制
// good
function clicker() {
    // ......
}
for (var i = 0, len = elements.length; i < len; i++) {
    var element = elements[i];
    addListener(element, 'click', clicker);
}
// bad
for (var i = 0, len = elements.length; i < len; i++) {
    var element = elements[i];
    addListener(element, 'click', function () {});
}
建议 对循环内多次使用的不变值,在循环外用变量缓存。

示例:

代码语言:javascript
复制
// 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';
    // ......
}
建议 对有序集合进行遍历时,缓存 length。

解释:

虽然现代浏览器都对数组长度进行了缓存,但对于一些宿主对象和老旧浏览器的数组对象,在每次 length 访问时会动态计算元素个数,此时缓存 length 能有效提高程序性能。

示例:

代码语言:javascript
复制
for (var i = 0, len = elements.length; i < len; i++) {
    var element = elements[i];
    // ......
}
建议 对有序集合进行顺序无关的遍历时,使用逆序遍历。

解释:

逆序遍历可以节省变量,代码比较优化。

示例:

代码语言:javascript
复制
var len = elements.length;
while (len--) {
    var element = elements[len];
    // ......
}

2.4 类型

2.4.1类型检测
建议 类型检测优先使用 typeof。对象类型检测使用 instanceof。null 或 undefined 的检测使用 == null。

示例:

代码语言:javascript
复制
// string
typeof variable === 'string'
// number
typeof variable === 'number'
// boolean
typeof variable === 'boolean'
// Function
typeof variable === 'function'
// Object
typeof variable === 'object'
// RegExp
variable instanceof RegExp
// Array
variable instanceof Array
// null
variable === null
// null or undefined
variable == null
// undefined
typeof variable === 'undefined'
2.4.2类型转换
建议 转换成 string 时,使用 + ''。

示例:

代码语言:javascript
复制
// good
num + '';
// bad
new String(num);
num.toString();
String(num);
建议 转换成 number 时,通常使用 +。

示例:

代码语言:javascript
复制
// good
+str;
// bad
Number(str);
建议 string 转换成 number,要转换的字符串结尾包含非数字并期望忽略时,使用 parseInt。

示例:

代码语言:javascript
复制
var width = '200px';
parseInt(width, 10);
强制 使用 parseInt 时,必须指定进制。

示例:

代码语言:javascript
复制
// good
parseInt(str, 10);
// bad
parseInt(str);
建议 转换成 boolean 时,使用 !!。

示例:

代码语言:javascript
复制
var num = 3.14;
!!num;
建议 number 去除小数点,使用 Math.floor / Math.round / Math.ceil,不使用 parseInt。

示例:

代码语言:javascript
复制
// good
var num = 3.14;
Math.ceil(num);
// bad
var num = 3.14;
parseInt(num, 10);

2.5 字符串

强制 字符串开头和结束使用单引号 '。

解释:

  1. 输入单引号不需要按住 shift,方便输入。
  2. 实际使用中,字符串经常用来拼接 HTML。为方便 HTML 中包含双引号而不需要转义写法。

示例:

代码语言:javascript
复制
var str = '我是一个字符串';
var html = '<div class="cls">拼接HTML可以省去双引号转义</div>';

建议 使用 数组 或 + 拼接字符串。

解释:

  1. 使用 + 拼接字符串,如果拼接的全部是 StringLiteral,压缩工具可以对其进行自动合并的优化。所以,静态字符串建议使用 + 拼接。
  2. 在现代浏览器下,使用 + 拼接字符串,性能较数组的方式要高。
  3. 如需要兼顾老旧浏览器,应尽量使用数组拼接字符串。

示例:

代码语言:javascript
复制
// 使用数组拼接字符串
var str = [
    // 推荐换行开始并缩进开始第一个字符串, 对齐代码, 方便阅读.
    '<ul>',
        '<li>第一项</li>',
        '<li>第二项</li>',
    '</ul>'
].join('');
// 使用 `+` 拼接字符串
var str2 = '' // 建议第一个为空字符串, 第二个换行开始并缩进开始, 对齐代码, 方便阅读
    + '<ul>',
    +    '<li>第一项</li>',
    +    '<li>第二项</li>',
    + '</ul>';
建议 程序化生成的字符串使用 Array#join 连接而不是使用连接符。尤其是 IE 下;

示例:

代码语言:javascript
复制
var items;
var messages;
var length;
var i;
messages = [{
  state: 'success',
  message: 'This one worked.'
}, {
  state: 'success',
  message: 'This one worked as well.'
}, {
  state: 'error',
  message: 'This one did not work.'
}];
length = messages.length;
// good
function inbox(messages) {
  items = [];
  for (i = 0; i < length; i++) {
    // use direct assignment in this case because we're micro-optimizing.
    items[i] = '<li>' + messages[i].message + '</li>';
  }
  return '<ul>' + items.join('') + '</ul>';
}
// bad
function inbox(messages) {
  items = '<ul>';
  for (i = 0; i < length; i++) {
    items += '<li>' + messages[i].message + '</li>';
  }
  return items + '</ul>';
}
建议 使用字符串拼接的方式生成HTML,需要根据语境进行合理的转义。

解释:

在 JavaScript 中拼接,并且最终将输出到页面中的字符串,需要进行合理转义,以防止安全漏洞。下面的示例代码为场景说明,不能直接运行。

示例:

代码语言:javascript
复制
// HTML 转义
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>';
建议 复杂的数据到视图字符串的转换过程,选用一种模板引擎。

解释:

使用模板引擎有如下好处:

  1. 在开发过程中专注于数据,将视图生成的过程由另外一个层级维护,使程序逻辑结构更清晰。
  2. 优秀的模板引擎,通过模板编译技术和高质量的编译产物,能获得比手工拼接字符串更高的性能。
  3. 模板引擎能方便的对动态数据进行相应的转义,部分模板引擎默认进行HTML转义,安全性更好。

介绍几款模板插件:

  1. artTemplate: 体积较小,在所有环境下性能高,语法灵活。
  2. dot.js: 体积小,在现代浏览器下性能高,语法灵活。
  3. etpl: 体积较小,在所有环境下性能高,模板复用性高,语法灵活。
  4. handlebars: 体积大,在所有环境下性能高,扩展性高。
  5. hogon: 体积小,在现代浏览器下性能高。
  6. nunjucks: 体积较大,性能一般,模板复用性高。

2.6 对象

强制 不允许修改和扩展任何原生对象和宿主对象的原型。

示例:

代码语言:javascript
复制
// 以下行为绝对禁止
String.prototype.trim = function () {
};
建议 使用对象字面量 {} 创建新 Object。
代码语言:javascript
复制
// good
var obj = {};
// bad
var obj = new Object();
建议 对象创建时,如果一个对象的所有 属性 均可以不添加引号,建议所有 属性 不添加引号。如果任何一个 属性 需要添加引号,则所有 属性 建议添加 '。

解释:

如果属性不符合 Identifier 和 NumberLiteral 的形式,就需要以 StringLiteral 的形式提供。

示例:

代码语言:javascript
复制
// good
var info = {
    'name': 'someone',
    'age': 28,
    'more-info': '...'
};
// bad
var info = {
    name: 'someone',
    age: 28,
    'more-info': '...'
};
建议 属性访问时,尽量使用 .

解释:

属性名符合 Identifier 的要求,就可以通过 . 来访问,否则就只能通过 expr 方式访问。

通常在 JavaScript 中声明的对象,属性命名是使用 Camel 命名法,用 . 来访问更清晰简洁。部分特殊的属性(比如来自后端的 JSON ),可能采用不寻常的命名方式,可以通过 expr 方式访问。

示例:

代码语言:javascript
复制
info.age;
info['more-info'];
建议 for in 遍历对象时, 使用 hasOwnProperty 过滤掉原型中的属性。

示例:

代码语言:javascript
复制
var newInfo = {};
for (var key in info) {
    if (info.hasOwnProperty(key)) {
        newInfo[key] = info[key];
    }
}

2.7 数组

建议 使用数组字面量 [] 创建新数组,除非想要创建的是指定长度的数组。

示例:

代码语言:javascript
复制
// good
var arr = [];
// bad
var arr = new Array();
强制 遍历数组不使用 for in。

解释:

数组对象可能存在数字以外的属性, 这种情况下 for in 不会得到正确结果。

示例:

代码语言:javascript
复制
var arr = ['a', 'b', 'c'];
// 这里仅作演示, 实际中应使用 Object 类型
arr.other = 'other things';
// 正确的遍历方式
for (var i = 0, len = arr.length; i < len; i++) {
    console.log(i);
}
// 错误的遍历方式
for (var i in arr) {
    console.log(i);
}
建议 不因为性能的原因自己实现数组排序功能,尽量使用数组的 sort 方法。
建议 使用 Array#slice 将类数组对象转换成数组。
建议 清空数组使用 .length = 0。

2.8 函数

2.8.1 函数长度
建议 一个函数的长度控制在 50 行以内。

解释:

将过多的逻辑单元混在一个大函数中,易导致难以维护。一个清晰易懂的函数应该完成单一的逻辑单元。复杂的操作应进一步抽取,通过函数的调用来体现流程。

特定算法等不可分割的逻辑允许例外。

示例:

代码语言:javascript
复制
function syncViewStateOnUserAction() {
    if (x.checked) {
        y.checked = true;
        z.value = '';
    }
    else {
        y.checked = false;
    }
    if (a.value) {
        warning.innerText = '';
        submitButton.disabled = false;
    }
    else {
        warning.innerText = 'Please enter it';
        submitButton.disabled = true;
    }
}
// 直接阅读该函数会难以明确其主线逻辑,因此下方是一种更合理的表达方式:
function syncViewStateOnUserAction() {
    syncXStateToView();
    checkAAvailability();
}
function syncXStateToView() {
    y.checked = x.checked;
    if (x.checked) {
        z.value = '';
    }
}
function checkAAvailability() {
    if (a.value) {
        clearWarnignForA();
    }
    else {
        displayWarningForAMissing();
    }
}
2.8.2参数设计
建议 一个函数的参数控制在 6 个以内。

解释:

除去不定长参数以外,函数具备不同逻辑意义的参数建议控制在 6 个以内,过多参数会导致维护难度增大。

某些情况下,如使用 AMD Loader 的 require 加载多个模块时,其 callback 可能会存在较多参数,因此对函数参数的个数不做强制限制。

建议 通过 options 参数传递非数据输入型参数。

解释:

有些函数的参数并不是作为函数逻辑的输入,而是对函数逻辑的某些分支条件判断之用,此类参数建议通过一个 options 参数传递。

如下函数:

代码语言:javascript
复制
/**
 * 移除某个元素
 *
 * @param {Node} element 需要移除的元素
 * @param {boolean} removeEventListeners 是否同时将所有注册在元素上的事件移除
 */
function removeElement(element, removeEventListeners) {
    element.parent.removeChild(element);
    if (removeEventListeners) {
        element.clearEventListeners();
    }
}

可以转换为下面的签名:

代码语言:javascript
复制
/**
 * 移除某个元素
 *
 * @param {Node} element 需要移除的元素
 * @param {Object} options 相关的逻辑配置
 * @param {boolean} options.removeEventListeners 是否同时将所有注册在元素上的事件移除
 */
function removeElement(element, options) {
    element.parent.removeChild(element);
    if (options.removeEventListeners) {
        element.clearEventListeners();
    }
}

这种模式有几个显著的优势:

  1. boolean 型的配置项具备名称,从调用的代码上更易理解其表达的逻辑意义。
  2. 当配置项有增长时,无需无休止地增加参数个数,不会出现 removeElement(element, true, false, false, 3) 这样难以理解的调用代码。
  3. 当部分配置参数可选时,多个参数的形式非常难处理重载逻辑,而使用一个 options 对象只需判断属性是否存在,实现得以简化。
建议 不要把参数命名为 arguments。这将取代函数作用域内的 arguments 对象。

示例:

代码语言:javascript
复制
// good
function yup(name, options, args) {
  // ...stuff...
}
// bad
function nope(name, options, arguments) {
  // ...stuff...
}

2.9面向对象

强制 类的继承方案,实现时需要修正 constructor。

解释:

通常使用其他 library 的类继承方案都会进行 constructor 修正。如果是自己实现的类继承方案,需要进行 constructor 修正。

示例:

代码语言:javascript
复制
/**
 * 构建类之间的继承关系
 *
 * @param {Function} subClass 子类函数
 * @param {Function} superClass 父类函数
 */
function inherits(subClass, superClass) {
    var F = new Function();
    F.prototype = superClass.prototype;
    subClass.prototype = new F();
    subClass.prototype.constructor = subClass;
}
建议 声明类时,保证 constructor 的正确性。

示例:

代码语言:javascript
复制
function Animal(name) {
    this.name = name;
}
// 直接prototype等于对象时,需要修正constructor
Animal.prototype = {
    constructor: Animal,
    jump: function () {
        alert('animal ' + this.name + ' jump');
    }
};
// 这种方式扩展prototype则无需理会constructor
Animal.prototype.jump = function () {
    alert('animal ' + this.name + ' jump');
};
建议 属性在构造函数中声明,方法在原型中声明。

解释:

原型对象的成员被所有实例共享,能节约内存占用。所以编码时我们应该遵守这样的原则:原型对象包含程序不会修改的成员,如方法函数或配置项。

代码语言:javascript
复制
function TextNode(value, engine) {
    this.value = value;
    this.engine = engine;
}
TextNode.prototype.clone = function () {
    return this;
};
建议 自定义事件的 事件名 必须全小写。

解释:

在 JavaScript 广泛应用的浏览器环境,绝大多数 DOM 事件名称都是全小写的。为了遵循大多数 JavaScript 开发者的习惯,在设计自定义事件时,事件名也应该全小写。

强制 自定义事件只能有一个 event 参数。如果事件需要传递较多信息,应仔细设计事件对象。

解释:

一个事件对象的好处有:

  1. 顺序无关,避免事件监听者需要记忆参数顺序。
  2. 每个事件信息都可以根据需要提供或者不提供,更自由。
  3. 扩展方便,未来添加事件信息时,无需考虑会破坏监听器参数形式而无法向后兼容。
建议 设计自定义事件时,应考虑禁止默认行为。

解释:

常见禁止默认行为的方式有两种:

  1. 事件监听函数中 return false。
  2. 事件对象中包含禁止默认行为的方法,如 preventDefault。

2.10 动态特性

2.10.1 eval
强制 避免使用直接 eval 函数。

解释:

直接 eval,指的是以函数方式调用 eval 的调用方法。直接 eval 调用执行代码的作用域为本地作用域,应当避免。

如果有特殊情况需要使用直接 eval,需在代码中用详细的注释说明为何必须使用直接 eval,不能使用其它动态执行代码的方式,同时需要其他资深工程师进行 Code Review。

2.10.2 with
建议 尽量不要使用 with。

解释:

使用 with 可能会增加代码的复杂度,不利于阅读和管理;也会对性能有影响。大多数使用 with 的场景都能使用其他方式较好的替代。所以,尽量不要使用 with。

2.10.3 delete
建议 处理 delete 可能产生的异常。

解释:

对于有被遍历需求,且值 null 被认为具有业务逻辑意义的值的对象,移除某个属性必须使用 delete 操作。

在严格模式或 IE 下使用 delete 时,不能被删除的属性会抛出异常,因此在不确定属性是否可以删除的情况下,建议添加 try-catch 块。

示例:

代码语言:javascript
复制
try {
    delete o.x;
}
catch (deleteError) {
    o.x = null;
}
2.10.4对象属性
建议 避免修改外部传入的对象。

解释:

JavaScript 因其脚本语言的动态特性,当一个对象未被 seal 或 freeze 时,可以任意添加、删除、修改属性值。

但是随意地对 非自身控制的对象 进行修改,很容易造成代码在不可预知的情况下出现问题。因此,设计良好的组件、函数应该避免对外部传入的对象的修改。

下面代码的 selectNode 方法修改了由外部传入的 datasource 对象。如果 datasource 用在其它场合(如另一个 Tree 实例)下,会造成状态的混乱。

代码语言:javascript
复制
function Tree(datasource) {
    this.datasource = datasource;
}
Tree.prototype.selectNode = function (id) {
    // 从datasource中找出节点对象
    var node = this.findNode(id);
    if (node) {
        node.selected = true;
        this.flushView();
    }
};

对于此类场景,需要使用额外的对象来维护,使用由自身控制,不与外部产生任何交互的 selectedNodeIndex 对象来维护节点的选中状态,不对 datasource 作任何修改。

代码语言:javascript
复制
function Tree(datasource) {
    this.datasource = datasource;
    this.selectedNodeIndex = {}; 
}
Tree.prototype.selectNode = function (id) {
    // 从datasource中找出节点对象
    var node = this.findNode(id);
    if (node) {
        this.selectedNodeIndex[id] = true;
        this.flushView();
    }
};

除此之外,也可以通过 deepClone 等手段将自身维护的对象与外部传入的分离,保证不会相互影响。

建议 具备强类型的设计。

解释:

  1. 如果一个属性被设计为 boolean 类型,则不要使用 1 或 0 作为其值。对于标识性的属性,如对代码体积有严格要求,可以从一开始就设计为 number 类型且将 0 作为否定值。
  2. 从 DOM 中取出的值通常为 string 类型,如果有对象或函数的接收类型为 number 类型,提前作好转换,而不是期望对象、函数可以处理多类型的值。
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. 代码风格
    • 1.1 文件
      • 强制 JavaScript 文件使用无 BOM 的 UTF-8 编码。
      • 建议 在文件结尾处,保留一个空行。
    • 1.2 结构
      • 1.2.1 缩进
      • 强制 使用 4 个空格做为一个缩进层级,不允许使用 2 个空格 或 tab 字符。
      • 强制 switch 下的 case 和 default 必须增加一个缩进层级。
      • 1.2.2空格
      • 强制 二元运算符两侧必须有一个空格,一元运算符与操作对象之间不允许有空格。
      • 强制 在对象创建时,属性中的 : 之后必须有空格, : 之前不允许有空格。
      • 强制 , 和 ; 前不允许有空格。如果不位于行尾, 和 ; 后必须跟一个空格。
      • 强制 单行声明的数组与对象,如果包含元素, {} 和 [] 内紧贴括号部分不允许包含空格。
      • 强制 行尾不得有多余的空格。
      • 建议 用作代码块起始的左花括号{前必须有一个空格。
      • 建议 if / else / for / while / function / switch / do / try / catch / finally 关键字后,必须有一个空格。
      • 建议 函数声明、具名函数表达式、函数调用中,函数名和(之间不允许有空格。
      • 1.2.3换行
      • 强制 每个独立语句结束后必须换行。
      • 强制 每行不得超过 120 个字符。
      • 强制 在函数声明、函数表达式、函数调用、对象创建、数组创建、for 语句等场景中,不允许在 , 或 ; 前换行。
      • 建议 不同行为或逻辑的语句集,使用空行隔开,更易阅读。
      • 建议 在语句的行长度超过 120 时,根据逻辑条件合理缩进。
      • 建议 对于 if...else...、try...catch...finally 等语句,推荐使用在 } 号后添加一个换行 的风格,使代码层次结构更清晰,阅读性更好。
      • 1.2.4语句
      • 强制 不得省略语句结束的分号。
      • 强制 在 if / else / for / do / while 语句中,即使只有一行,也不得省略块 {...}。
      • 强制 IIFE 必须在函数表达式外添加 (,非 IIFE 不得在函数表达式外添加 (。
    • 1.3命名
      • 强制 变量 使用 Camel命名法。
      • 强制 jQuery对象变量,使用$作为变量名前缀。
      • 强制 常量 使用 全部字母大写,单词间下划线分隔 的命名方式。
      • 强制 函数 使用 Camel命名法。
      • 建议 函数名 使用 动宾短语。
      • 强制 类 使用 Pascal命名法。
      • 强制 类名 使用 名词。
      • 强制 类的 方法 / 属性 使用 Camel命名法。
      • 强制 枚举变量 使用 Pascal命名法,枚举的属性 使用 全部字母大写,单词间下划线分隔 的命名方式。
      • 强制 由多个单词组成的缩写词,在命名中,根据当前命名法和出现的位置,所有字母的大小写与首字母的大小写保持一致。
      • 建议 boolean 类型的变量使用 is 或 has 开头。
      • 建议 Promise对象 用 动宾短语的进行时 表达。
      • 建议 不保存this引用,使用Function#bind。
    • 1.4 注释
      • 1.4.1 单行注释
      • 强制 必须独占一行。// 后跟一个空格,缩进与下一行被注释说明的代码一致。
      • 1.4.2多行注释
      • 建议 避免使用 /.../ 这样的多行注释。有多行注释内容时,使用多个单行注释。
      • 1.4.3文档化注释
      • 强制 为了便于代码阅读和自文档化,以下内容必须包含以 /*.../ 形式的块注释中。
      • 强制 文档注释前必须空一行。
      • 建议 自文档化的文档说明 what,而不是 how。
      • 1.4.4类型定义
      • 强制 类型定义都是以 { 开始, 以 } 结束。
      • 强制 对于基本类型 {string}, {number}, {boolean},首字母必须小写。
      • 1.4.5 文件注释
      • 强制 文件顶部必须包含文件注释,用 @file 标识文件说明。
      • 1.4.6 类注释
      • 强制 类的属性或方法等成员信息不是 public 的,应使用 @protected 或 @private 标识可访问性。
      • 建议 使用 @class 标记类或构造函数。
      • 建议 使用 @extends 标记类的继承信息。
      • 1.4.7 函数/方法注释
      • 强制 函数/方法注释必须包含函数说明,有参数和返回值时必须使用注释标识。
      • 强制 参数和返回值注释必须包含类型信息,且不允许省略参数的说明。
      • 强制 对 Object 中各项的描述, 必须使用 @param 标识。
      • 1.4.8 事件注释
      • 强制 必须使用 @event 标识事件,事件参数的标识与方法描述的参数标识相同。
      • 建议 对于事件对象的注释,使用 @param 标识,生成文档时可读性更好。
      • 1.4.9 常量注释
      • 强制 常量必须使用 @const 标记,并包含说明和类型信息。
      • 1.4.10 细节注释
      • 强制 有时我们会使用一些特殊标记进行说明。特殊标记必须使用单行注释的形式。下面列举了一些常用标记:
  • 2. 语言特性
    • 2.1 变量
      • 强制 变量、函数在使用前必须先定义。
      • 强制 变量必须 即用即声明,不得在函数或其它形式的代码块起始位置统一声明所有变量。
    • 2.2条件
      • 强制 在 Equality Expression 中使用类型严格的 ===。仅当判断 null 或 undefined 时,允许使用 == null。
      • 建议 尽可能使用简洁的表达式。
      • 建议 按执行频率排列分支的顺序。
      • 建议 对于相同变量或表达式的多值条件,用 switch 代替 if。
    • 2.3循环
      • 建议 不要在循环体中包含函数表达式,事先将函数提取到循环体外。
      • 建议 对循环内多次使用的不变值,在循环外用变量缓存。
      • 建议 对有序集合进行遍历时,缓存 length。
      • 建议 对有序集合进行顺序无关的遍历时,使用逆序遍历。
    • 2.4 类型
      • 2.4.1类型检测
      • 建议 类型检测优先使用 typeof。对象类型检测使用 instanceof。null 或 undefined 的检测使用 == null。
      • 2.4.2类型转换
      • 建议 转换成 string 时,使用 + ''。
      • 建议 转换成 number 时,通常使用 +。
      • 建议 string 转换成 number,要转换的字符串结尾包含非数字并期望忽略时,使用 parseInt。
      • 建议 转换成 boolean 时,使用 !!。
      • 建议 number 去除小数点,使用 Math.floor / Math.round / Math.ceil,不使用 parseInt。
    • 2.5 字符串
      • 强制 字符串开头和结束使用单引号 '。
      • 建议 程序化生成的字符串使用 Array#join 连接而不是使用连接符。尤其是 IE 下;
      • 建议 使用字符串拼接的方式生成HTML,需要根据语境进行合理的转义。
      • 建议 复杂的数据到视图字符串的转换过程,选用一种模板引擎。
    • 2.6 对象
      • 强制 不允许修改和扩展任何原生对象和宿主对象的原型。
      • 建议 使用对象字面量 {} 创建新 Object。
      • 建议 对象创建时,如果一个对象的所有 属性 均可以不添加引号,建议所有 属性 不添加引号。如果任何一个 属性 需要添加引号,则所有 属性 建议添加 '。
      • 建议 属性访问时,尽量使用 . 。
      • 建议 for in 遍历对象时, 使用 hasOwnProperty 过滤掉原型中的属性。
    • 2.7 数组
      • 建议 使用数组字面量 [] 创建新数组,除非想要创建的是指定长度的数组。
      • 强制 遍历数组不使用 for in。
      • 建议 不因为性能的原因自己实现数组排序功能,尽量使用数组的 sort 方法。
      • 建议 使用 Array#slice 将类数组对象转换成数组。
      • 建议 清空数组使用 .length = 0。
    • 2.8 函数
      • 2.8.1 函数长度
      • 建议 一个函数的长度控制在 50 行以内。
      • 2.8.2参数设计
      • 建议 一个函数的参数控制在 6 个以内。
      • 建议 通过 options 参数传递非数据输入型参数。
    • 2.9面向对象
      • 强制 类的继承方案,实现时需要修正 constructor。
      • 建议 声明类时,保证 constructor 的正确性。
      • 建议 属性在构造函数中声明,方法在原型中声明。
      • 建议 自定义事件的 事件名 必须全小写。
      • 强制 自定义事件只能有一个 event 参数。如果事件需要传递较多信息,应仔细设计事件对象。
      • 建议 设计自定义事件时,应考虑禁止默认行为。
    • 2.10 动态特性
      • 2.10.1 eval
      • 强制 避免使用直接 eval 函数。
      • 2.10.2 with
      • 建议 尽量不要使用 with。
      • 2.10.3 delete
      • 建议 处理 delete 可能产生的异常。
      • 2.10.4对象属性
      • 建议 避免修改外部传入的对象。
      • 建议 具备强类型的设计。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档