[关闭]
@xiaoqq 2016-07-21T10:49:53.000000Z 字数 21510 阅读 1232

JavaScript编程规范

JavaScript 编程规范


一. JavaScript编程规范

类型

基本类型

访问基本类型时,应该直接操作类型值 string number boolean null undefined

  1. var foo = 1;
  2. var bar = foo;
  3. bar = 9;
  4. console.log(foo, bar); // => 1, 9

复合类型

访问复合类型时,应该操作其引用 object array function

  1. var foo = [1, 2];
  2. var bar = foo;
  3. bar[0] = 9;
  4. console.log(foo[0], bar[0]); // => 9, 9

对象

使用字面量语法创建对象

  1. // bad
  2. var item = new Object();
  3. // good
  4. var item = {};

不要使用保留字,在IE8中不起作用,更多相关信息

  1. // bad
  2. var superman = {
  3. default: { clark: 'kent' },
  4. private: true
  5. };
  6. // good
  7. var superman = {
  8. defaults: { clark: 'kent' },
  9. hidden: true
  10. };

使用易读的同义词代替保留字

  1. // bad
  2. var superman = {
  3. class: 'alien'
  4. };
  5. // bad
  6. var superman = {
  7. klass: 'alien'
  8. };
  9. // good
  10. var superman = {
  11. type: 'alien'
  12. };

数组

使用字面量语法创建数组

  1. // bad
  2. var items = new Array();
  3. // good
  4. var items = [];

添加数组元素时,使用push而不是直接添加

  1. var someStack = [];
  2. // bad
  3. someStack[someStack.length] = 'abracadabra';
  4. // good
  5. someStack.push('abracadabra');

需要复制数组时,可以使用slice,jsPerf的相关文章

  1. var len = items.length;
  2. var itemsCopy = [];
  3. var i;
  4. // bad
  5. for (i = 0; i < len; i++) {
  6. itemsCopy[i] = items[i];
  7. }
  8. // good
  9. itemsCopy = items.slice();

使用slice将类数组对象转为数组

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

字符串

对字符串使用单引号

  1. // bad
  2. var name = "Bob Parr";
  3. // good
  4. var name = 'Bob Parr';
  5. // bad
  6. var fullName = "Bob " + this.lastName;
  7. // good
  8. var fullName = 'Bob ' + this.lastName;

超过80个字符的字符串应该使用字符串连接符进行跨行

注意:对长字符串过度使用连接符将会影响性能。相关的文章和主题讨论: jsPerf & Discussion.

  1. // bad
  2. 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.';
  3. // bad
  4. var errorMessage = 'This is a super long error that was thrown because \
  5. of Batman. When you stop to think about how Batman had anything to do \
  6. with this, you would get nowhere \
  7. fast.';
  8. // good
  9. var errorMessage = 'This is a super long error that was thrown because ' +
  10. 'of Batman. When you stop to think about how Batman had anything to do ' +
  11. 'with this, you would get nowhere fast.';

以编程方式创建字符串的时应该使用Array的join方法而不是通过连接符,尤其是在IE中:jsPerf.

  1. var items;
  2. var messages;
  3. var length;
  4. var i;
  5. messages = [{
  6. state: 'success',
  7. message: 'This one worked.'
  8. }, {
  9. state: 'success',
  10. message: 'This one worked as well.'
  11. }, {
  12. state: 'error',
  13. message: 'This one did not work.'
  14. }];
  15. length = messages.length;
  16. // bad
  17. function inbox(messages) {
  18. items = '<ul>';
  19. for (i = 0; i < length; i++) {
  20. items += '<li>' + messages[i].message + '</li>';
  21. }
  22. return items + '</ul>';
  23. }
  24. // good
  25. function inbox(messages) {
  26. items = [];
  27. for (i = 0; i < length; i++) {
  28. items[i] = '<li>' + messages[i].message + '</li>';
  29. }
  30. return '<ul>' + items.join('') + '</ul>';
  31. }

函数

函数表达式

  1. // anonymous function expression
  2. var anonymous = function() {
  3. return true;
  4. };
  5. // named function expression
  6. var named = function named() {
  7. return true;
  8. };
  9. // immediately-invoked function expression (IIFE)
  10. (function() {
  11. console.log('Welcome to the Internet. Please follow me.');
  12. })();

不要在非函数块中(if, while, etc)声明函数,尽管浏览器允许你分配函数给一个变量,但坏消息是,不同的浏览器用不同的方式解析它

注意:ECMA-262把块定义为一组语句,但函数声明不是一个语句:Read ECMA-262’s note on this issue.

  1. // bad
  2. if (currentUser) {
  3. function test() {
  4. console.log('Nope.');
  5. }
  6. }
  7. // good
  8. var test;
  9. if (currentUser) {
  10. test = function test() {
  11. console.log('Yup.');
  12. };
  13. }

不要命名一个参数为arguments,否则它将优先于传递给每个函数作用域中的arguments对象,

  1. // bad
  2. function nope(name, options, arguments) {
  3. // ...stuff...
  4. }
  5. // good
  6. function yup(name, options, args) {
  7. // ...stuff...
  8. }

