Zving前端开发规范(JS部分)

最后修改2017-04-07

目录

引言

软件的长期价值直接源于其编码质量,清晰易理解的代码是编码质量的重要一环,代码应该写成可供他人容易阅读的。

编写目的

本文档旨在为zving前端开发提供基本规范,保持页面规范,避免版本管理(git/svn)中开发人员因代码风格不同而产生的非功能性的冲突,提高前端开发人员协作开发的水平。同时减少页面错误、提高系统健壮性。

使用环境及对象

本规范适用基于浏览器的B/S版软件项目开发工作。适用于目前正在进行开发的系统(ZCMS,ZOA,ZShop)的前/后台页面。本规范不约束第三方库(如jQuery、React)。

适用对象包括:

  • UI设计人员;
  • 开发编码人员;
  • 模板编写人员。

JS开发编辑器

建议使用Visual Studio Code作主要的JS开发编辑器,Atom、Sublime Text、VIM、WebStorm 等带有JS linter、JS Formatter插件的编辑也是可以的,不要使用UltraEdit、EditPlus、Notepad++、Dreamweaver等没有JS linter、JS Formatter支持的编辑器。
特别注意我们约定使用 JavaScript Standard Style(Standard JS风格规范),所以在使用以下编辑器时要安装 Standard 插件。在多人协作时使用同一个代码格式化工具可以杜绝因代码风格不同导致的svn版本冲突,在有必要使用是JS代码格式化的情况下务必统一使用 prettier-standard-formatter 进行JS代码格式化

Visual Studio Code

下载地址 https://code.visualstudio.com/
安装JavaScript Standard Style插件:vscode-standardjs
参考 https://marketplace.visualstudio.com/items?itemName=chenxsan.vscode-standardjs
安装Prettier-Standard - JavaScript formatter插件:prettier-standard-vscode
参考 https://marketplace.visualstudio.com/items?itemName=numso.prettier-standard-vscode

Atom

下载地址 https://atom.io/
安装JavaScript Standard Style插件:依次安装linter-js-standardstandard-formatter, prettier-standard-formatterstandardjs-snippets
参考
https://atom.io/packages/linter-js-standard
https://atom.io/packages/standard-formatter
https://atom.io/packages/prettier-standard-formatter
https://atom.io/packages/standardjs-snippets

Sublime Text

下载地址 http://www.sublimetext.com
安装JavaScript Standard Style插件:依次安装SublimeLinterSublime​Linter-contrib-standardStandardFormatStandardSnippets
参考
http://www.sublimelinter.com/en/latest/installation.html
https://packagecontrol.io/packages/SublimeLinter-contrib-standard
https://packagecontrol.io/packages/StandardFormat
https://packagecontrol.io/packages/StandardSnippets

VIM

下载地址 http://www.vim.org/
安装JavaScript Standard Style插件:安装Syntastic插件里面包含有Standard,或者安装spf13-vim插件包,里面包含有Syntastic插件,并且默认开启了Standard
参考
https://github.com/vim-syntastic/syntastic http://vim.spf13.com/
安装 https://github.com/dtinth/prettier-standard-formatter 提供的 CLI 工具
yarn global add prettier-standard-formatter
在.vimrc文件里配置js文件格式化工具
autocmd FileType javascript set formatprg=prettier-standard-formatter

WebStorm

下载地址 http://www.jetbrains.com/webstorm/ 可以直接在 IDE 中使用 standard。无需再安装JavaScript Standard Style插件。

编码

JS文件的“换行符类型”为“windows换行符-CR/LF”;编码为UTF-8(在需要与支持GBK的控件交互的情况下,使用GBK编码)

Javascript编码规范

我们约定使用Airbnb公司的JavaScript编码规范,参考网址 https://github.com/airbnb/javascript

JS风格一览表

结构 规则 备注
模块 首字母大写驼峰 不要使用多重语义(Never multiple words)
文件名 首字母大写驼峰 可带版本号或其它说明,如Dialog.compress.js
首字母大写驼峰
公有方法 首字母小写驼峰
公有属性 首字母小写驼峰
私有方法/属性 '_'+首字母小写驼峰
侵入型私有属性 '__'+首字母小写驼峰 侵入型私有属性是指在别的对象上偷偷添加,给自己用的私有属性。需要尽量避免本类属性
变量 首字母小写驼峰
常量 首字母小写驼峰或大写 (可以包含_)

