@xiaoqq
2016-07-21T10:49:53.000000Z
字数 21510
阅读 1305
JavaScript 编程规范
访问基本类型时,应该直接操作类型值 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
使用字面量语法创建对象
// badvar item = new Object();// goodvar item = {};
// badvar superman = {default: { clark: 'kent' },private: true};// goodvar superman = {defaults: { clark: 'kent' },hidden: true};
使用易读的同义词代替保留字
// badvar superman = {class: 'alien'};// badvar superman = {klass: 'alien'};// goodvar superman = {type: 'alien'};
使用字面量语法创建数组
// badvar items = new Array();// goodvar items = [];
添加数组元素时,使用push而不是直接添加
var someStack = [];// badsomeStack[someStack.length] = 'abracadabra';// goodsomeStack.push('abracadabra');
需要复制数组时,可以使用slice,jsPerf的相关文章
var len = items.length;var itemsCopy = [];var i;// badfor (i = 0; i < len; i++) {itemsCopy[i] = items[i];}// gooditemsCopy = items.slice();
使用slice将类数组对象转为数组
function trigger() {var args = Array.prototype.slice.call(arguments);...}
对字符串使用单引号
// badvar name = "Bob Parr";// goodvar name = 'Bob Parr';// badvar fullName = "Bob " + this.lastName;// goodvar fullName = 'Bob ' + this.lastName;
超过80个字符的字符串应该使用字符串连接符进行跨行
注意:对长字符串过度使用连接符将会影响性能。相关的文章和主题讨论: jsPerf & Discussion.
// badvar 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.';// badvar 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.';// goodvar 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;// badfunction inbox(messages) {items = '<ul>';for (i = 0; i < length; i++) {items += '<li>' + messages[i].message + '</li>';}return items + '</ul>';}// goodfunction inbox(messages) {items = [];for (i = 0; i < length; i++) {items[i] = '<li>' + messages[i].message + '</li>';}return '<ul>' + items.join('') + '</ul>';}
函数表达式
// anonymous function expressionvar anonymous = function() {return true;};// named function expressionvar 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.
// badif (currentUser) {function test() {console.log('Nope.');}}// goodvar test;if (currentUser) {test = function test() {console.log('Yup.');};}
不要命名一个参数为arguments,否则它将优先于传递给每个函数作用域中的arguments对象,
// badfunction nope(name, options, arguments) {// ...stuff...}// goodfunction yup(name, options, args) {// ...stuff...}
使用点表示法访问属性
var luke = {jedi: true,age: 28};// badvar isJedi = luke['jedi'];// goodvar isJedi = luke.jedi;
用变量访问属性时要使用下标表示法([])
var luke = {jedi: true,age: 28};function getProp(prop) {return luke[prop];}var isJedi = getProp('jedi');
总是使用var声明变量,不然其将变为全局变量。我们要想办法避免全局空间污染
// badsuperPower = new SuperPower();// goodvar superPower = new SuperPower();
使用var声明每个变量,这样很容易添加新的变量声明,而不用去担心用a;替换a,
// badvar items = getItems(),goSportsTeam = true,dragonball = 'z';// bad// (compare to above, and try to spot the mistake)var items = getItems(),goSportsTeam = true;dragonball = 'z';// goodvar items = getItems();var goSportsTeam = true;var dragonball = 'z';
最后声明未赋值的变量,这对于你需要根据之前已经赋值的变量对一个变量进行赋值时是很有帮助的
// badvar i, len, dragonball,items = getItems(),goSportsTeam = true;// badvar i;var items = getItems();var dragonball;var goSportsTeam = true;var len;// goodvar items = getItems();var goSportsTeam = true;var dragonball;var length;var i;
在作用域顶端对变量赋值,这有助于避免变量声明问题和与声明提升相关的问题
// badfunction() {test();console.log('doing stuff..');//..other stuff..var name = getName();if (name === 'test') {return false;}return name;}// goodfunction() {var name = getName();test();console.log('doing stuff..');//..other stuff..if (name === 'test') {return false;}return name;}// badfunction() {var name = getName();if (!arguments.length) {return false;}return true;}// goodfunction() {if (!arguments.length) {return false;}var name = getName();return true;}
Javascript会自动将变量声明"提升"(hoist)到代码块(block)的头部。
if (!o) {var o = {};}
等同于
var o;if (!o) {o = {};}
为了避免可能出现的问题,不如把变量声明都放在代码块的头部。
for (var i ...) {...}
最好写成:
var i;for (i ...) {...,}
因此,
所有变量声明都放在函数的头部。
所有函数都在使用之前定义。
变量声明是在作用域的顶端,但是赋值没有
// 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); // => undefinedvar 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); // => undefineddeclaredButNotAssigned = true;}
匿名表达式能提升他们的变量名,但不能提升函数赋值
function example() {console.log(anonymous); // => undefinedanonymous(); // => TypeError anonymous is not a functionvar anonymous = function() {console.log('anonymous function expression');};}
命名函数表达式会提升变量名,而不是函数名或者函数体
function example() {console.log(named); // => undefinednamed(); // => TypeError named is not a functionsuperPower(); // => ReferenceError superPower is not definedvar 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); // => undefinednamed(); // => TypeError named is not a functionvar named = function named() {console.log('named');}}
函数声明会提升变量名和函数体
function example() {superPower(); // => Flyingfunction 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}
使用快捷方式
// badif (name !== '') {// ...stuff...}// goodif (name) {// ...stuff...}// badif (collection.length > 0) {// ...stuff...}// goodif (collection.length) {// ...stuff...}
对多行的语句块使用大括号
// badif (test)return false;// goodif (test) return false;// goodif (test) {return false;}// badfunction() { return false; }// goodfunction() {return false;}
对于使用if和else的多行语句块,把else和if语句块的右大括号放在同一行
// badif (test) {thing1();thing2();}else {thing3();}// goodif (test) {thing1();thing2();} else {thing3();}
多行注释使用/** … */,需包含一个描述、所有参数的具体类型和值以及返回值
// bad// make() returns a new element// based on the passed in tag name//// @param {String} tag// @return {Element} elementfunction 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;}
单行注释使用//,把单行注释放在语句的上一行,并且在注释之前空一行
// badvar active = true; // is current tab// good// is current tabvar active = true;// badfunction getType() {console.log('fetching type...');// set the default type to 'no type'var type = this._type || 'no type';return type;}// goodfunction 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 heretotal = 0;return this;}
使用//TODO:给问题解决方案作注释
function Calculator() {// TODO: total should be configurable by an options paramthis.total = 0;return this;}
使用软制表符设置两个空格
// badfunction() {∙∙∙∙var name;}// badfunction() {∙var name;}// goodfunction() {∙∙var name;}
在左大括号之前留一个空格
// badfunction test(){console.log('test');}// goodfunction test() {console.log('test');}// baddog.set('attr',{age: '1 year',breed: 'Bernese Mountain Dog'});// gooddog.set('attr', {age: '1 year',breed: 'Bernese Mountain Dog'});
在控制语句中(if, while etc),左括号之前留一个空格。函数的参数列表之前不要有空格
// badif(isJedi) {fight ();}// goodif (isJedi) {fight();}// badfunction fight () {console.log ('Swooosh!');}// goodfunction fight() {console.log('Swooosh!');}
用空白分隔运算符
// badvar x=y+5;// goodvar 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();// badvar 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);// goodvar 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);
在语句块和下一个语句之前留一个空行
// badif (foo) {return bar;}return baz;// goodif (foo) {return bar;}return baz;// badvar obj = {foo: function() {},bar: function() {}};return obj;// goodvar obj = {foo: function() {},bar: function() {}};return obj;
不要在语句前留逗号
// badvar story = [once, upon, aTime];// goodvar story = [once,upon,aTime];// badvar hero = {firstName: 'Bob', lastName: 'Parr', heroName: 'Mr. Incredible', superPower: 'strength'};// goodvar hero = {firstName: 'Bob',lastName: 'Parr',heroName: 'Mr. Incredible',superPower: 'strength'};
不要有多余逗号:这会在IE6、IE7和IE9的怪异模式中导致一些问题;同时,在ES3的一些实现中,多余的逗号会增加数组的长度。在ES5中已经澄清(source)
// badvar hero = {firstName: 'Kevin',lastName: 'Flynn',};var heroes = ['Batman','Superman',];// goodvar 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;// badvar totalScore = this.reviewScore + '';// goodvar totalScore = '' + this.reviewScore;// badvar totalScore = '' + this.reviewScore + ' total score';// goodvar totalScore = this.reviewScore + ' total score';
使用parseInt对Numbers进行转换,并带一个进制作为参数
var inputValue = '4';// badvar val = new Number(inputValue);// badvar val = +inputValue;// badvar val = inputValue >> 0;// badvar val = parseInt(inputValue);// goodvar val = Number(inputValue);// goodvar 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;// badvar hasAge = new Boolean(age);// goodvar hasAge = Boolean(age);// goodvar hasAge = !!age;
避免单字母名称,让名称具有描述性
// badfunction q() {// ...stuff...}// goodfunction query() {// ..stuff..}
当命名对象、函数和实例时使用骆驼拼写法
// badvar OBJEcttsssss = {};var this_is_my_object = {};function c() {}var u = new user({name: 'Bob Parr'});// goodvar thisIsMyObject = {};function thisIsMyFunction() {}var user = new User({name: 'Bob Parr'});
当命名构造函数或类名时,使用驼峰式写法
// badfunction user(options) {this.name = options.name;}var bad = new user({name: 'nope'});// goodfunction User(options) {this.name = options.name;}var good = new User({name: 'yup'});
命名私有属性时使用前置下划线
// badthis.__firstName__ = 'Panda';this.firstName_ = 'Panda';// goodthis._firstName = 'Panda';保存this引用时使用_this// badfunction() {var self = this;return function() {console.log(self);};}// badfunction() {var that = this;return function() {console.log(that);};}// goodfunction() {var _this = this;return function() {console.log(_this);};}
命名函数时,下面的方式有利于堆栈跟踪
// badvar log = function(msg) {console.log(msg);};// goodvar log = function log(msg) {console.log(msg);};
注意:IE8和怪异模式下命名函数表示,戳此:http://kangax.github.io/nfe/
如果文件作为一个类被导出,文件名应该和类名保持一致
// file contentsclass CheckBox {// ...}module.exports = CheckBox;// in some other file// badvar CheckBox = require('./checkBox');// badvar CheckBox = require('./check_box');// goodvar CheckBox = require('./CheckBox');
对于属性,访问器函数不是必须的
如果定义了存取器函数,应参照getVal() 和 setVal(‘hello’)格式.
// baddragon.age();// gooddragon.getAge();// baddragon.age(25);// gooddragon.setAge(25);
如果属性时boolean,格式应为isVal() or hasVal().
// badif (!dragon.age()) {return false;}// goodif (!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');}// badJedi.prototype = {fight: function fight() {console.log('fighting');},block: function block() {console.log('blocking');}};// goodJedi.prototype.fight = function fight() {console.log('fighting');};Jedi.prototype.block = function block() {console.log('blocking');};
方法应该返回this,有利于构成方法链
// badJedi.prototype.jump = function() {this.jumping = true;return true;};Jedi.prototype.setHeight = function(height) {this.height = height;};var luke = new Jedi();luke.jump(); // => trueluke.setHeight(20); // => undefined// goodJedi.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对象变量使用前缀$
// badvar sidebar = $('.sidebar');// goodvar $sidebar = $('.sidebar');
缓存jQuery查询
// badfunction setSidebar() {$('.sidebar').hide();// ...stuff...$('.sidebar').css({'background-color': 'pink'});}// goodfunction 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();
常量的形式如: NAMES_LIKE_THIS, 即使用大写字符, 并用下划线分隔. 你也可用 @const 标记来指明它是一个常量. 但请永远不要使用 const 关键词.
Decision:
对于基本类型的常量, 只需转换命名.
/*** The number of seconds in a minute.* @type {number}*/goog.example.SECONDS_IN_A_MINUTE = 60;
对于非基本类型, 使用 @const 标记.
/*** The number of seconds in each of the given units.* @type {Object.<number>}* @const*/goog.example.SECONDS_TABLE = {minute: 60,hour: 60 * 60,day: 60 * 60 * 24}
这标记告诉编译器它是常量.
至于关键词 const, 因为 IE 不能识别, 所以不要使用.
[没有任何理由去封装基本类型, 另外还存在一些风险]:
//bad x会被判断为真var x = new Boolean(false);if (x) {alert('hi'); // Shows 'hi'.}
除非明确用于类型转换, 其他情况请千万不要这样做!
var x = Boolean(0);if (x) {alert('hi'); // This will never be alerted.}typeof Boolean(0) == 'boolean';typeof new Boolean(0) == 'object';
有时用作 number, string 或 boolean时, 类型的转换会非常实用.
[不是首选]
多级原型结构是指 JavaScript 中的继承关系. 当你自定义一个D类, 且把B类作为其原型, 那么这就获得了一个多级原型结构. 这些原型结构会变得越来越复杂!
使用 the Closure 库 中的 goog.inherits() 或其他类似的用于继承的函数, 会是更好的选择.
function D() {goog.base(this)}goog.inherits(D, B);D.prototype.method = function() {...};
[可以, 但小心使用]
闭包也许是 JS 中最有用的特性了. 有一份比较好的介绍闭包原理的文档.
有一点需要牢记, 闭包保留了一个指向它封闭作用域的指针, 所以, 在给 DOM 元素附加闭包时, 很可能会产生循环引用, 进一步导致内存泄漏. 比如下面的代码:
//badfunction foo(element, a, b) {element.onclick = function() { /* uses a and b */ };}
这里, 即使没有使用 element, 闭包也保留了 element, a 和 b 的引用. 由于 element 也保留了对闭包的引用, 这就产生了循环引用, 这就不能被 GC 回收. 这种情况下, 可将代码重构为:
//goodfunction foo(element, a, b) {element.onclick = bar(a, b);}function bar(a, b) {return function() { /* uses a and b */ }}
[仅在对象构造器, 方法, 闭包中使用]
this 的语义很特别. 有时它引用
eval时), call() 或 apply()).使用时很容易出错, 所以只有在下面两个情况时才能使用:
[只用于 object/map/hash 的遍历]
对 Array 用 for-in 循环有时会出错. 因为它并不是从 0 到 length - 1 进行遍历, 而是所有出现在对象及其原型链的键值. 下面就是一些失败的使用案例:
//badfunction printArray(arr) {for (var key in arr) {print(arr[key]);}}printArray([0,1,2,3]); // This works.var a = new Array(10);printArray(a); // This is wrong.a = document.getElementsByTagName('*');printArray(a); // This is wrong.a = [0,1,2,3];a.buhu = 'wine';printArray(a); // This is wrong again.a = new Array;a[3] = 3;printArray(a); // This is wrong again.
而遍历数组通常用最普通的 for 循环.
function printArray(arr) {var l = arr.length;for (var i = 0; i < l; i++) {print(arr[i]);}}
永远不要使用 Array 作为 map/hash/associative 数组.
数组中不允许使用非整型作为索引值, 所以也就不允许用关联数组. 而取代它使用 Object 来表示 map/hash 对象. Array 仅仅是扩展自 Object (类似于其他 JS 中的对象, 就像 Date, RegExp 和 String)一样来使用.
使用 Array 和 Object 语法, 而不使用 Array 和 Object 构造器.
使用 Array 构造器很容易因为传参不恰当导致错误.
//bad// Length is 3.var a1 = new Array(x1, x2, x3);// Length is 2.var a2 = new Array(x1, x2);// If x1 is a number and it is a natural number the length will be x1.// If x1 is a number but not a natural number this will throw an exception.// Otherwise the array will have one element with x1 as its value.var a3 = new Array(x1);// Length is 0.var a4 = new Array();
如果传入一个参数而不是2个参数, 数组的长度很有可能就不是你期望的数值了.
为了避免这些歧义, 我们应该使用更易读的直接量来声明.
//goodvar a = [x1, x2, x3];var a2 = [x1, x2];var a3 = [x1];var a4 = [];
虽然 Object 构造器没有上述类似的问题, 但鉴于可读性和一致性考虑, 最好还是在字面上更清晰地指明.
//badvar o = new Object();var o2 = new Object();o2.a = 0;o2.b = 1;o2.c = 2;o2['strange key'] = 3;
应该写成:
//goodvar o = {};var o2 = {a: 0,b: 1,c: 2,'strange key': 3};
[不要]
千万不要修改内置对象, 如 Object.prototype 和 Array.prototype 的原型. 而修改内置对象, 如 Function.prototype 的原型, 虽然少危险些, 但仍会导致调试时的诡异现象. 所以也要避免修改其原型.
尽量让函数参数在同一行上. 如果一行超过 80 字符, 每个参数独占一行, 并以4个空格缩进, 或者与括号对齐, 以提高可读性. 尽可能不要让每行超过80个字符. 比如下面这样:
// Four-space, wrap at 80. Works with very long function names, survives// renaming without reindenting, low on space.goog.foo.bar.doThingThatIsVeryDifficultToExplain = function(veryDescriptiveArgumentNumberOne, veryDescriptiveArgumentTwo,tableModelEventHandlerProxy, artichokeDescriptorAdapterIterator) {// ...};// Four-space, one argument per line. Works with long function names,// survives renaming, and emphasizes each argument.goog.foo.bar.doThingThatIsVeryDifficultToExplain = function(veryDescriptiveArgumentNumberOne,veryDescriptiveArgumentTwo,tableModelEventHandlerProxy,artichokeDescriptorAdapterIterator) {// ...};// Parenthesis-aligned indentation, wrap at 80. Visually groups arguments,// low on space.function foo(veryDescriptiveArgumentNumberOne, veryDescriptiveArgumentTwo,tableModelEventHandlerProxy, artichokeDescriptorAdapterIterator) {// ...}// Parenthesis-aligned, one argument per line. Visually groups and// emphasizes each individual argument.function bar(veryDescriptiveArgumentNumberOne,veryDescriptiveArgumentTwo,tableModelEventHandlerProxy,artichokeDescriptorAdapterIterator) {// ...}
如果参数中有匿名函数, 函数体从调用该函数的左边开始缩进2个空格, 而不是从 function 这个关键字开始. 这让匿名函数更加易读 (不要增加很多没必要的缩进让函数体显示在屏幕的右侧).
var names = items.map(function(item) {return item.name;});prefix.something.reallyLongFunctionName('whatever', function(a1, a2) {if (a1.equals(a2)) {someOtherLongFunctionName(a1);} else {andNowForSomethingCompletelyDifferent(a2.parrot);}});
事实上, 除了 初始化数组和对象 , 和传递匿名函数外, 所有被拆开的多行文本要么选择与之前的表达式左对齐, 要么以4个(而不是2个)空格作为一缩进层次.
someWonderfulHtml = '' +getEvenMoreHtml(someReallyInterestingValues, moreValues,
解释:
UTF-8 编码具有更广泛的适应性。BOM 在使用程序或工具处理文件时可能造成不必要的干扰。
[建议] 在文件结尾处,保留一个空行。
示例:
var a = !arr.length;a++;a = b + c;
{ 前必须有一个空格。示例:
// goodif (condition) {}while (condition) {}function funcName() {}// badif (condition){}while (condition){}function funcName(){}
if / else / for / while / function / switch / do / try / catch / finally 关键字后,必须有一个空格。示例:
// goodif (condition) {}while (condition) {}(function () {})();// badif(condition) {}while(condition) {}(function() {})();
: 之后必须有空格,: 之前不允许有空格。示例:
// goodvar obj = {a: 1,b: 2,c: 3};// badvar obj = {a : 1,b:2,c :3};
( 之间不允许有空格。示例:
// goodfunction funcName() {}var funcName = function funcName() {};funcName();// badfunction funcName () {}var funcName = function funcName () {};funcName ();
, 和 ; 前不允许有空格。示例:
// goodcallFunc(a, b);// badcallFunc(a , b) ;
if / for / while / switch / catch 等语句中,() 和 [] 内紧贴括号部分不允许有空格。示例:
// goodcallFunc(param1, param2, param3);save(this.list[this.indexes[i]]);needIncream && (variable += increament);if (num > list.length) {}while (len--) {}// badcallFunc( param1, param2, param3 );save( this.list[ this.indexes[ i ] ] );needIncreament && ( variable += increament );if ( num > list.length ) {}while ( len-- ) {}
{} 和 [] 内紧贴括号部分不允许包含空格。解释:
声明包含元素的数组与对象,只有当内部元素的形式较为简单时,才允许写在一行。元素复杂的情况,还是应该换行书写。
示例:
// goodvar arr1 = [];var arr2 = [1, 2, 3];var obj1 = {};var obj2 = {name: 'obj'};var obj3 = {name: 'obj',age: 20,sex: 1};// badvar arr1 = [ ];var arr2 = [ 1, 2, 3 ];var obj1 = { };var obj2 = { name: 'obj' };var obj3 = {name: 'obj', age: 20, sex: 1};
string 时,使用 + ''。示例:
// goodnum + '';// badnew String(num);num.toString();String(num);
number 时,通常使用 +。示例:
// good+str;// badNumber(str);
string 转换成 number,要转换的字符串结尾包含非数字并期望忽略时,使用 parseInt。示例:
var width = '200px';parseInt(width, 10);
parseInt 时,必须指定进制。示例:
// goodparseInt(str, 10);// badparseInt(str);
boolean 时,使用 !!。示例:
var num = 3.14;!!num;
number 去除小数点,使用 Math.floor / Math.round / Math.ceil,不使用 parseInt。示例:
// goodvar num = 3.14;Math.ceil(num);// badvar num = 3.14;parseInt(num, 10);
[强制] 类名 使用 名词。
[建议] 函数名 使用 动宾短语。
[建议] boolean 类型的变量使用 is 或 has 开头。
[建议] Promise对象 用 动宾短语的进行时 表达。
示例:
var loadingData = ajax.get('url');loadingData.then(callback);