属性

使用点表示法访问属性

  1. var luke = {
  2. jedi: true,
  3. age: 28
  4. };
  5. // bad
  6. var isJedi = luke['jedi'];
  7. // good
  8. var isJedi = luke.jedi;

用变量访问属性时要使用下标表示法([])

  1. var luke = {
  2. jedi: true,
  3. age: 28
  4. };
  5. function getProp(prop) {
  6. return luke[prop];
  7. }
  8. var isJedi = getProp('jedi');

变量

总是使用var声明变量,不然其将变为全局变量。我们要想办法避免全局空间污染

  1. // bad
  2. superPower = new SuperPower();
  3. // good
  4. var superPower = new SuperPower();

使用var声明每个变量,这样很容易添加新的变量声明,而不用去担心用a;替换a,

  1. // bad
  2. var items = getItems(),
  3. goSportsTeam = true,
  4. dragonball = 'z';
  5. // bad
  6. // (compare to above, and try to spot the mistake)
  7. var items = getItems(),
  8. goSportsTeam = true;
  9. dragonball = 'z';
  10. // good
  11. var items = getItems();
  12. var goSportsTeam = true;
  13. var dragonball = 'z';

最后声明未赋值的变量,这对于你需要根据之前已经赋值的变量对一个变量进行赋值时是很有帮助的

  1. // bad
  2. var i, len, dragonball,
  3. items = getItems(),
  4. goSportsTeam = true;
  5. // bad
  6. var i;
  7. var items = getItems();
  8. var dragonball;
  9. var goSportsTeam = true;
  10. var len;
  11. // good
  12. var items = getItems();
  13. var goSportsTeam = true;
  14. var dragonball;
  15. var length;
  16. var i;

在作用域顶端对变量赋值,这有助于避免变量声明问题和与声明提升相关的问题

  1. // bad
  2. function() {
  3. test();
  4. console.log('doing stuff..');
  5. //..other stuff..
  6. var name = getName();
  7. if (name === 'test') {
  8. return false;
  9. }
  10. return name;
  11. }
  12. // good
  13. function() {
  14. var name = getName();
  15. test();
  16. console.log('doing stuff..');
  17. //..other stuff..
  18. if (name === 'test') {
  19. return false;
  20. }
  21. return name;
  22. }
  23. // bad
  24. function() {
  25. var name = getName();
  26. if (!arguments.length) {
  27. return false;
  28. }
  29. return true;
  30. }
  31. // good
  32. function() {
  33. if (!arguments.length) {
  34. return false;
  35. }
  36. var name = getName();
  37. return true;
  38. }

! 声明提升

Javascript会自动将变量声明"提升"(hoist)到代码块(block)的头部。

  1.   if (!o) {
  2.     var o = {};
  3.   }

等同于

  1.   var o;
  2.   if (!o) {
  3.     o = {};
  4.   }

为了避免可能出现的问题,不如把变量声明都放在代码块的头部。

  1.   for (var i ...) {...}

最好写成:

  1.   var i;
  2.   for (i ...) {...,}

因此,
  所有变量声明都放在函数的头部。
  所有函数都在使用之前定义。

变量声明是在作用域的顶端,但是赋值没有

  1. // we know this wouldn't work (assuming there
  2. // is no notDefined global variable)
  3. function example() {
  4. console.log(notDefined); // => throws a ReferenceError
  5. }
  6. // creating a variable declaration after you
  7. // reference the variable will work due to
  8. // variable hoisting. Note: the assignment
  9. // value of `true` is not hoisted.
  10. function example() {
  11. console.log(declaredButNotAssigned); // => undefined
  12. var declaredButNotAssigned = true;
  13. }
  14. // The interpreter is hoisting the variable
  15. // declaration to the top of the scope,
  16. // which means our example could be rewritten as:
  17. function example() {
  18. var declaredButNotAssigned;
  19. console.log(declaredButNotAssigned); // => undefined
  20. declaredButNotAssigned = true;
  21. }

匿名表达式能提升他们的变量名,但不能提升函数赋值

  1. function example() {
  2. console.log(anonymous); // => undefined
  3. anonymous(); // => TypeError anonymous is not a function
  4. var anonymous = function() {
  5. console.log('anonymous function expression');
  6. };
  7. }

命名函数表达式会提升变量名,而不是函数名或者函数体

  1. function example() {
  2. console.log(named); // => undefined
  3. named(); // => TypeError named is not a function
  4. superPower(); // => ReferenceError superPower is not defined
  5. var named = function superPower() {
  6. console.log('Flying');
  7. };
  8. }
  9. // the same is true when the function name
  10. // is the same as the variable name.
  11. function example() {
  12. console.log(named); // => undefined
  13. named(); // => TypeError named is not a function
  14. var named = function named() {
  15. console.log('named');
  16. }
  17. }

函数声明会提升变量名和函数体

  1. function example() {
  2. superPower(); // => Flying
  3. function superPower() {
  4. console.log('Flying');
  5. }
  6. }

更多信息指引:JavaScript Scoping & Hoisting by Ben Cherry.

比较运算符&相等