编码规范目录

  1. 类型
  2. 对象
  3. 数组
  4. 字符串
  5. 函数
  6. 属性
  7. 变量
  8. 提升
  9. 比较运算符 & 等号
  10. 注释
  11. 空白
  12. 逗号
  13. 分号
  14. 类型转化
  15. 命名规则
  16. 存取器
  17. 构造函数
  18. 事件
  19. 模块
  20. jQuery

类型

  • 原始值: 存取直接作用于它自身。

    • string
    • number
    • boolean
    • null
    • undefined
    var foo = 1;
    var bar = foo;
    
    bar = 9;
    
    console.log(foo, bar); // => 1, 9
  • 复杂类型: 存取时作用于它自身值的引用。

    • object
    • array
    • function
    var foo = [1, 2];
    var bar = foo;
    
    bar[0] = 9;
    
    console.log(foo[0], bar[0]); // => 9, 9

↑ 回到编码规范目录

对象

  • 使用直接量创建对象。

    // bad
    var item = new Object();
    
    // good
    var item = {};
  • 不要使用保留字作为键名,它们在 IE8 下不工作。更多信息

    // bad
    var superman = {
      default: { clark: 'kent' },
      private: true
    };
    
    // good
    var superman = {
      defaults: { clark: 'kent' },
      hidden: true
    };
  • 使用同义词替换需要使用的保留字。

    // bad
    var superman = {
      class: 'alien'
    };
    
    // bad
    var superman = {
      klass: 'alien'
    };
    
    // good
    var superman = {
      type: 'alien'
    };

↑ 回到编码规范目录

数组

  • 使用直接量创建数组。

    // bad
    var items = new Array();
    
    // good
    var items = [];
  • 向数组增加元素时使用 Array#push 来替代直接赋值。

    var someStack = [];
    
    
    // bad
    someStack[someStack.length] = 'abracadabra';
    
    // good
    someStack.push('abracadabra');
  • 当你需要拷贝数组时,使用 Array#slice。jsPerf

    var len = items.length;
    var itemsCopy = [];
    var i;
    
    // bad
    for (i = 0; i < len; i++) {
      itemsCopy[i] = items[i];
    }
    
    // good
    itemsCopy = items.slice();
  • 使用 Array#slice 将类数组对象转换成数组。

    function trigger() {
      var args = Array.prototype.slice.call(arguments);
      ...
    }

↑ 回到编码规范目录

字符串

  • 使用单引号 '' 包裹字符串。

    // bad
    var name = "Bob Parr";
    
    // good
    var name = 'Bob Parr';
    
    // bad
    var fullName = "Bob " + this.lastName;
    
    // good
    var fullName = 'Bob ' + this.lastName;
  • 超过 100 个字符的字符串应该使用连接符写成多行。

  • 注:若过度使用,通过连接符连接的长字符串可能会影响性能。jsPerf & 讨论.

    // bad
    var errorMessage = 'This is a super long error that was thrown because of Batman. When you stop to think about how Batman had anything to do with this, you would get nowhere fast.';
    
    // bad
    var errorMessage = 'This is a super long error that was thrown because \
    of Batman. When you stop to think about how Batman had anything to do \
    with this, you would get nowhere \
    fast.';
    
    // good
    var errorMessage = 'This is a super long error that was thrown because ' +
      'of Batman. When you stop to think about how Batman had anything to do ' +
      'with this, you would get nowhere fast.';
  • 程序化生成的字符串使用 Array#join 连接而不是使用连接符。尤其是 IE 下:jsPerf.

    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;
    
    // bad
    function inbox(messages) {
      items = '<ul>';
    
      for (i = 0; i < length; i++) {
        items += '<li>' + messages[i].message + '</li>';
      }
    
      return items + '</ul>';
    }
    
    // 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>';
    }

↑ 回到编码规范目录

函数

  • 函数表达式:

    // 匿名函数表达式
    var anonymous = function() {
      return true;
    };
    
    // 命名函数表达式
    var named = function named() {
      return true;
    };
    
    // 立即调用的函数表达式(IIFE)
    (function () {
      console.log('Welcome to the Internet. Please follow me.');
    }());
  • 永远不要在一个非函数代码块(if、while 等)中声明一个函数,把那个函数赋给一个变量。浏览器允许你这么做,但它们的解析表现不一致。

  • 注: ECMA-262 把 定义为一组语句。函数声明不是语句。阅读对 ECMA-262 这个问题的说明

    // bad
    if (currentUser) {
      function test() {
        console.log('Nope.');
      }
    }
    
    // good
    var test;
    if (currentUser) {
      test = function test() {
        console.log('Yup.');
      };
    }
  • 永远不要把参数命名为 arguments。这将取代函数作用域内的 arguments 对象。

    // bad
    function nope(name, options, arguments) {
      // ...stuff...
    }
    
    // good
    function yup(name, options, args) {
      // ...stuff...
    }

