【译】JavaScript最全编码规范

类型

基本类型:访问基本类型时,应该直接操作类型值

  • string
  • number
  • boolean
  • null
  • undefined
javascriptvar 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 = [];
  • 添加数组元素时,使用push而不是直接添加
var someStack = [];


// bad
someStack[someStack.length] = 'abracadabra';

// good
someStack.push('abracadabra');
  • 需要复制数组时,可以使用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();
  • 使用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;
  • 超过80个字符的字符串应该使用字符串连接符进行跨行
  • 注意:对长字符串过度使用连接符将会影响性能。相关的文章和主题讨论: jsPerf & Discussion.
// 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 = '

    '; for (i = 0; i < length; i++) { items += '
  • ' + messages[i].message + '
  • '; } return items + '
'; } // good function inbox(messages) { items = []; for (i = 0; i < length; i++) { items[i] = '
  • ' + messages[i].message + '
  • '; } return '
      ' + items.join('') + '
    '; }

    函数

    • 函数表达式
    // anonymous function expression
    var anonymous = function() {
      return true;
    };
    
    // named function expression
    var named = function named() {
      return true;
    };
    
    // immediately-invoked function expression (IIFE)
    (function() {
      console.log('Welcome to the Internet. Please follow me.');
    })();
    
    • 不要在非函数块中(if, while, etc)声明函数,尽管浏览器允许你分配函数给一个变量,但坏消息是,不同的浏览器用不同的方式解析它
    • 注意:ECMA-262把块定义为一组语句,但函数声明不是一个语句:Read ECMA-262’s note
      on this issue.
    // 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声明每个变量,这样很容易添加新的变量声明,而不用去担心用a;替换a,
    // bad
    var items = getItems(),
        goSportsTeam = true,
        dragonball = 'z';
    
    // bad
    // (compare to above, and try to spot the mistake)
    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;
      }
    
      return true;
    }
    
    // good
    function() {
      if (!arguments.length) {
        return false;
      }
    
      var name = getName();
    
      return true;
    }
    

    声明提升

    • 变量声明是在作用域的顶端,但是赋值没有
    // we know this wouldn't work (assuming there
    // is no notDefined global variable)
    function example() {
      console.log(notDefined); // => throws a ReferenceError
    }
    
    // creating a variable declaration after you
    // reference the variable will work due to
    // variable hoisting. Note: the assignment
    // value of `true` is not hoisted.
    function example() {
      console.log(declaredButNotAssigned); // => undefined
      var declaredButNotAssigned = true;
    }
    
    // The interpreter is hoisting the variable
    // declaration to the top of the scope,
    // which means our example could be rewritten as:
    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');
      };
    }
    
    // the same is true when the function name
    // is the same as the variable name.
    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.

    比较运算符&相等

    • 使用===和!==代替==和!=
    • 比较运算符进行计算时会利用ToBoolean方法进行强制转换数据类型,并遵从一下规则
      >Objects的计算值是true
      >Undefined的计算值是false
      >Boolean的计算值是boolean的值
      >Numbers如果是-0,+0或者NaN,则计算值是false,反之是true
      >Strings如果是空,则计算值是false,反之是true
    if ([0]) {
      // true
      // An array is an object, objects evaluate to true
    }
    
    • 使用快捷方式
    // bad
    if (name !== '') {
      // ...stuff...
    }
    
    // good
    if (name) {
      // ...stuff...
    }
    
    // bad
    if (collection.length > 0) {
      // ...stuff...
    }
    
    // good
    if (collection.length) {
      // ...stuff...
    }
    

    语句块

    • 对多行的语句块使用大括号
    // bad
    if (test)
      return false;
    
    // good
    if (test) return false;
    
    // good
    if (test) {
      return false;
    }
    
    // bad
    function() { return false; }
    
    // good
    function() {
      return false;
    }
    
    • 对于使用if和else的多行语句块,把else和if语句块的右大括号放在同一行
    // bad
    if (test) {
      thing1();
      thing2();
    }
    else {
      thing3();
    }
    
    // good
    if (test) {
      thing1();
      thing2();
    } else {
      thing3();
    }
    

    注释

    • 多行注释使用/** … */,需包含一个描述、所有参数的具体类型和值以及返回值
    // 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;
    }
    
    • 如果你指出的问题需要重新定位或者提出一个待解决的问题需要实现,给注释添加FIXME or TODO 前缀有利于其他开发者快速理解。这些注释不同于通常的注释,因为它们是可实施的。这些实施措施就是FIXME -- need to figure this out or 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;
    }
    

    空白

    • 使用软制表符设置两个空格
    // 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'
    });
    
    • 在控制语句中(if, while etc),左括号之前留一个空格。函数的参数列表之前不要有空格
    // 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、IE7和IE9的怪异模式中导致一些问题;同时,在ES3的一些实现中,多余的逗号会增加数组的长度。在ES5中已经澄清(source)
     // 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 (guards against the function becoming an argument when two files with IIFEs are concatenated)
    ;(function() {
      var name = 'Skywalker';
      return name;
    })();
    

    阅读更多

    类型分配&强制转换

    • 执行强制类型转换的语句。
    Strings:
    //  => 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对Numbers进行转换,并带一个进制作为参数
    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;
    
    • 注意:当使用位运算时,Numbers被视为64位值,但是位运算总是返回32位整型(source)。对于整型值大于32位的进行位运算将导致不可预见的行为。Discussion.最大的有符号32位整数是2,147,483,647
    2147483647 >> 0 //=> 2147483647
    2147483648 >> 0 //=> -2147483648
    2147483649 >> 0 //=> -2147483647
    Booleans:
    var age = 0;
    
    // bad
    var hasAge = new Boolean(age);
    
    // good
    var hasAge = Boolean(age);
    
    // good
    var hasAge = !!age;
    

    命名规范

    • 避免单字母名称,让名称具有描述性
    // bad
    function q() {
      // ...stuff...
    }
    
    // good
    function query() {
      // ..stuff..
    }
    当命名对象、函数和实例时使用骆驼拼写法
    // bad
    var OBJEcttsssss = {};
    var this_is_my_object = {};
    function c() {}
    var u = new user({
      name: 'Bob Parr'
    });
    
    // good
    var thisIsMyObject = {};
    function thisIsMyFunction() {}
    var user = new User({
      name: 'Bob Parr'
    });
    
    • 当命名构造函数或类名时,使用驼峰式写法
    // 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'
    });
    
    • 命名私有属性时使用前置下划线
    // bad
    this.__firstName__ = 'Panda';
    this.firstName_ = 'Panda';
    
    // good
    this._firstName = 'Panda';
    保存this引用时使用_this
    // bad
    function() {
      var self = this;
      return function() {
        console.log(self);
      };
    }
    
    // bad
    function() {
      var that = this;
      return function() {
        console.log(that);
      };
    }
    
    // good
    function() {
      var _this = this;
      return function() {
        console.log(_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);
    
    • 如果属性时boolean,格式应为isVal() or hasVal().
    // bad
    if (!dragon.age()) {
      return false;
    }
    
    // good
    if (!dragon.hasAge()) {
      return false;
    }
    
    • 创建get() and set()函数时不错的想法,但是要保持一致
    function Jedi(options) {
      options || (options = {});
      var lightsaber = options.lightsaber || 'blue';
      this.set('lightsaber', lightsaber);
    }
    
    Jedi.prototype.set = function(key, val) {
      this[key] = val;
    };
    
    Jedi.prototype.get = function(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() {
      this.jumping = true;
      return true;
    };
    
    Jedi.prototype.setHeight = function(height) {
      this.height = height;
    };
    
    var luke = new Jedi();
    luke.jump(); // => true
    luke.setHeight(20); // => undefined
    
    // good
    Jedi.prototype.jump = function() {
      this.jumping = true;
      return this;
    };
    
    Jedi.prototype.setHeight = function(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事件还是如Backbone一样拥有的私有事件),应传递散列对象而不是原始值,这可以让随后的贡献者给事件对象添加更多的数据,而不必去查找或者更新每一个事件处理程序。举个粟子,不要用下面的方式:
    // 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
    });
    

    模块

    • 模块应该以 ! 开始,这能确保当脚本连接时,如果畸形模块忘记导入,包括最后一个分号,不会产生错误。Explanation
    • 文件应该以驼峰式命名,放在同名的文件夹中,和单出口的名称相匹配
    • 定义一个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'
      });
    }
    
    • 使用级联$(‘.sidebar ul’)或父子$(‘.sidebar > ul’)选择器进行DOM查询。jsPerf
    • 在范围内使用find进行jQuery对象查询
    // bad
    $('ul', '.sidebar').hide();
    
    // bad
    $('.sidebar').find('ul').hide();
    
    // good
    $('.sidebar ul').hide();
    
    // good
    $('.sidebar > ul').hide();
    
    // good
    $sidebar.find('ul').hide();
    

    译文出处:http://www.ido321.com/1520.html

    你可能感兴趣的:(【译】JavaScript最全编码规范)