使用===和!==代替==和!=
比较运算符进行计算时会利用ToBoolean方法进行强制转换数据类型,并遵从一下规则
Objects的计算值是true
Undefined的计算值是false
Boolean的计算值是boolean的值
Numbers如果是-0,+0或者NaN,则计算值是false,反之是true
Strings如果是空,则计算值是false,反之是true

  1. if ([0]) {
  2. // true
  3. // An array is an object, objects evaluate to true
  4. }

使用快捷方式

  1. // bad
  2. if (name !== '') {
  3. // ...stuff...
  4. }
  5. // good
  6. if (name) {
  7. // ...stuff...
  8. }
  9. // bad
  10. if (collection.length > 0) {
  11. // ...stuff...
  12. }
  13. // good
  14. if (collection.length) {
  15. // ...stuff...
  16. }

语句块

对多行的语句块使用大括号

  1. // bad
  2. if (test)
  3. return false;
  4. // good
  5. if (test) return false;
  6. // good
  7. if (test) {
  8. return false;
  9. }
  10. // bad
  11. function() { return false; }
  12. // good
  13. function() {
  14. return false;
  15. }

对于使用if和else的多行语句块,把else和if语句块的右大括号放在同一行

  1. // bad
  2. if (test) {
  3. thing1();
  4. thing2();
  5. }
  6. else {
  7. thing3();
  8. }
  9. // good
  10. if (test) {
  11. thing1();
  12. thing2();
  13. } else {
  14. thing3();
  15. }

注释

多行注释使用/** … */,需包含一个描述、所有参数的具体类型和值以及返回值

  1. // bad
  2. // make() returns a new element
  3. // based on the passed in tag name
  4. //
  5. // @param {String} tag
  6. // @return {Element} element
  7. function make(tag) {
  8. // ...stuff...
  9. return element;
  10. }
  11. // good
  12. /**
  13. * make() returns a new element
  14. * based on the passed in tag name
  15. *
  16. * @param {String} tag
  17. * @return {Element} element
  18. */
  19. function make(tag) {
  20. // ...stuff...
  21. return element;
  22. }

单行注释使用//,把单行注释放在语句的上一行,并且在注释之前空一行

  1. // bad
  2. var active = true; // is current tab
  3. // good
  4. // is current tab
  5. var active = true;
  6. // bad
  7. function getType() {
  8. console.log('fetching type...');
  9. // set the default type to 'no type'
  10. var type = this._type || 'no type';
  11. return type;
  12. }
  13. // good
  14. function getType() {
  15. console.log('fetching type...');
  16. // set the default type to 'no type'
  17. var type = this._type || 'no type';
  18. return type;
  19. }

如果你指出的问题需要重新定位或者提出一个待解决的问题需要实现,给注释添加FIXME or TODO 前缀有利于其他开发者快速理解。这些注释不同于通常的注释,因为它们是可实施的。这些实施措施就是FIXME -- need to figure this out or TODO -- need to implement.
使用// FIXME:给一个问题作注释

  1. function Calculator() {
  2. // FIXME: shouldn't use a global here
  3. total = 0;
  4. return this;
  5. }

使用//TODO:给问题解决方案作注释

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

空白

使用软制表符设置两个空格

  1. // bad
  2. function() {
  3. ∙∙∙∙var name;
  4. }
  5. // bad
  6. function() {
  7. var name;
  8. }
  9. // good
  10. function() {
  11. ∙∙var name;
  12. }

在左大括号之前留一个空格

  1. // bad
  2. function test(){
  3. console.log('test');
  4. }
  5. // good
  6. function test() {
  7. console.log('test');
  8. }
  9. // bad
  10. dog.set('attr',{
  11. age: '1 year',
  12. breed: 'Bernese Mountain Dog'
  13. });
  14. // good
  15. dog.set('attr', {
  16. age: '1 year',
  17. breed: 'Bernese Mountain Dog'
  18. });

在控制语句中(if, while etc),左括号之前留一个空格。函数的参数列表之前不要有空格

  1. // bad
  2. if(isJedi) {
  3. fight ();
  4. }
  5. // good
  6. if (isJedi) {
  7. fight();
  8. }
  9. // bad
  10. function fight () {
  11. console.log ('Swooosh!');
  12. }
  13. // good
  14. function fight() {
  15. console.log('Swooosh!');
  16. }

用空白分隔运算符

  1. // bad
  2. var x=y+5;
  3. // good
  4. var x = y + 5;

用一个换行符结束文件

  1. // bad
  2. (function(global) {
  3. // ...stuff...
  4. })(this);
  5. // bad
  6. (function(global) {
  7. // ...stuff...
  8. })(this);↵
  9. // good
  10. (function(global) {
  11. // ...stuff...
  12. })(this);↵