↑ 回到编码规范目录

属性

  • 使用 . 来访问对象的属性。

    var luke = {
      jedi: true,
      age: 28
    };
    
    // bad
    var isJedi = luke['jedi'];
    
    // good
    var isJedi = luke.jedi;
  • 当通过变量访问属性时使用中括号 []

    var luke = {
      jedi: true,
      age: 28
    };
    
    function getProp(prop) {
      return luke[prop];
    }
    
    var isJedi = getProp('jedi');

↑ 回到编码规范目录

变量

  • 总是使用 var 来声明变量。不这么做将导致产生全局变量。我们要避免污染全局命名空间。

    // bad
    superPower = new SuperPower();
    
    // good
    var superPower = new SuperPower();
  • 使用 var 声明每一个变量。 这样做的好处是增加新变量将变的更加容易,而且你永远不用再担心调换错 ;,

    // bad
    var items = getItems(),
        goSportsTeam = true,
        dragonball = 'z';
    
    // bad
    // (跟上面的代码比较一下,看看哪里错了)
    var items = getItems(),
        goSportsTeam = true;
        dragonball = 'z';
    
    // good
    var items = getItems();
    var goSportsTeam = true;
    var dragonball = 'z';
  • 最后再声明未赋值的变量。当你需要引用前面的变量赋值时这将变的很有用。

    // bad
    var i, len, dragonball,
        items = getItems(),
        goSportsTeam = true;
    
    // bad
    var i;
    var items = getItems();
    var dragonball;
    var goSportsTeam = true;
    var len;
    
    // good
    var items = getItems();
    var goSportsTeam = true;
    var dragonball;
    var length;
    var i;
  • 在作用域顶部声明变量。这将帮你避免变量声明提升相关的问题。

    // bad
    function () {
      test();
      console.log('doing stuff..');
    
      //..other stuff..
    
      var name = getName();
    
      if (name === 'test') {
        return false;
      }
    
      return name;
    }
    
    // good
    function () {
      var name = getName();
    
      test();
      console.log('doing stuff..');
    
      //..other stuff..
    
      if (name === 'test') {
        return false;
      }
    
      return name;
    }
    
    // bad - 不必要的函数调用
    function () {
      var name = getName();
    
      if (!arguments.length) {
        return false;
      }
    
      this.setFirstName(name);
    
      return true;
    }
    
    // good
    function () {
      var name;
    
      if (!arguments.length) {
        return false;
      }
    
      name = getName();
      this.setFirstName(name);
    
      return true;
    }

↑ 回到编码规范目录

提升

  • 变量声明会提升至作用域顶部,但赋值不会。

    // 我们知道这样不能正常工作(假设这里没有名为 notDefined 的全局变量)
    function example() {
      console.log(notDefined); // => throws a ReferenceError
    }
    
    // 但由于变量声明提升的原因,在一个变量引用后再创建它的变量声明将可以正常工作。
    // 注:变量赋值为 `true` 不会提升。
    function example() {
      console.log(declaredButNotAssigned); // => undefined
      var declaredButNotAssigned = true;
    }
    
    // 解释器会把变量声明提升到作用域顶部,意味着我们的例子将被重写成:
    function example() {
      var declaredButNotAssigned;
      console.log(declaredButNotAssigned); // => undefined
      declaredButNotAssigned = true;
    }
  • 匿名函数表达式会提升它们的变量名,但不会提升函数的赋值。

    function example() {
      console.log(anonymous); // => undefined
    
      anonymous(); // => TypeError anonymous is not a function
    
      var anonymous = function () {
        console.log('anonymous function expression');
      };
    }
  • 命名函数表达式会提升变量名,但不会提升函数名或函数体。

    function example() {
      console.log(named); // => undefined
    
      named(); // => TypeError named is not a function
    
      superPower(); // => ReferenceError superPower is not defined
    
      var named = function superPower() {
        console.log('Flying');
      };
    }
    
    // 当函数名跟变量名一样时,表现也是如此。
    function example() {
      console.log(named); // => undefined
    
      named(); // => TypeError named is not a function
    
      var named = function named() {
        console.log('named');
      }
    }
  • 函数声明提升它们的名字和函数体。

    function example() {
      superPower(); // => Flying
    
      function superPower() {
        console.log('Flying');
      }
    }
  • 了解更多信息在 JavaScript Scoping & Hoisting by Ben Cherry.

