js代码规范

总体规范

  • switch 下的 case 和 default 必须增加一个缩进层级

  • 二元运算符两侧必须有一个空格,一元运算符与操作对象之间不允许有空格

      var a = !arr.length;
      a++;
      a = b + c;
    
  • 用作代码块起始的左花括号 {前必须有一个空格

  • if / else / for / while / function / switch / do / try / catch / finally关键字后,必须有一个空格

  • 在对象创建时,属性中的:之后必须有空格,:之前不允许有空格

      var obj = {
          a: 1,
          b: 2,
          c: 3
      };
    
      // bad
      var obj = {
          a : 1,
          b:2,
          c :3
      };
    
  • 函数声明、具名函数表达式、函数调用中,函数名和(之间不允许有空格

  • ,;前不允许有空格

  • 在函数调用、函数声明、括号表达式、属性访问、if / for / while / switch / catch 等语句中,() 和 [] 内紧贴括号部分不允许有空格

      // good
    
      callFunc(param1, param2, param3);
    
      save(this.list[this.indexes[i]]);
    
      needIncream && (variable += increament);
    
      if (num > list.length) {
      }
    
      while (len--) {
      }
    
    
      // bad
    
      callFunc( param1, param2, param3 );
    
      save( this.list[ this.indexes[ i ] ] );
    
      needIncreament && ( variable += increament );
    
      if ( num > list.length ) {
      }
    
      while ( len-- ) {
      }
    
  • 单行声明的数组与对象,如果包含元素,{}[]内紧贴括号部分不允许包含空格

  • 行尾不得有多余的空格

  • 运算符处换行时,运算符必须在新行的行首

      // 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语句等场景中,不允许在,;前换行

      // 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
      );
    
  • 不同行为或逻辑的语句集,使用空行隔开,更易阅读

  • 较复杂的逻辑条件组合,将每个条件独立一行,逻辑运算符放置在行首进行分隔,或将部分逻辑按逻辑组合进行分隔。

  • 建议最终将右括号 )与左大括号{ 放在独立一行,保证与 if 内语句块能容易视觉辨识。

     if (user.isAuthenticated()
         && user.isInRole('admin')
         && user.hasAuthority('add-admin')
         || user.hasAuthority('delete-admin')
     ) {
         // Code
     }
    
  • 按一定长度截断字符串,并使用 + 运算符进行连接。

  • 分隔字符串尽量按语义进行,如不要在一个完整的名词中间断开。

  • 特别的,对于HTML片段的拼接,通过缩进,保持和HTML相同的结构。
    也可使用数组来进行拼接,相对 + 更容易调整缩进。

      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等语句,推荐使用在}号后添加一个换行 的风格,使代码层次结构更清晰,阅读性更好。

  • 不得省略语句结束的分号
    在 if / else / for / do / while 语句中,即使只有一行,也不得省略块{...}

  • 函数定义结束不允许添加分号

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

命名

  • 变量 使用 Camel命名法

  • 常量 使用 全部字母大写,单词间下划线分隔 的命名方式

  • 类 使用 Pascal命名法

      function TextNode(options) {
      }
    
  • 类的 方法 / 属性 使用 Camel命名法

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

  • 由多个单词组成的缩写词,在命名中,根据当前命名法和出现的位置,所有字母的大小写与首字母的大小写保持一致。

  • 由多个单词组成的缩写词,在命名中,根据当前命名法和出现的位置,所有字母的大小写与首字母的大小写保持一致

     function XMLParser() {
     }
    
     function insertHTML(element, html) {
     }
    
     var httpRequest = new HTTPRequest();
    
  • 类名 使用 名词

  • 函数名 使用 动宾短语

  • boolean 类型的变量使用 is 或 has 开头

  • Promise对象 用 动宾短语的进行时 表达

      var loadingData = ajax.get('url');
      loadingData.then(callback);
    

注释

  • 单行注释
    必须独占一行。// 后跟一个空格,缩进与下一行被注释说明的代码一致。

  • 多行注释
    避免使用 /.../ 这样的多行注释。有多行注释内容时,使用多个单行注释

  • 文档化注释
    为了便于代码阅读和自文档化,以下内容必须包含以 /*.../ 形式的块注释中

    文档注释前必须空一行
    自文档化的文档说明 what,而不是 how

  • 文件顶部必须包含文件注释,用 @file 标识文件说明
    文件注释中可以用 @author 标识开发者信息

  • 命名空间使用 @namespace 标识。

示例:

    /**
     * @namespace
     */
    var util = {};