当调用很长的方法链时使用缩进,可以强调这行是方法调用,不是新的语句

  1. // bad
  2. $('#items').find('.selected').highlight().end().find('.open').updateCount();
  3. // bad
  4. $('#items').
  5. find('.selected').
  6. highlight().
  7. end().
  8. find('.open').
  9. updateCount();
  10. // good
  11. $('#items')
  12. .find('.selected')
  13. .highlight()
  14. .end()
  15. .find('.open')
  16. .updateCount();
  17. // bad
  18. var leds = stage.selectAll('.led').data(data).enter().append('svg:svg').classed('led', true)
  19. .attr('width', (radius + margin) * 2).append('svg:g')
  20. .attr('transform', 'translate(' + (radius + margin) + ',' + (radius + margin) + ')')
  21. .call(tron.led);
  22. // good
  23. var leds = stage.selectAll('.led')
  24. .data(data)
  25. .enter().append('svg:svg')
  26. .classed('led', true)
  27. .attr('width', (radius + margin) * 2)
  28. .append('svg:g')
  29. .attr('transform', 'translate(' + (radius + margin) + ',' + (radius + margin) + ')')
  30. .call(tron.led);

在语句块和下一个语句之前留一个空行

  1. // bad
  2. if (foo) {
  3. return bar;
  4. }
  5. return baz;
  6. // good
  7. if (foo) {
  8. return bar;
  9. }
  10. return baz;
  11. // bad
  12. var obj = {
  13. foo: function() {
  14. },
  15. bar: function() {
  16. }
  17. };
  18. return obj;
  19. // good
  20. var obj = {
  21. foo: function() {
  22. },
  23. bar: function() {
  24. }
  25. };
  26. return obj;

逗号

不要在语句前留逗号

  1. // bad
  2. var story = [
  3. once
  4. , upon
  5. , aTime
  6. ];
  7. // good
  8. var story = [
  9. once,
  10. upon,
  11. aTime
  12. ];
  13. // bad
  14. var hero = {
  15. firstName: 'Bob'
  16. , lastName: 'Parr'
  17. , heroName: 'Mr. Incredible'
  18. , superPower: 'strength'
  19. };
  20. // good
  21. var hero = {
  22. firstName: 'Bob',
  23. lastName: 'Parr',
  24. heroName: 'Mr. Incredible',
  25. superPower: 'strength'
  26. };

不要有多余逗号:这会在IE6、IE7和IE9的怪异模式中导致一些问题;同时,在ES3的一些实现中,多余的逗号会增加数组的长度。在ES5中已经澄清(source)

  1. // bad
  2. var hero = {
  3. firstName: 'Kevin',
  4. lastName: 'Flynn',
  5. };
  6. var heroes = [
  7. 'Batman',
  8. 'Superman',
  9. ];
  10. // good
  11. var hero = {
  12. firstName: 'Kevin',
  13. lastName: 'Flynn'
  14. };
  15. var heroes = [
  16. 'Batman',
  17. 'Superman'
  18. ];

分号

恩,这也是规范一部分

  1. // bad
  2. (function() {
  3. var name = 'Skywalker'
  4. return name
  5. })()
  6. // good
  7. (function() {
  8. var name = 'Skywalker';
  9. return name;
  10. })();
  11. // good (guards against the function becoming an argument when two files with IIFEs are concatenated)
  12. ;(function() {
  13. var name = 'Skywalker';
  14. return name;
  15. })();

阅读更多

类型分配&强制转换

执行强制类型转换的语句。
Strings:

  1. // => this.reviewScore = 9;
  2. // bad
  3. var totalScore = this.reviewScore + '';
  4. // good
  5. var totalScore = '' + this.reviewScore;
  6. // bad
  7. var totalScore = '' + this.reviewScore + ' total score';
  8. // good
  9. var totalScore = this.reviewScore + ' total score';

使用parseInt对Numbers进行转换,并带一个进制作为参数

  1. var inputValue = '4';
  2. // bad
  3. var val = new Number(inputValue);
  4. // bad
  5. var val = +inputValue;
  6. // bad
  7. var val = inputValue >> 0;
  8. // bad
  9. var val = parseInt(inputValue);
  10. // good
  11. var val = Number(inputValue);
  12. // good
  13. var val = parseInt(inputValue, 10);

无论出于什么原因,或许你做了一些”粗野”的事;或许parseInt成了你的瓶颈;或许考虑到性能,需要使用位运算,都要用注释说明你为什么这么做

  1. // good
  2. /**
  3. * parseInt was the reason my code was slow.
  4. * Bitshifting the String to coerce it to a
  5. * Number made it a lot faster.
  6. */
  7. 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:

  1. var age = 0;
  2. // bad
  3. var hasAge = new Boolean(age);
  4. // good
  5. var hasAge = Boolean(age);
  6. // good
  7. var hasAge = !!age;

命名规范

避免单字母名称,让名称具有描述性

  1. // bad
  2. function q() {
  3. // ...stuff...
  4. }
  5. // good
  6. function query() {
  7. // ..stuff..
  8. }

当命名对象、函数和实例时使用骆驼拼写法

  1. // bad
  2. var OBJEcttsssss = {};
  3. var this_is_my_object = {};
  4. function c() {}
  5. var u = new user({
  6. name: 'Bob Parr'
  7. });
  8. // good
  9. var thisIsMyObject = {};
  10. function thisIsMyFunction() {}
  11. var user = new User({
  12. name: 'Bob Parr'
  13. });