↑ 回到编码规范目录

比较运算符 & 等号

  • 优先使用 ===!== 而不是 ==!=.
  • 条件表达式例如 if 语句通过抽象方法 ToBoolean 强制计算它们的表达式并且总是遵守下面的规则:

    • 对象 被计算为 true
    • Undefined 被计算为 false
    • Null 被计算为 false
    • 布尔值 被计算为 布尔的值
    • 数字 如果是 +0、-0 或 NaN 被计算为 false,否则为 true
    • 字符串 如果是空字符串 '' 被计算为 false,否则为 true
    if ([0]) {
      // true
      // 一个数组就是一个对象,对象被计算为 true
    }
  • 使用快捷方式。

    // bad
    if (name !== '') {
      // ...stuff...
    }
    
    // good
    if (name) {
      // ...stuff...
    }
    
    // bad
    if (collection.length > 0) {
      // ...stuff...
    }
    
    // good
    if (collection.length) {
      // ...stuff...
    }
  • 了解更多信息在 Truth Equality and JavaScript by Angus Croll.

↑ 回到编码规范目录

  • 使用大括号包裹所有的多行代码块。

    // bad
    if (test)
      return false;
    
    // good
    if (test) return false;
    
    // good
    if (test) {
      return false;
    }
    
    // bad
    function () { return false; }
    
    // good
    function () {
      return false;
    }
  • 如果通过 ifelse 使用多行代码块,把 else 放在 if 代码块关闭括号的同一行。

    // bad
    if (test) {
      thing1();
      thing2();
    }
    else {
      thing3();
    }
    
    // good
    if (test) {
      thing1();
      thing2();
    } else {
      thing3();
    }

↑ 回到编码规范目录

注释

  • 鼓励按JsDoc的注释规范来写注释,以便可以通过JsDoc等工具自动生成API文档。
  • JS组件的开头要写上此文档所实现的功能及实现思路(使用多行注释),保证非原作者也可以接手开发工作。
  • 重要代码后的注释主要是说明代码段的用处,疑难代码后的注释对逻辑进行解释。

  • 使用 /** ... */ 作为多行注释。包含描述、指定所有参数和返回值的类型和值。

    // bad
    // make() returns a new element
    // based on the passed in tag name
    //
    // @param {String} tag
    // @return {Element} element
    function make(tag) {
    
      // ...stuff...
    
      return element;
    }
    
    // good
    /**
     * make() returns a new element
     * based on the passed in tag name
     *
     * @param {String} tag
     * @return {Element} element
     */
    function make(tag) {
    
      // ...stuff...
    
      return element;
    }
  • 使用 // 作为单行注释。在评论对象上面另起一行使用单行注释。在注释前插入空行。

    // bad
    var active = true;  // is current tab
    
    // good
    // is current tab
    var active = true;
    
    // bad
    function getType() {
      console.log('fetching type...');
      // set the default type to 'no type'
      var type = this.type || 'no type';
    
      return type;
    }
    
    // good
    function getType() {
      console.log('fetching type...');
    
      // set the default type to 'no type'
      var type = this.type || 'no type';
    
      return type;
    }
  • 给注释增加 FIXMETODO 的前缀可以帮助其他开发者快速了解这是一个需要复查的问题,或是给需要实现的功能提供一个解决方式。这将有别于常见的注释,因为它们是可操作的。使用 FIXME -- need to figure this out 或者 TODO -- need to implement

  • 使用 // FIXME: 标注问题。

    function Calculator() {
    
      // FIXME: shouldn't use a global here
      total = 0;
    
      return this;
    }
  • 使用 // TODO: 标注问题的解决方式。

    function Calculator() {
    
      // TODO: total should be configurable by an options param
      this.total = 0;
    
      return this;
    }

↑ 回到编码规范目录

