Skip to content

hbxeagle/JavaScriptStyleGuide

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

8 Commits
 
 
 
 

Repository files navigation

JavaScriptStyleGuide

javascript代码规范 create by 不下 2015.07.21

###本代码规范参照idiomatic.js、Airbnb JavaScript Style Guide和谷歌代码规范,结合自己的经验编写而成,以供参考。

Sublime Text 插件

jsFormat 方便开发者美化javascript代码。

Object

使用如下语法创建Object对象

// bad
var item = new Object();
var item2 = new Object();
item.a = 0;
item.b = 1;
item.c = 2;
item['strange key'] = 3;

// good
var item = {};
var item2 = {
  a: 0,
  b: 1,
  c: 2,
  'strange key': 3
};

不要使用保留关键字作为key,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'
};

Array

使用如下语法创建数组

// bad
// Length is 3.
var a1 = new Array(x1, x2, x3);

// Length is 2.
var a2 = new Array(x1, x2);

// 如果 x1 是一个数字,并且是个自然数,那么所创建的数组的长度为 x1。
// 如果 x1 是一个数字,但不是个自然色,则会抛出一个异常。
// 其他情况下,会创建一个长度为 1 ,元素为 x1的数组。
var a3 = new Array(x1);

// Length is 0.
var a4 = new Array();

// good
var a = [x1, x2, x3];
var a2 = [x1, x2];
var a3 = [x1];
var a4 = [];

使用数组的push方法为数组添加新的元素。

var someStack = [];


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

// good
someStack.push('abracadabra');

当你需要拷贝一个数组时,请使用数组方法slice()

var len = items.length;
var itemsCopy = [];
var i;

// bad
for (i = 0; i < len; i++) {
  itemsCopy[i] = items[i];
}

// good
itemsCopy = items.slice();

将一个类数组对象转制为数组,也可以使用数组方法slice()

var len = items.length;
var itemsCopy = [];
var i;

// bad
for (i = 0; i < len; i++) {
  itemsCopy[i] = items[i];
}

// good
itemsCopy = items.slice();

Strings

使用单引号定义字符串

// bad
var name = "Bob Parr";

// good
var name = 'Bob Parr';

// bad
var fullName = "Bob " + this.lastName;

// good
var fullName = 'Bob ' + this.lastName;

字符串长度超过80个字符是,应该分成多行,通过字符串连接符进行连接。
注:如果过度使用连接符,长字符串拼接会有性能问题。

// 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.';

如果是需要用程序生成字符串,可以使用数组方法join替代连接符,尤其是在IE中。

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>';
}

Functions

定义 Function:

// 匿名 function 表达式
var anonymous = function() {
  return true;
};

// 命名 function 表达式
var named = function named() {
  return true;
};

// 立即执行 function 表达式 (IIFE: immediately-invoked function expression)
(function() {
  console.log('Welcome to the Internet. Please follow me.');
})();

绝对不要在“非功能”块中申明方法(如,if、while等),而是通过将方法赋值给一个变量实现。浏览器会允许你这么做,但是他们会有各自的解析。
注: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...
}

Properties

使用 . 访问对象的属性

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');

变量 Variables

申明变量时,必须使用 var 。如果不这么做,所申明的变量将会是一个全局变量,我们要劲量避免申明全局变量。

// bad
superPower = new SuperPower();

// good
var superPower = new SuperPower();

使用一个 var 打头申明一组变量,或者每个变量都用一个 var 申明都可以,不做强求。

// bad
// (compare to above, and try to spot the mistake)
var items = getItems(),
    goSportsTeam = true;
    dragonball = 'z';


// good
var items = getItems(),
    goSportsTeam = true,
    dragonball = 'z';

// good
var items = getItems();
var goSportsTeam = true;
var dragonball = 'z';

在作用域的顶部申明变量,这样有主意避免变量申明和赋值提升相关的问题。

// 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 - unnecessary function call
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;
}

比较操作符

优先使用 === 和 !== 替换 == 和 !=

  • 像if这样的条件判断语句,会遵循下面简单的规则,将表达式转换为布尔型的值。
    • Objects 等价于 true
    • Undefined 等价于 false
    • Null 等价于 false
    • Booleans 等价于 对应的布尔值
    • Numbers 如果是 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();
}

将for循环中的变量提升到上面进行定义

// bad
for ( i = 0; i < 100; i++ ) {
  // 语句
}

// normal
for ( var i = 0; i < 100; i++ ) {
  // 语句
}

// good
var i,
  length = 100;

for ( i = 0; i < length; i++ ) {
  // 语句
}

// good
var i = 0,
  length = 100;

for ( ; i < length; i++ ) {
  // 语句
}

// good
var prop;
for ( prop in object ) {
  // 语句
}

注释

多行注释使用 javascript/** ... */ 注释内容包括描述,所有参数、返回值的具体类型和值

// 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;
}

使用 javascript// 进行单行注释。注释时,在需要注释的语句上面新起一行,并在注释的上面保留一行空行。

// 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;
}

使用前缀 javascriptFIXME 和 TODO 标注注释,方便开发人员快速理解需要解决的问题,或着提出解决的建议。

// 使用 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;
}

空格