当命名构造函数或类名时,使用驼峰式写法

  1. // bad
  2. function user(options) {
  3. this.name = options.name;
  4. }
  5. var bad = new user({
  6. name: 'nope'
  7. });
  8. // good
  9. function User(options) {
  10. this.name = options.name;
  11. }
  12. var good = new User({
  13. name: 'yup'
  14. });

命名私有属性时使用前置下划线

  1. // bad
  2. this.__firstName__ = 'Panda';
  3. this.firstName_ = 'Panda';
  4. // good
  5. this._firstName = 'Panda';
  6. 保存this引用时使用_this
  7. // bad
  8. function() {
  9. var self = this;
  10. return function() {
  11. console.log(self);
  12. };
  13. }
  14. // bad
  15. function() {
  16. var that = this;
  17. return function() {
  18. console.log(that);
  19. };
  20. }
  21. // good
  22. function() {
  23. var _this = this;
  24. return function() {
  25. console.log(_this);
  26. };
  27. }

命名函数时,下面的方式有利于堆栈跟踪

  1. // bad
  2. var log = function(msg) {
  3. console.log(msg);
  4. };
  5. // good
  6. var log = function log(msg) {
  7. console.log(msg);
  8. };

注意:IE8和怪异模式下命名函数表示,戳此:http://kangax.github.io/nfe/
如果文件作为一个类被导出,文件名应该和类名保持一致

  1. // file contents
  2. class CheckBox {
  3. // ...
  4. }
  5. module.exports = CheckBox;
  6. // in some other file
  7. // bad
  8. var CheckBox = require('./checkBox');
  9. // bad
  10. var CheckBox = require('./check_box');
  11. // good
  12. var CheckBox = require('./CheckBox');

存取器

对于属性,访问器函数不是必须的
如果定义了存取器函数,应参照getVal() 和 setVal(‘hello’)格式.

  1. // bad
  2. dragon.age();
  3. // good
  4. dragon.getAge();
  5. // bad
  6. dragon.age(25);
  7. // good
  8. dragon.setAge(25);

如果属性时boolean,格式应为isVal() or hasVal().

  1. // bad
  2. if (!dragon.age()) {
  3. return false;
  4. }
  5. // good
  6. if (!dragon.hasAge()) {
  7. return false;
  8. }

创建get() and set()函数时不错的想法,但是要保持一致

  1. function Jedi(options) {
  2. options || (options = {});
  3. var lightsaber = options.lightsaber || 'blue';
  4. this.set('lightsaber', lightsaber);
  5. }
  6. Jedi.prototype.set = function(key, val) {
  7. this[key] = val;
  8. };
  9. Jedi.prototype.get = function(key) {
  10. return this[key];
  11. };

构造函数

在原型对象上定义方法,而不是用新对象重写它。重写使继承变为不可能:重置原型将重写整个基类

  1. function Jedi() {
  2. console.log('new jedi');
  3. }
  4. // bad
  5. Jedi.prototype = {
  6. fight: function fight() {
  7. console.log('fighting');
  8. },
  9. block: function block() {
  10. console.log('blocking');
  11. }
  12. };
  13. // good
  14. Jedi.prototype.fight = function fight() {
  15. console.log('fighting');
  16. };
  17. Jedi.prototype.block = function block() {
  18. console.log('blocking');
  19. };

方法应该返回this,有利于构成方法链

  1. // bad
  2. Jedi.prototype.jump = function() {
  3. this.jumping = true;
  4. return true;
  5. };
  6. Jedi.prototype.setHeight = function(height) {
  7. this.height = height;
  8. };
  9. var luke = new Jedi();
  10. luke.jump(); // => true
  11. luke.setHeight(20); // => undefined
  12. // good
  13. Jedi.prototype.jump = function() {
  14. this.jumping = true;
  15. return this;
  16. };
  17. Jedi.prototype.setHeight = function(height) {
  18. this.height = height;
  19. return this;
  20. };
  21. var luke = new Jedi();
  22. luke.jump()
  23. .setHeight(20);

写一个自定义的toString()方法是可以的,只要确保它能正常运行并且不会产生副作用

  1. function Jedi(options) {
  2. options || (options = {});
  3. this.name = options.name || 'no name';
  4. }
  5. Jedi.prototype.getName = function getName() {
  6. return this.name;
  7. };
  8. Jedi.prototype.toString = function toString() {
  9. return 'Jedi - ' + this.getName();
  10. };

事件

当在事件对象上附加数据时(无论是DOM事件还是如Backbone一样拥有的私有事件),应传递散列对象而不是原始值,这可以让随后的贡献者给事件对象添加更多的数据,而不必去查找或者更新每一个事件处理程序。举个粟子,不要用下面的方式:

  1. // bad
  2. $(this).trigger('listingUpdated', listing.id);
  3. ...
  4. $(this).on('listingUpdated', function(e, listingId) {
  5. // do something with listingId
  6. });

应该按如下方式:

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

模块