空白

  • 使用 2 个空格作为缩进。不要制表位和空格混合使用。

    // bad
    function () {
    ∙∙∙∙var name;
    }
    
    // bad
    function () {
    ∙var name;
    }
    
    // good
    function () {
    ∙∙var name;
    }
  • 在大括号前放一个空格。

    // bad
    function test(){
      console.log('test');
    }
    
    // good
    function test() {
      console.log('test');
    }
    
    // bad
    dog.set('attr',{
      age: '1 year',
      breed: 'Bernese Mountain Dog'
    });
    
    // good
    dog.set('attr', {
      age: '1 year',
      breed: 'Bernese Mountain Dog'
    });
  • 在控制语句(ifwhile 等)的小括号前放一个空格。在函数调用及声明中,不在函数的参数列表前加空格。

    // bad
    if(isJedi) {
      fight ();
    }
    
    // good
    if (isJedi) {
      fight();
    }
    
    // bad
    function fight () {
      console.log ('Swooosh!');
    }
    
    // good
    function fight() {
      console.log('Swooosh!');
    }
  • 使用空格把运算符隔开。

    // bad
    var x=y+5;
    
    // good
    var x = y + 5;
  • 在文件末尾插入一个空行。

    // bad
    (function (global) {
      // ...stuff...
    })(this);
    // bad
    (function (global) {
      // ...stuff...
    })(this);↵
    ↵
    // good
    (function (global) {
      // ...stuff...
    })(this);↵
  • 在使用长方法链时进行缩进。使用前面的点 . 强调这是方法调用而不是新语句。

    // bad
    $('#items').find('.selected').highlight().end().find('.open').updateCount();
    
    // bad
    $('#items').
      find('.selected').
        highlight().
        end().
      find('.open').
        updateCount();
    
    // good
    $('#items')
      .find('.selected')
        .highlight()
        .end()
      .find('.open')
        .updateCount();
    
    // bad
    var leds = stage.selectAll('.led').data(data).enter().append('svg:svg').classed('led', true)
        .attr('width', (radius + margin) * 2).append('svg:g')
        .attr('transform', 'translate(' + (radius + margin) + ',' + (radius + margin) + ')')
        .call(tron.led);
    
    // good
    var leds = stage.selectAll('.led')
        .data(data)
      .enter().append('svg:svg')
        .classed('led', true)
        .attr('width', (radius + margin) * 2)
      .append('svg:g')
        .attr('transform', 'translate(' + (radius + margin) + ',' + (radius + margin) + ')')
        .call(tron.led);
  • 在块末和新语句前插入空行。

    // bad
    if (foo) {
      return bar;
    }
    return baz;
    
    // good
    if (foo) {
      return bar;
    }
    
    return baz;
    
    // bad
    var obj = {
      foo: function () {
      },
      bar: function () {
      }
    };
    return obj;
    
    // good
    var obj = {
      foo: function () {
      },
    
      bar: function () {
      }
    };
    
    return obj;

↑ 回到编码规范目录

逗号

  • 行首逗号: 不需要

    // bad
    var story = [
        once
      , upon
      , aTime
    ];
    
    // good
    var story = [
      once,
      upon,
      aTime
    ];
    
    // bad
    var hero = {
        firstName: 'Bob'
      , lastName: 'Parr'
      , heroName: 'Mr. Incredible'
      , superPower: 'strength'
    };
    
    // good
    var hero = {
      firstName: 'Bob',
      lastName: 'Parr',
      heroName: 'Mr. Incredible',
      superPower: 'strength'
    };
  • 额外的行末逗号:不需要。这样做会在 IE6/7 和 IE9 怪异模式下引起问题。同样,多余的逗号在某些 ES3 的实现里会增加数组的长度。在 ES5 中已经澄清了 (source):

    Edition 5 clarifies the fact that a trailing comma at the end of an ArrayInitialiser does not add to the length of the array. This is not a semantic change from Edition 3 but some implementations may have previously misinterpreted this.

      // bad
      var hero = {
        firstName: 'Kevin',
        lastName: 'Flynn',
      };
    
      var heroes = [
        'Batman',
        'Superman',
      ];
    
      // good
      var hero = {
        firstName: 'Kevin',
        lastName: 'Flynn'
      };
    
      var heroes = [
        'Batman',
        'Superman'
      ];

↑ 回到编码规范目录