类和构造函数

  • 使用 @class 标记类或构造函数。
    解释:
    对于使用对象 constructor 属性来定义的构造函数,可以使用 @constructor 来标记。

示例:

    /**
     * 描述
     *
     * @class
     */
    function Developer() {
        // constructor body
    }
  • 使用 @extends 标记类的继承信息。

示例:

    /**
     * 描述
     *
     * @class
     * @extends Developer
     */
    function Fronteer() {
        Developer.call(this);
        // constructor body
    }
    util.inherits(Fronteer, Developer);
  • 使用包装方式扩展类成员时, 必须通过 @lends 进行重新指向.没有 @lends 标记将无法为该类生成包含扩展类成员的文档。

示例:

    /**
     * 类描述
     *
     * @class
     * @extends Developer
     */
    function Fronteer() {
        Developer.call(this);
        // constructor body
    }

    util.extend(
        Fronteer.prototype,
        /** @lends Fronteer.prototype */{
            _getLevel: function () {
                // TODO
            }
        }
    );
  • 类的属性或方法等成员信息使用 @public / @protected / @private 中的任意一个,指明可访问性。
    解释:
    生成的文档中将有可访问性的标记,避免用户直接使用非 public 的属性或方法。

示例:

    /**
     * 类描述
     *
     * @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 () {
    };
函数/方法注释

函数/方法注释必须包含函数说明,有参数和返回值时必须使用注释标识
参数和返回值注释必须包含类型信息和说明

    // null 或 undefined

    // good
    if (noValue == null) {
      // ......
    }

    // bad
    if (noValue === null || typeof noValue === 'undefined') {
      // ......
    }
如果函数或全局中的 else 块后没有任何语句,可以删除 else。
    // good
    function getName() {
        if (name) {
            return name;
        }

        return 'unnamed';
    }

    // bad
    function getName() {
        if (name) {
            return name;
        }
        else {
            return 'unnamed';
        }
    }
对循环内多次使用的不变值,在循环外用变量缓存
    // 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';
        // ......
    }
对有序集合进行顺序无关的遍历时,使用逆序遍历。

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

示例:

    var len = elements.length;
    while (len--) {
        var element = elements[len];
        // ......
    }

类型转换检测

  • 类型检测优先使用typeof。对象类型检测使用 instanceofnullundefined 的检测使用 == null

  • 转换成 string 时,使用 + ''。

示例:

    // good
    num + '';

    // bad
    new String(num);
    num.toString();
    String(num);
  • 转换成 number 时,通常使用 +。

示例:

    // good
      +str;

    // bad
    Number(str);
  • string 转换成 number,要转换的字符串结尾包含非数字并期望忽略时,使用 parseInt。

示例:

    var width = '200px';
    parseInt(width, 10);
  • 使用 parseInt 时,必须指定进制。

示例:

    // good
    parseInt(str, 10);

    // bad
    parseInt(str);
  • 转换成 boolean 时,使用 !!。

示例:

    var num = 3.14;
    !!num;
  • number 去除小数点,使用 Math.floor / Math.round / Math.ceil,不使用 parseInt。

示例:

    // good
    var num = 3.14;
    Math.ceil(num);

    // bad
    var num = 3.14;
    parseInt(num, 10);

字符串

  • 字符串开头和结束使用单引号 '。
    解释:
    输入单引号不需要按住 shift,方便输入。
    实际使用中,字符串经常用来拼接 HTML。为方便 HTML 中包含双引号而不需要转义写法。
    示例:

      var str = '我是一个字符串';
      var html = '<div class="cls">拼接HTML可以省去双引号转义</div>';
    
  • 使用 数组 或 + 拼接字符串。
    解释:
    使用 + 拼接字符串,如果拼接的全部是 StringLiteral,压缩工具可以对其进行自动合并的优化。所以,静态字符串建议使用 + 拼接。
    在现代浏览器下,使用 + 拼接字符串,性能较数组的方式要高。
    如需要兼顾老旧浏览器,应尽量使用数组拼接字符串。
    示例:

      // 使用数组拼接字符串
      var str = [
          // 推荐换行开始并缩进开始第一个字符串, 对齐代码, 方便阅读.
          '<ul>',
              '<li>第一项</li>',
              '<li>第二项</li>',
          '</ul>'
        ].join('');
    
      // 使用 + 拼接字符串
      var str2 = '' // 建议第一个为空字符串, 第二个换行开始并缩进开始, 对齐代码, 方便阅读
          + '<ul>',
          +    '<li>第一项</li>',
          +    '<li>第二项</li>',
          + '</ul>';
    

对象

  • 使用对象字面量 {} 创建新 Object。

示例:

    // good
    var obj = {};

    // bad
    var obj = new Object();
  • 对象创建时,如果一个对象的所有 属性 均可以不添加引号,则所有 属性 不得添加引号。

示例:

    var info = {
        name: 'someone',
        age: 28
    };
  • 对象创建时,如果任何一个 属性 需要添加引号,则所有 属性 必须添加 '。
    解释:
    如果属性不符合 Identifier 和 NumberLiteral 的形式,就需要以 StringLiteral 的形式提供。

示例:

    // good
      var info = {
        'name': 'someone',
        'age': 28,
        'more-info': '...'
    };

    // bad
    var info = {
        name: 'someone',
        age: 28,
        'more-info': '...'
    };
  • 不允许修改和扩展任何原生对象和宿主对象的原型。

示例:

    // 以下行为绝对禁止
    String.prototype.trim = function () {
    };
  • 属性访问时,尽量使用.
    解释:
    属性名符合 Identifier 的要求,就可以通过 . 来访问,否则就只能通过 [expr] 方式访问。

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

示例:

    info.age;
    info['more-info'];
  • for in 遍历对象时, 使用 hasOwnProperty 过滤掉原型中的属性。

示例:

    var newInfo = {};
    for (var key in info) {
        if (info.hasOwnProperty(key)) {
            newInfo[key] = info[key];
        }
    }

数组

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

示例:

    // good
    var arr = [];

    // bad
    var arr = new Array();
  • 遍历数组不使用 for in。
    解释:
    数组对象可能存在数字以外的属性, 这种情况下 for in 不会得到正确结果.

示例:

    var arr = ['a', 'b', 'c'];
    arr.other = 'other things'; // 这里仅作演示, 实际中应使用Object类型

    // 正确的遍历方式
      for (var i = 0, len = arr.length; i < len; i++) {
        console.log(i);
    }

    // 错误的遍历方式
      for (i in arr) {
        console.log(i);
    }
  • 不因为性能的原因自己实现数组排序功能,尽量使用数组的 sort 方法。
    解释:
    自己实现的常规排序算法,在性能上并不优于数组默认的 sort 方法。以下两种场景可以自己实现排序:

  • 需要稳定的排序算法,达到严格一致的排序结果。
    数据特点鲜明,适合使用桶排。

  • 清空数组使用 .length = 0。

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

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

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

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

  • 空函数不使用 new Function() 的形式。

示例:

    var emptyFunction = function () {};
  • 对于性能有高要求的场合,建议存在一个空函数的常量,供多处使用共享。

示例:

    var EMPTY_FUNCTION = function () {};

    function MyClass() {
    }

    MyClass.prototype.abstractMethod = EMPTY_FUNCTION;
    MyClass.prototype.hooks.before = EMPTY_FUNCTION;
    MyClass.prototype.hooks.after = EMPTY_FUNCTION;

元素样式style

  • 尽可能通过为元素添加预定义的 className 来改变元素样式,避免直接操作 style 设置

  • 通过 style 对象设置元素样式时,对于带单位非 0 值的属性,不允许省略单位

之前写的笔记,想要再找原文,找不到了。。只是知识的搬运工。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,558评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,002评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,036评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,024评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,144评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,255评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,295评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,068评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,478评论 1 305
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,789评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,965评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,649评论 4 336
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,267评论 3 318
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,982评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,223评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,800评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,847评论 2 351

推荐阅读更多精彩内容

  • 原文: https://github.com/ecomfe/spec/blob/master/javascript...
    zock阅读 3,371评论 2 36
  • 第5章 引用类型(返回首页) 本章内容 使用对象 创建并操作数组 理解基本的JavaScript类型 使用基本类型...
    大学一百阅读 3,226评论 0 4
  • JavaScript编码规范 1 前言 [2 代码风格] [2.1 文件] [2.2 结构] [2.2.1 缩进]...
    忆飞阅读 1,156评论 1 2
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,644评论 18 139
  • 人间岁月不饶人, 低唱情歌两三首。 往事回首物依旧, 时光昔人却白头。
    书南Q阅读 301评论 1 9