模块应该以 ! 开始,这能确保当脚本连接时,如果畸形模块忘记导入,包括最后一个分号,不会产生错误。Explanation
文件应该以驼峰式命名,放在同名的文件夹中,和单出口的名称相匹配
定义一个noConflict()方法来设置导出模块之前的版本,并返回当前版本。
在模块的顶部申明’use strict';

  1. // fancyInput/fancyInput.js
  2. !function(global) {
  3. 'use strict';
  4. var previousFancyInput = global.FancyInput;
  5. function FancyInput(options) {
  6. this.options = options || {};
  7. }
  8. FancyInput.noConflict = function noConflict() {
  9. global.FancyInput = previousFancyInput;
  10. return FancyInput;
  11. };
  12. global.FancyInput = FancyInput;
  13. }(this);

jQuery

jQuery对象变量使用前缀$

  1. // bad
  2. var sidebar = $('.sidebar');
  3. // good
  4. var $sidebar = $('.sidebar');

缓存jQuery查询

  1. // bad
  2. function setSidebar() {
  3. $('.sidebar').hide();
  4. // ...stuff...
  5. $('.sidebar').css({
  6. 'background-color': 'pink'
  7. });
  8. }
  9. // good
  10. function setSidebar() {
  11. var $sidebar = $('.sidebar');
  12. $sidebar.hide();
  13. // ...stuff...
  14. $sidebar.css({
  15. 'background-color': 'pink'
  16. });
  17. }

使用级联$('.sidebar ul')或父子$('.sidebar > ul;')选择器进行DOM查询。jsPerf

在范围内使用find进行jQuery对象查询

  1. // bad
  2. $('ul', '.sidebar').hide();
  3. // bad
  4. $('.sidebar').find('ul').hide();
  5. // good
  6. $('.sidebar ul').hide();
  7. // good
  8. $('.sidebar > ul').hide();
  9. // good
  10. $sidebar.find('ul').hide();

二. Google代码规范

常量

常量的形式如: NAMES_LIKE_THIS, 即使用大写字符, 并用下划线分隔. 你也可用 @const 标记来指明它是一个常量. 但请永远不要使用 const 关键词.
Decision:
对于基本类型的常量, 只需转换命名.

  1. /**
  2. * The number of seconds in a minute.
  3. * @type {number}
  4. */
  5. goog.example.SECONDS_IN_A_MINUTE = 60;

对于非基本类型, 使用 @const 标记.

  1. /**
  2. * The number of seconds in each of the given units.
  3. * @type {Object.<number>}
  4. * @const
  5. */
  6. goog.example.SECONDS_TABLE = {
  7. minute: 60,
  8. hour: 60 * 60,
  9. day: 60 * 60 * 24
  10. }

这标记告诉编译器它是常量.

至于关键词 const, 因为 IE 不能识别, 所以不要使用.

不要封装基本类型

[没有任何理由去封装基本类型, 另外还存在一些风险]:

  1. //bad x会被判断为真
  2. var x = new Boolean(false);
  3. if (x) {
  4. alert('hi'); // Shows 'hi'.
  5. }

除非明确用于类型转换, 其他情况请千万不要这样做!

  1. var x = Boolean(0);
  2. if (x) {
  3. alert('hi'); // This will never be alerted.
  4. }
  5. typeof Boolean(0) == 'boolean';
  6. typeof new Boolean(0) == 'object';

有时用作 number, string 或 boolean时, 类型的转换会非常实用.

多级原型结构

[不是首选]
多级原型结构是指 JavaScript 中的继承关系. 当你自定义一个D类, 且把B类作为其原型, 那么这就获得了一个多级原型结构. 这些原型结构会变得越来越复杂!

使用 the Closure 库 中的 goog.inherits() 或其他类似的用于继承的函数, 会是更好的选择.

  1. function D() {
  2. goog.base(this)
  3. }
  4. goog.inherits(D, B);
  5. D.prototype.method = function() {
  6. ...
  7. };

! 闭包

[可以, 但小心使用]
闭包也许是 JS 中最有用的特性了. 有一份比较好的介绍闭包原理的文档.

有一点需要牢记, 闭包保留了一个指向它封闭作用域的指针, 所以, 在给 DOM 元素附加闭包时, 很可能会产生循环引用, 进一步导致内存泄漏. 比如下面的代码:

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

这里, 即使没有使用 element, 闭包也保留了 element, a 和 b 的引用. 由于 element 也保留了对闭包的引用, 这就产生了循环引用, 这就不能被 GC 回收. 这种情况下, 可将代码重构为:

  1. //good
  2. function foo(element, a, b) {
  3. element.onclick = bar(a, b);
  4. }
  5. function bar(a, b) {
  6. return function() { /* uses a and b */ }
  7. }

this

[仅在对象构造器, 方法, 闭包中使用]
this 的语义很特别. 有时它引用

使用时很容易出错, 所以只有在下面两个情况时才能使用:

! for-in 循环