分号

  • 使用分号。

    // bad
    (function () {
      var name = 'Skywalker'
      return name
    })()
    
    // good
    (function () {
      var name = 'Skywalker';
      return name;
    })();
    
    // good (防止函数在两个 IIFE 合并时被当成一个参数
    ;(function () {
      var name = 'Skywalker';
      return name;
    })();

    了解更多.

↑ 回到编码规范目录

类型转换

  • 在语句开始时执行类型转换。
  • 字符串:

    //  => this.reviewScore = 9;
    
    // bad
    var totalScore = this.reviewScore + '';
    
    // good
    var totalScore = '' + this.reviewScore;
    
    // bad
    var totalScore = '' + this.reviewScore + ' total score';
    
    // good
    var totalScore = this.reviewScore + ' total score';
  • 使用 parseInt 转换数字时总是带上类型转换的基数。

    var inputValue = '4';
    
    // bad
    var val = new Number(inputValue);
    
    // bad
    var val = +inputValue;
    
    // bad
    var val = inputValue >> 0;
    
    // bad
    var val = parseInt(inputValue);
    
    // good
    var val = Number(inputValue);
    
    // good
    var val = parseInt(inputValue, 10);
  • 如果因为某些原因 parseInt 成为你所做的事的瓶颈而需要使用位操作解决性能问题时,留个注释说清楚原因和你的目的。

    // good
    /**
     * parseInt was the reason my code was slow.
     * Bitshifting the String to coerce it to a
     * Number made it a lot faster.
     */
    var val = inputValue >> 0;
  • 注: 小心使用位操作运算符。数字会被当成 64 位值,但是位操作运算符总是返回 32 位的整数(source)。位操作处理大于 32 位的整数值时还会导致意料之外的行为。讨论。最大的 32 位整数是 2,147,483,647:

    2147483647 >> 0 //=> 2147483647
    2147483648 >> 0 //=> -2147483648
    2147483649 >> 0 //=> -2147483647
  • 布尔:

    var age = 0;
    
    // bad
    var hasAge = new Boolean(age);
    
    // good
    var hasAge = Boolean(age);
    
    // good
    var hasAge = !!age;

↑ 回到编码规范目录

命名规则

  • 所有的变量名必须使用英文名称,不要使用拼音。
  • 方法的命令 必须 为动词或者是动词短语。

    // bad
    obj.value();
    
    // good
    obj.getSomeValue()
  • 不要使用下划线前/后缀

    为什么?JavaScript 并没有私有属性或私有方法的概念。虽然使用下划线是表示「私有」的一种共识,但实际上这些属性是完全公开的,它本身就是你公共接口的一部分。这种习惯或许会导致开发者错误的认为改动它不会造成破坏或者不需要去测试。长话短说:如果你想要某处为「私有」,它必须不能是显式提出的。

  • 允许使用下划线前缀来暗示私有变量,建议使用闭包来限制对私有变量的访问。

  • 循环语句中计数变量建议使用 "i", "j", "k" (依次类推),其他情况下避免单字母命名。常用单词可进行缩写,如element或缩写为elem/ele/el,作用域越大的变量越要避免使用缩写;

  • 使用有意义的英文单词作变量名,命名应具备描述性,清楚的表达其自身的属性,避免字义含糊不清。

    // bad
    var mseEvtHdlr;
    function q() {
      // ...stuff...
    }
    
    // good
    var mouseEventHandler;
    function query() {
      // ..stuff..
    }
  • 使用驼峰式命名对象、函数和实例。

    // bad
    var OBJEcttsssss = {};
    var this_is_my_object = {};
    var o = {};
    function c() {}
    
    // good
    var thisIsMyObject = {};
    function thisIsMyFunction() {}
  • 使用帕斯卡式命名构造函数或类。

    // bad
    function user(options) {
      this.name = options.name;
    }
    
    var bad = new user({
      name: 'nope'
    });
    
    // good
    function User(options) {
      this.name = options.name;
    }
    
    var good = new User({
      name: 'yup'
    });
  • 不要保存 this 的引用。使用 Function#bind。

    // 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);
      };
    }
    
    // good
    function () {
      return function () {
        console.log(this);
      }.bind(this);
    }
  • 给函数命名。这在做堆栈轨迹时很有帮助。

    // bad
    var log = function (msg) {
      console.log(msg);
    };
    
    // good
    var log = function log(msg) {
      console.log(msg);
    };
  • 注: IE8 及以下版本对命名函数表达式的处理有些怪异。了解更多信息到 http://kangax.github.io/nfe/

  • 如果你的文件导出一个类,你的文件名应该与类名完全相同。

    // file contents
    class CheckBox {
      // ...
    }
    module.exports = CheckBox;
    
    // in some other file
    // bad
    var CheckBox = require('./checkBox');
    
    // bad
    var CheckBox = require('./check_box');
    
    // good
    var CheckBox = require('./CheckBox');