永远都不要混用空格和Tab

  • 编辑中设置两个空格代替Tab
  • 总是打开 “显示不可见字符” 这个设置
// 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等语句的 ( 前放置一个空格。命名方法、调用方法时,不需要这么做。

// 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
$('#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 quirksmode会引发错误。同样的在一些执行ES3标准的浏览器中,多余的逗号会使数组的长度增加。这个问题在ES5中已经修复。

// 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 (两个立即执行的函数文件拼接时,在函数前面添加一个分号,防止后面的函数变成前面函数的参数)
;(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转换成数字是需要标注出进制。

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函数成为的你的瓶颈,出于性能的考虑需要使用Bitshift,则需要写好注释解释为什么你这么做。

// 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;

注:在使用Bitshifting时要注意,当转换一个表示64位的数字时,由于Bitshifting总是返回一个32位的数字,会导致转换错误。

'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
function q() {
  // ...stuff...
}

// good
function query() {
  // ..stuff..
}

使用全字母大写,下划线分隔的方式命名不变量。不要使用 const 关键词命名常量,ie不支持。

// bad
const md5_key = 'ya23adfuodf4sd';

// good
var MD5_key = 'ya23adfuodf4sd';

使用驼峰命名法(camelCase),命名你的对象、方法和实例

// bad
var OBJEcttsssss = {};
var this_is_my_object = {};
var o = {};
function c() {}

// good
var thisIsMyObject = {};
function thisIsMyFunction() {}

正则表达式变量使用 r 作为前缀

// good
rNumber = /(\d)+/;

使用帕斯卡命名法(PascalCase),命名你的构造函数或类

// 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);
  };
}

属性存取函数

属性存取函数不是必须的。 如果你需要使用属性存取函数,请用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;
}

构造函数

分配而不是覆盖新对象的prototype。覆盖prototype会导致不能继承:通过复位prototype,会重写base的prototype 属性要定义在构造函数中,函数定义在prototype上

// jedi.js
(function(global){
  function Jedi(foo) {
    console.log('new jedi');
    // good
    this.foo = foo;
    return this;
  }

  // bad
  Jedi.prototype.foo = 'foo';

  // 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');
  };

  // To call constructor's without `new`, you might do this:
  var jedi = function( foo ) {
    return new Jedi( foo );
  };


  // expose our constructor to the global object
  global.jedi = jedi;

})(this)

函数可以返回 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('ulo');

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();
};

Modules

// module.js
(function( global ) {
  var Module = (function() {

    var data = "secret";

    return {
      // This is some boolean property
      bool: true,
      // Some string value
      string: "a string",
      // An array property
      array: [ 1, 2, 3, 4 ],
      // An object property
      object: {
        lang: "en-Us"
      },
      getData: function() {
        // get the current value of `data`
        return data;
      },
      setData: function( value ) {
        // set the value of `data` and return it
        return ( data = value );
      }
    };
  })();

  // Other things might happen here

  // expose our module to the global object
  global.Module = Module;

})( this );

Events

当把数据传递给event时(无论是DOM event或者是自定义 event),传递的值都用哈希值代替。这样即使以后会新增加数据字段,也不用更新每个事件处理函数。

// 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
});

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') 或者 parent > child $('.sidebar > ul') 需要调用作用域内的已经缓存的jQuery对象进行查询时,使用 find

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

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

// good
$('.sidebar ul').hide();

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

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

闭包

闭包,也许是js中最有用和最经常被忽略的功能。
但是,有一点要特别注意,闭包会在它封闭的作用域中保存变量引用。其结果是,附带着会创建DOM对象的循环引用,从而产生内存泄露。 例如下面的代码:

// bad
function foo(element, a, b) {
  element.onclick = function() { /* uses a and b */ };
}

在这个匿名方法中,闭包保存了element、a和b的引用,即使它不会用到element。我们构建了一个闭环,导致不会被垃圾清理回收。 在这种情况下,可以做如下重构:

// good
function foo(element, a, b) {
  element.onclick = bar(a, b);
}

function bar(a, b) {
  return function() { /* uses a and b */ };
}

delete

在现代的js引擎中,修改对象的元素个数比重新分配对应的值要慢的多。
所以要尽量避免使用delete删除键,除非有必须删除一个键,如在迭代器中,或者是for in循环中。

// bad
Foo.prototype.dispose = function() {
  delete this.property_;
};

// good
Foo.prototype.dispose = function() {
  this.property_ = null;
};

eval

仅在代码加载器或者REPL(交互式的开发环境)中使用eval,通常情况下使用JSON.parse替代eval
例如,如果从服务端返回如下字符串

{
  "name": "Alice",
  "id": 31502,
  "email": "[email protected]"
}
// bad
var userInfo = eval(feed);
var email = userInfo['email'];

// good
var userInfo = JSON.parse(feed);
var email = userInfo['email'];

with(){}

不要使用with,因为: 在with语句中的函数定义和变量初始化可能产生令人惊讶,和直觉相抵触的行为。
使用with语句速度要比不使用with语句的等价代码的速度慢得多。
90%(或者更高比例)的with应用场景都可以用其他更好的方式代替

参考

idiomatic.js
Airbnb JavaScript Style Guide
google javascriptguide

About

JS代码规范

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published