[只用于 object/map/hash 的遍历]
对 Array 用 for-in 循环有时会出错. 因为它并不是从 0 到 length - 1 进行遍历, 而是所有出现在对象及其原型链的键值. 下面就是一些失败的使用案例:

  1. //bad
  2. function printArray(arr) {
  3. for (var key in arr) {
  4. print(arr[key]);
  5. }
  6. }
  7. printArray([0,1,2,3]); // This works.
  8. var a = new Array(10);
  9. printArray(a); // This is wrong.
  10. a = document.getElementsByTagName('*');
  11. printArray(a); // This is wrong.
  12. a = [0,1,2,3];
  13. a.buhu = 'wine';
  14. printArray(a); // This is wrong again.
  15. a = new Array;
  16. a[3] = 3;
  17. printArray(a); // This is wrong again.

而遍历数组通常用最普通的 for 循环.

  1. function printArray(arr) {
  2. var l = arr.length;
  3. for (var i = 0; i < l; i++) {
  4. print(arr[i]);
  5. }
  6. }

关联数组

永远不要使用 Array 作为 map/hash/associative 数组.
数组中不允许使用非整型作为索引值, 所以也就不允许用关联数组. 而取代它使用 Object 来表示 map/hash 对象. Array 仅仅是扩展自 Object (类似于其他 JS 中的对象, 就像 Date, RegExp 和 String)一样来使用.

Array 和 Object 直接量

使用 Array 和 Object 语法, 而不使用 Array 和 Object 构造器.

使用 Array 构造器很容易因为传参不恰当导致错误.

  1. //bad
  2. // Length is 3.
  3. var a1 = new Array(x1, x2, x3);
  4. // Length is 2.
  5. var a2 = new Array(x1, x2);
  6. // If x1 is a number and it is a natural number the length will be x1.
  7. // If x1 is a number but not a natural number this will throw an exception.
  8. // Otherwise the array will have one element with x1 as its value.
  9. var a3 = new Array(x1);
  10. // Length is 0.
  11. var a4 = new Array();

如果传入一个参数而不是2个参数, 数组的长度很有可能就不是你期望的数值了.

为了避免这些歧义, 我们应该使用更易读的直接量来声明.

  1. //good
  2. var a = [x1, x2, x3];
  3. var a2 = [x1, x2];
  4. var a3 = [x1];
  5. var a4 = [];

虽然 Object 构造器没有上述类似的问题, 但鉴于可读性和一致性考虑, 最好还是在字面上更清晰地指明.

  1. //bad
  2. var o = new Object();
  3. var o2 = new Object();
  4. o2.a = 0;
  5. o2.b = 1;
  6. o2.c = 2;
  7. o2['strange key'] = 3;

应该写成:

  1. //good
  2. var o = {};
  3. var o2 = {
  4. a: 0,
  5. b: 1,
  6. c: 2,
  7. 'strange key': 3
  8. };

修改内置对象的原型

[不要]
千万不要修改内置对象, 如 Object.prototype 和 Array.prototype 的原型. 而修改内置对象, 如 Function.prototype 的原型, 虽然少危险些, 但仍会导致调试时的诡异现象. 所以也要避免修改其原型.

! 函数参数

尽量让函数参数在同一行上. 如果一行超过 80 字符, 每个参数独占一行, 并以4个空格缩进, 或者与括号对齐, 以提高可读性. 尽可能不要让每行超过80个字符. 比如下面这样:

  1. // Four-space, wrap at 80. Works with very long function names, survives
  2. // renaming without reindenting, low on space.
  3. goog.foo.bar.doThingThatIsVeryDifficultToExplain = function(
  4. veryDescriptiveArgumentNumberOne, veryDescriptiveArgumentTwo,
  5. tableModelEventHandlerProxy, artichokeDescriptorAdapterIterator) {
  6. // ...
  7. };
  8. // Four-space, one argument per line. Works with long function names,
  9. // survives renaming, and emphasizes each argument.
  10. goog.foo.bar.doThingThatIsVeryDifficultToExplain = function(
  11. veryDescriptiveArgumentNumberOne,
  12. veryDescriptiveArgumentTwo,
  13. tableModelEventHandlerProxy,
  14. artichokeDescriptorAdapterIterator) {
  15. // ...
  16. };
  17. // Parenthesis-aligned indentation, wrap at 80. Visually groups arguments,
  18. // low on space.
  19. function foo(veryDescriptiveArgumentNumberOne, veryDescriptiveArgumentTwo,
  20. tableModelEventHandlerProxy, artichokeDescriptorAdapterIterator) {
  21. // ...
  22. }
  23. // Parenthesis-aligned, one argument per line. Visually groups and
  24. // emphasizes each individual argument.
  25. function bar(veryDescriptiveArgumentNumberOne,
  26. veryDescriptiveArgumentTwo,
  27. tableModelEventHandlerProxy,
  28. artichokeDescriptorAdapterIterator) {
  29. // ...
  30. }

! 传递匿名函数

如果参数中有匿名函数, 函数体从调用该函数的左边开始缩进2个空格, 而不是从 function 这个关键字开始. 这让匿名函数更加易读 (不要增加很多没必要的缩进让函数体显示在屏幕的右侧).

  1. var names = items.map(function(item) {
  2. return item.name;
  3. });
  4. prefix.something.reallyLongFunctionName('whatever', function(a1, a2) {
  5. if (a1.equals(a2)) {
  6. someOtherLongFunctionName(a1);
  7. } else {
  8. andNowForSomethingCompletelyDifferent(a2.parrot);
  9. }
  10. });