↑ 回到编码规范目录

存取器

  • 属性的存取函数不是必须的。
  • 如果你需要存取函数时使用 getVal()setVal('hello')

    // bad
    dragon.age();
    
    // good
    dragon.getAge();
    
    // bad
    dragon.age(25);
    
    // good
    dragon.setAge(25);
  • 如果属性是布尔值,使用 isVal()hasVal()

    // bad
    if (!dragon.age()) {
      return false;
    }
    
    // good
    if (!dragon.hasAge()) {
      return false;
    }
  • 创建 get() 和 set() 函数是可以的,但要保持一致。

    function Jedi(options) {
      options || (options = {});
      var lightsaber = options.lightsaber || 'blue';
      this.set('lightsaber', lightsaber);
    }
    
    Jedi.prototype.set = function set(key, val) {
      this[key] = val;
    };
    
    Jedi.prototype.get = function get(key) {
      return this[key];
    };

↑ 回到编码规范目录

构造函数

  • 给对象原型分配方法,而不是使用一个新对象覆盖原型。覆盖原型将导致继承出现问题:重设原型将覆盖原有原型!

    function Jedi() {
      console.log('new jedi');
    }
    
    // bad
    Jedi.prototype = {
      fight: function fight() {
        console.log('fighting');
      },
    
      block: function block() {
        console.log('blocking');
      }
    };
    
    // good
    Jedi.prototype.fight = function fight() {
      console.log('fighting');
    };
    
    Jedi.prototype.block = function block() {
      console.log('blocking');
    };
  • 方法可以返回 this 来实现方法链式使用。

    // bad
    Jedi.prototype.jump = function jump() {
      this.jumping = true;
      return true;
    };
    
    Jedi.prototype.setHeight = function setHeight(height) {
      this.height = height;
    };
    
    var luke = new Jedi();
    luke.jump(); // => true
    luke.setHeight(20); // => undefined
    
    // good
    Jedi.prototype.jump = function jump() {
      this.jumping = true;
      return this;
    };
    
    Jedi.prototype.setHeight = function setHeight(height) {
      this.height = height;
      return this;
    };
    
    var luke = new Jedi();
    
    luke.jump()
      .setHeight(20);
  • 写一个自定义的 toString() 方法是可以的,但是确保它可以正常工作且不会产生副作用。

    function Jedi(options) {
      options || (options = {});
      this.name = options.name || 'no name';
    }
    
    Jedi.prototype.getName = function getName() {
      return this.name;
    };
    
    Jedi.prototype.toString = function toString() {
      return 'Jedi - ' + this.getName();
    };

↑ 回到编码规范目录

事件

  • 当给事件附加数据时(无论是 DOM 事件还是私有事件),传入一个哈希而不是原始值。这样可以让后面的贡献者增加更多数据到事件数据而无需找出并更新事件的每一个处理器。例如,不好的写法:

    // bad
    $(this).trigger('listingUpdated', listing.id);
    
    ...
    
    $(this).on('listingUpdated', function (e, listingId) {
      // do something with listingId
    });

    更好的写法:

    // good
    $(this).trigger('listingUpdated', { listingId : listing.id });
    
    ...
    
    $(this).on('listingUpdated', function (e, data) {
      // do something with data.listingId
    });

    ↑ 回到编码规范目录

模块

  • 模块应该以 ! 开始。这样确保了当一个不好的模块忘记包含最后的分号时,在合并代码到生产环境后不会产生错误。详细说明
  • 文件应该以驼峰式命名,并放在同名的文件夹里,且与导出的名字一致。
  • 增加一个名为 noConflict() 的方法来设置导出的模块为前一个版本并返回它。
  • 永远在模块顶部声明 'use strict';

    // fancyInput/fancyInput.js
    
    !function (global) {
      'use strict';
    
      var previousFancyInput = global.FancyInput;
    
      function FancyInput(options) {
        this.options = options || {};
      }
    
      FancyInput.noConflict = function noConflict() {
        global.FancyInput = previousFancyInput;
        return FancyInput;
      };
    
      global.FancyInput = FancyInput;
    }(this);

