- JavaScript编码规范
- 目录" level="2">目录
- 类型" level="2">类型
- 对象" level="2">对象
- 数组" level="2">数组
- 字符串" level="2">字符串
- 函数" level="2">函数
- 属性" level="2">属性
- 变量" level="2">变量
- 提升" level="2">提升
- 比较运算符 & 等号" level="2">比较运算符 & 等号
- 块" level="2">块
- 注释" level="2">注释
- 空白" level="2">空白
- 逗号" level="2">逗号
- 分号" level="2">分号
- 类型转换" level="2">类型转换
- 命名规则" level="2">命名规则
- 存取器" level="2">存取器
- 构造函数" level="2">构造函数
- 事件" level="2">事件
- 模块" level="2">模块
- jQuery" level="2">jQuery
- ECMAScript 5 兼容性" level="2">ECMAScript 5 兼容性
- 测试" level="2">测试
- 性能" level="2">性能
- 资源" level="2">资源
JavaScript编码规范
主要依据下面的文档整理,局部有修改。
https://raw.githubusercontent.com/sivan/javascript-style-guide
https://github.com/airbnb/javascript
JavaScript Standard Style
目录" class="reference-link">目录
- 类型
- 对象
- 数组
- 字符串
- 函数
- 属性
- 变量
- 提升
- 比较运算符 & 等号
- 块
- 注释
- 空白
- 逗号
- 分号
- 类型转化
- 命名规则
- 存取器
- 构造函数
- 事件
- 模块
- jQuery
- ECMAScript 5 兼容性
- 测试
- 性能
- 资源
类型" class="reference-link">类型
原始值: 存取直接作用于它自身。
stringnumberbooleannullundefined
var foo = 1;var bar = foo;bar = 9;console.log(foo, bar); // => 1, 9
复杂类型: 存取时作用于它自身值的引用。
objectarrayfunction
var foo = [1, 2];var bar = foo;bar[0] = 9;console.log(foo[0], bar[0]); // => 9, 9
⬆ 回到顶部
对象" class="reference-link">对象
使用直接量创建对象。
// badvar item = new Object();// goodvar item = {};
不要使用保留字作为键名,它们在 IE8 下不工作。更多信息。
// badvar superman = {default: { clark: 'kent' },privated: true};// goodvar superman = {defaults: { clark: 'kent' },hidden: true};
使用同义词替换需要使用的保留字。
// badvar superman = {class: 'alien'};// badvar superman = {klass: 'alien'};// goodvar superman = {type: 'alien'};
⬆ 回到顶部
数组" class="reference-link">数组
使用直接量创建数组。
// badvar items = new Array();// goodvar items = [];
向数组增加元素时使用 Array#push 来替代直接赋值。
```javascript
var someStack = [];
// badsomeStack[someStack.length] = 'abracadabra';// goodsomeStack.push('abracadabra');```
当你需要拷贝数组时,使用 Array#slice。jsPerf
var len = items.length;var itemsCopy = [];var i;// badfor (i = 0; i < len; i++) {itemsCopy[i] = items[i];}// gooditemsCopy = items.slice();
使用 Array#slice 将类数组对象转换成数组。
function trigger() {var args = Array.prototype.slice.call(arguments);...}
⬆ 回到顶部
字符串" class="reference-link">字符串
使用单引号
''包裹字符串。// badvar name = "Bob Parr";// goodvar name = 'Bob Parr';// badvar fullName = "Bob " + this.lastName;// goodvar fullName = 'Bob ' + this.lastName;
超过 100 个字符的字符串应该使用连接符写成多行。
注:若过度使用,通过连接符连接的长字符串可能会影响性能。jsPerf & 讨论.
// 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++) {// use direct assignment in this case because we're micro-optimizing.items[i] = '<li>' + messages[i].message + '</li>';}return '<ul>' + items.join('') + '</ul>';}
⬆ 回到顶部
函数" class="reference-link">函数
函数表达式:
// 匿名函数表达式var anonymous = function() {return true;};// 命名函数表达式var named = function named() {return true;};// 立即调用的函数表达式(IIFE)(function () {console.log('Welcome to the Internet. Please follow me.');}());
永远不要在一个非函数代码块(if、while 等)中声明一个函数,把那个函数赋给一个变量。浏览器允许你这么做,但它们的解析表现不一致。
注: ECMA-262 把
块定义为一组语句。函数声明不是语句。阅读对 ECMA-262 这个问题的说明。// 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...}
⬆ 回到顶部
属性" class="reference-link">属性
使用
.来访问对象的属性。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');
⬆ 回到顶部
变量" class="reference-link">变量
总是使用
var来声明变量。不这么做将导致产生全局变量。我们要避免污染全局命名空间。// badsuperPower = new SuperPower();// goodvar superPower = new SuperPower();
使用
var声明每一个变量。
这样做的好处是增加新变量将变的更加容易,而且你永远不用再担心调换错;跟,。// badvar items = getItems(),goSportsTeam = true,dragonball = 'z';// bad// (跟上面的代码比较一下,看看哪里错了)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;}// bad - 不必要的函数调用function () {var name = getName();if (!arguments.length) {return false;}this.setFirstName(name);return true;}// goodfunction () {var name;if (!arguments.length) {return false;}name = getName();this.setFirstName(name);return true;}
⬆ 回到顶部
提升" class="reference-link">提升
变量声明会提升至作用域顶部,但赋值不会。
// 我们知道这样不能正常工作(假设这里没有名为 notDefined 的全局变量)function example() {console.log(notDefined); // => throws a ReferenceError}// 但由于变量声明提升的原因,在一个变量引用后再创建它的变量声明将可以正常工作。// 注:变量赋值为 `true` 不会提升。function example() {console.log(declaredButNotAssigned); // => undefinedvar declaredButNotAssigned = true;}// 解释器会把变量声明提升到作用域顶部,意味着我们的例子将被重写成: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');};}// 当函数名跟变量名一样时,表现也是如此。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.
⬆ 回到顶部
比较运算符 & 等号" class="reference-link">比较运算符 & 等号
- 优先使用
===和!==而不是==和!=. 条件表达式例如
if语句通过抽象方法ToBoolean强制计算它们的表达式并且总是遵守下面的规则:- 对象 被计算为 true
- Undefined 被计算为 false
- Null 被计算为 false
- 布尔值 被计算为 布尔的值
- 数字 如果是 +0、-0 或 NaN 被计算为 false,否则为 true
- 字符串 如果是空字符串
''被计算为 false,否则为 true
if ([0]) {// true// 一个数组就是一个对象,对象被计算为 true}
使用快捷方式。
// badif (name !== '') {// ...stuff...}// goodif (name) {// ...stuff...}// badif (collection.length > 0) {// ...stuff...}// goodif (collection.length) {// ...stuff...}
了解更多信息在 Truth Equality and JavaScript by Angus Croll.
⬆ 回到顶部
块" class="reference-link">块
使用大括号包裹所有的多行代码块。
// 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();}
⬆ 回到顶部
注释" class="reference-link">注释
使用
/** ... */作为多行注释。包含描述、指定所有参数和返回值的类型和值。// 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或TODO的前缀可以帮助其他开发者快速了解这是一个需要复查的问题,或是给需要实现的功能提供一个解决方式。这将有别于常见的注释,因为它们是可操作的。使用FIXME -- need to figure this out或者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;}
⬆ 回到顶部
空白" class="reference-link">空白
使用 2 个空格作为缩进。
// 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等)的小括号前放一个空格。在函数调用及声明中,不在函数的参数列表前加空格。// 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;
⬆ 回到顶部
逗号" class="reference-link">逗号
行首逗号: 不需要。
// 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/7 和 IE9 怪异模式下引起问题。同样,多余的逗号在某些 ES3 的实现里会增加数组的长度。在 ES5 中已经澄清了 (source):
Edition 5 clarifies the fact that a trailing comma at the end of an ArrayInitialiser does not add to the length of the array. This is not a semantic change from Edition 3 but some implementations may have previously misinterpreted this.
// badvar hero = {firstName: 'Kevin',lastName: 'Flynn',};var heroes = ['Batman','Superman',];// goodvar hero = {firstName: 'Kevin',lastName: 'Flynn'};var heroes = ['Batman','Superman'];
⬆ 回到顶部
分号" class="reference-link">分号
使用分号。
// bad(function () {var name = 'Skywalker'return name})()// good(function () {var name = 'Skywalker';return name;})();// good (防止函数在两个 IIFE 合并时被当成一个参数;(function () {var name = 'Skywalker';return name;})();
了解更多.
⬆ 回到顶部
类型转换" class="reference-link">类型转换
- 在语句开始时执行类型转换。
字符串:
// => this.reviewScore = 9;// badvar totalScore = this.reviewScore + '';// goodvar totalScore = '' + this.reviewScore;// badvar totalScore = '' + this.reviewScore + ' total score';// goodvar totalScore = this.reviewScore + ' total score';
使用
parseInt转换数字时总是带上类型转换的基数。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;
注: 小心使用位操作运算符。数字会被当成 64 位值,但是位操作运算符总是返回 32 位的整数(source)。位操作处理大于 32 位的整数值时还会导致意料之外的行为。讨论。最大的 32 位整数是 2,147,483,647:
2147483647 >> 0 //=> 21474836472147483648 >> 0 //=> -21474836482147483649 >> 0 //=> -2147483647
布尔:
var age = 0;// badvar hasAge = new Boolean(age);// goodvar hasAge = Boolean(age);// goodvar hasAge = !!age;
⬆ 回到顶部
命名规则" class="reference-link">命名规则
避免单字母命名。命名应具备描述性。
// badfunction q() {// ...stuff...}// goodfunction query() {// ..stuff..}
使用驼峰式命名对象、函数和实例。
// badvar OBJEcttsssss = {};var this_is_my_object = {};var o = {};function c() {}// goodvar thisIsMyObject = {};function thisIsMyFunction() {}
使用帕斯卡式命名构造函数或类。
// 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');
⬆ 回到顶部
存取器" class="reference-link">存取器
- 属性的存取函数不是必须的。
如果你需要存取函数时使用
getVal()和setVal('hello')。// baddragon.age();// gooddragon.getAge();// baddragon.age(25);// gooddragon.setAge(25);
如果属性是布尔值,使用
isVal()或hasVal()。// badif (!dragon.age()) {return false;}// goodif (!dragon.hasAge()) {return false;}
创建 get() 和 set() 函数是可以的,但要保持一致。
function Jedi(options) {options || (options = {});var lightsaber = options.lightsaber || 'blue';this.set('lightsaber', lightsaber);}Jedi.prototype.set = function set(key, val) {this[key] = val;};Jedi.prototype.get = function get(key) {return this[key];};
⬆ 回到顶部
构造函数" class="reference-link">构造函数
给对象原型分配方法,而不是使用一个新对象覆盖原型。覆盖原型将导致继承出现问题:重设原型将覆盖原有原型!
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 jump() {this.jumping = true;return true;};Jedi.prototype.setHeight = function setHeight(height) {this.height = height;};var luke = new Jedi();luke.jump(); // => trueluke.setHeight(20); // => undefined// goodJedi.prototype.jump = function jump() {this.jumping = true;return this;};Jedi.prototype.setHeight = function setHeight(height) {this.height = height;return this;};var luke = new Jedi();luke.jump().setHeight(20);
写一个自定义的
toString()方法是可以的,但是确保它可以正常工作且不会产生副作用。function Jedi(options) {options || (options = {});this.name = options.name || 'no name';}Jedi.prototype.getName = function getName() {return this.name;};Jedi.prototype.toString = function toString() {return 'Jedi - ' + this.getName();};
⬆ 回到顶部
事件" class="reference-link">事件
当给事件附加数据时(无论是 DOM 事件还是私有事件),传入一个哈希而不是原始值。这样可以让后面的贡献者增加更多数据到事件数据而无需找出并更新事件的每一个处理器。例如,不好的写法:
// bad$(this).trigger('listingUpdated', listing.id);...$(this).on('listingUpdated', function (e, listingId) {// do something with listingId});
更好的写法:
// good$(this).trigger('listingUpdated', { listingId : listing.id });...$(this).on('listingUpdated', function (e, data) {// do something with data.listingId});
⬆ 回到顶部
模块" class="reference-link">模块
- 模块应该以
!开始。这样确保了当一个不好的模块忘记包含最后的分号时,在合并代码到生产环境后不会产生错误。详细说明 - 文件应该以驼峰式命名,并放在同名的文件夹里,且与导出的名字一致。
- 增加一个名为
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" class="reference-link">jQuery
使用
$作为存储 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'});}
对 DOM 查询使用层叠
$('.sidebar ul')或 父元素 > 子元素$('.sidebar > ul')。 jsPerf对有作用域的 jQuery 对象查询使用
find。// bad$('ul', '.sidebar').hide();// bad$('.sidebar').find('ul').hide();// good$('.sidebar ul').hide();// good$('.sidebar > ul').hide();// good$sidebar.find('ul').hide();
⬆ 回到顶部
ECMAScript 5 兼容性" class="reference-link">ECMAScript 5 兼容性
- 参考 Kangax 的 ES5 兼容表.
⬆ 回到顶部
测试" class="reference-link">测试
Yup.
function () {return true;}
⬆ 回到顶部
性能" class="reference-link">性能
- On Layout & Web Performance
- String vs Array Concat
- Try/Catch Cost In a Loop
- Bang Function
- jQuery Find vs Context, Selector
- innerHTML vs textContent for script text
- Long String Concatenation
- Loading…
⬆ 回到顶部
资源" class="reference-link">资源
推荐阅读
- Annotated ECMAScript 5.1
工具
- Code Style Linters
- JSHint - Airbnb Style .jshintrc
- JSCS - Airbnb Style Preset
其它风格指南
- Google JavaScript Style Guide
- jQuery Core Style Guidelines
- Principles of Writing Consistent, Idiomatic JavaScript
- JavaScript Standard Style
其它风格
- Naming this in nested functions - Christian Johansen
- Conditional Callbacks - Ross Allen
- Popular JavaScript Coding Conventions on Github - JeongHoon Byun
- Multiple var statements in JavaScript, not superfluous - Ben Alman
⬆ 回到顶部