更多的缩进

事实上, 除了 初始化数组和对象 , 和传递匿名函数外, 所有被拆开的多行文本要么选择与之前的表达式左对齐, 要么以4个(而不是2个)空格作为一缩进层次.

  1. someWonderfulHtml = '' +
  2. getEvenMoreHtml(someReallyInterestingValues, moreValues,

三. 其他补充

JavaScript 文件使用无 BOM 的 UTF-8 编码

解释:

UTF-8 编码具有更广泛的适应性。BOM 在使用程序或工具处理文件时可能造成不必要的干扰。

[建议] 在文件结尾处,保留一个空行。

空格

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

示例:

  1. var a = !arr.length;
  2. a++;
  3. a = b + c;

[强制] 用作代码块起始的左花括号 { 前必须有一个空格。

示例:

  1. // good
  2. if (condition) {
  3. }
  4. while (condition) {
  5. }
  6. function funcName() {
  7. }
  8. // bad
  9. if (condition){
  10. }
  11. while (condition){
  12. }
  13. function funcName(){
  14. }

[强制] if / else / for / while / function / switch / do / try / catch / finally 关键字后,必须有一个空格。

示例:

  1. // good
  2. if (condition) {
  3. }
  4. while (condition) {
  5. }
  6. (function () {
  7. })();
  8. // bad
  9. if(condition) {
  10. }
  11. while(condition) {
  12. }
  13. (function() {
  14. })();

[强制] 在对象创建时,属性中的 : 之后必须有空格,: 之前不允许有空格。

示例:

  1. // good
  2. var obj = {
  3. a: 1,
  4. b: 2,
  5. c: 3
  6. };
  7. // bad
  8. var obj = {
  9. a : 1,
  10. b:2,
  11. c :3
  12. };

[强制] 函数声明、具名函数表达式、函数调用中,函数名和 ( 之间不允许有空格。

示例:

  1. // good
  2. function funcName() {
  3. }
  4. var funcName = function funcName() {
  5. };
  6. funcName();
  7. // bad
  8. function funcName () {
  9. }
  10. var funcName = function funcName () {
  11. };
  12. funcName ();

[强制] ,; 前不允许有空格。

示例:

  1. // good
  2. callFunc(a, b);
  3. // bad
  4. callFunc(a , b) ;

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

示例:

  1. // good
  2. callFunc(param1, param2, param3);
  3. save(this.list[this.indexes[i]]);
  4. needIncream && (variable += increament);
  5. if (num > list.length) {
  6. }
  7. while (len--) {
  8. }
  9. // bad
  10. callFunc( param1, param2, param3 );
  11. save( this.list[ this.indexes[ i ] ] );
  12. needIncreament && ( variable += increament );
  13. if ( num > list.length ) {
  14. }
  15. while ( len-- ) {
  16. }

[强制] 单行声明的数组与对象,如果包含元素,{}[] 内紧贴括号部分不允许包含空格。

解释:

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

示例:

  1. // good
  2. var arr1 = [];
  3. var arr2 = [1, 2, 3];
  4. var obj1 = {};
  5. var obj2 = {name: 'obj'};
  6. var obj3 = {
  7. name: 'obj',
  8. age: 20,
  9. sex: 1
  10. };
  11. // bad
  12. var arr1 = [ ];
  13. var arr2 = [ 1, 2, 3 ];
  14. var obj1 = { };
  15. var obj2 = { name: 'obj' };
  16. var obj3 = {name: 'obj', age: 20, sex: 1};

[强制] 行尾不得有多余的空格。

类型转换

[建议] 转换成 string 时,使用 + ''

示例:

  1. // good
  2. num + '';
  3. // bad
  4. new String(num);
  5. num.toString();
  6. String(num);

[建议] 转换成 number 时,通常使用 +

示例:

  1. // good
  2. +str;
  3. // bad
  4. Number(str);

[建议] string 转换成 number,要转换的字符串结尾包含非数字并期望忽略时,使用 parseInt

示例:

  1. var width = '200px';
  2. parseInt(width, 10);

[强制] 使用 parseInt 时,必须指定进制。

示例:

  1. // good
  2. parseInt(str, 10);
  3. // bad
  4. parseInt(str);

[建议] 转换成 boolean 时,使用 !!

示例:

  1. var num = 3.14;
  2. !!num;

[建议] number 去除小数点,使用 Math.floor / Math.round / Math.ceil,不使用 parseInt

示例:

  1. // good
  2. var num = 3.14;
  3. Math.ceil(num);
  4. // bad
  5. var num = 3.14;
  6. parseInt(num, 10);

命名

[强制] 类名 使用 名词。
[建议] 函数名 使用 动宾短语。
[建议] boolean 类型的变量使用 is 或 has 开头。
[建议] Promise对象 用 动宾短语的进行时 表达。

示例:

  1. var loadingData = ajax.get('url');
  2. loadingData.then(callback);
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注