↑ 回到编码规范目录

jQuery

  • 使用 $ 作为存储 jQuery 对象的变量名前缀。

    // bad
    var sidebar = $('.sidebar');
    
    // good
    var $sidebar = $('.sidebar');
  • 缓存 jQuery 查询。

    // bad
    function setSidebar() {
      $('.sidebar').hide();
    
      // ...stuff...
    
      $('.sidebar').css({
        'background-color': 'pink'
      });
    }
    
    // good
    function setSidebar() {
      var $sidebar = $('.sidebar');
      $sidebar.hide();
    
      // ...stuff...
    
      $sidebar.css({
        'background-color': 'pink'
      });
    }
  • 对 DOM 查询使用层叠 $('.sidebar ul') 或 父元素 > 子元素 $('.sidebar > ul')jsPerf

  • 对有作用域的 jQuery 对象查询使用 find

    // bad
    $('ul', '.sidebar').hide();
    
    // bad
    $('.sidebar').find('ul').hide();
    
    // good
    $('.sidebar ul').hide();
    
    // good
    $('.sidebar > ul').hide();
    
    // good
    $sidebar.find('ul').hide();

↑ 回到编码规范目录

其他注意事项

ECMAScript 5 兼容性

参考 Kangax 的 ES5 兼容表
ES5的严格模式下,以下情况下会显式的报错;所以要避免以下写法。

  • 对未定义的变量赋值

  • 操作被设置为不可写,不可配置或不可扩充的属性

  • 删除变量,函数,参数

  • 在对象直接量里重复定义属性

  • eval做关键字,在eval的字符串里定义变量

  • 覆写arguments

  • 使用arguments.callerarguments.callee(匿名函数必须具名才能引用自己)

  • (function(){}).call( null ); // Exception

  • 使用with

特别注意

  • 禁止扩展原生对象的原型,特别是Object.prototype

  • 尽量不要在if的条件部分使用赋值表达式,如果确实想要在if条件部分赋值,那么把它括在另一对括号中,如:if ((a = b)) { ... }

  • parseInt的第一个参数的第一个字符是0时,该字符串将被基于八进制而不是十进制求值。所以使用parseInt方法时,建议总是提供第二个参数来指明基于十进制求值。

  • 因为javascript遵循二进制浮点数算术标准,所以不能正确地处理十进制的小数,因此在js中 0.1+0.2 不等于 0.3。但js中的整数运算是精确的,所以在需要精确浮点运算的时候,建议可以先乘以10的阶乘,使用整数部分运算,运算完后再除以10的阶乘。

其他建议

  • 使用{}代替 new Object(),使用[] 代替 new Array()

  • 某些情况下使用||&&代替if,如 evt = evt || window.event

  • 避免在ifwhile语句的条件部分赋值。如 if(evt=window.event){}

鼓励

  • 使用命名空间来管理所有代码中的对象和变量

  • 使用闭包来隐藏临时变量、私有变量、私有方法。、

  • 建议在类型确定时使用===!==运算符代替==!=运算符。注意在ie下window!==window.parent

基础类及工具类开发流程

  • 主要参考Extjs、Kendo UI、dojo,因为这几个js框架都有比较多的组件:考虑了复杂的应用情况,同时也有较多的项目应用,我们需要的应用其中上都可以在这两个js框架中找到切实可行的解决方案。

  • 调研同类组件,整理成文档,分析列举出各种同类组件的优劣。主要参考Extjs、Kendo UI、dojo,因为这几个js框架都有比较多的组件:考虑了复杂的应用情况,同时也有较多的项目应用,我们需要的应用其中上都可以在这两个js框架中找到切实可行的解决方案。

  • 控件的表现,要参考Material UI,这几个JS框架比较新,并且注重在不同终端下的易用性。

  • 确定组件要实现的功能,确定好组件的 public api.:

  • 代码上,精简小巧,越小越好。功能上,遵循8/2原则,用 20% 的代码,开发出 80% 的常用功能即可,扩展上,适度灵活,提供扩展接口,让用户可以通过扩展,完成不常用的 20% 功能

  • 组件可以从已存在的DOM元素进行初始化,也可以纯配置初始化生成DOM。

  • 要有响应式特性,考虑在手机上的使用。

hackIE