JavaScript编码规范
1.类型
1.1基本类型: 直接存取基本类型。
string
number
boolean
null
undefined
const foo = 1;
let bar = foo;
bar = 9;
console.log(foo, bar); // => 1, 9
1.2复杂类型: 通过引用的方式存取复杂类型
object
array
function
const foo = [1, 2];
const bar = foo;
bar[0] = 9;
console.log(foo[0], bar[0]); // => 9, 9
2.引用
2.1 [强制] 对所有的引用使用 const
;不要使用 var
为什么?这能确保你无法对引用重新赋值,也不会导致出现 bug 或难以理解。
// bad
var a = 1;
var b = 2;
// good
const a = 1;
const b = 2;
2.2 [强制] 需要可变动的引用,使用 let
代替 var
为什么?因为
let
,const
是块级作用域,而var
是函数作用域。
const
和let
只存在于它们被定义的区块内。
{
let a = 1;
const b = 1;
}
console.log(a); // ReferenceError
console.log(b); // ReferenceError
// bad
var count = 1;
if (true) {
count += 1;
}
// good, use the let.
let count = 1;
if (true) {
count += 1;
}
3.对象
3.1 [强制] 禁止使用[保留字]作为属性名
因为在IE8及以下版本存在问题,使用同义词替换它
// bad
const item = {
class: 'dark'
};
// good
const item = {
type: 'dark'
};
3.2 [强制] 禁止在对象的最后一个属性值后添加,
IE6和IE7下会存在js报错(同上)
// bad
const item = {
type: 'dark',
element: 'flame',
};
// good
const item = {
type: 'dark',
element: 'flame'
};
3.3 [强制] 使用字面值创建对象
// bad
const item = new Object();
// good
const item = {};
3.4 [强制] 使用对象方法的简写
// bad
const atom = {
value: 1,
addValue: function (value) {
return atom.value + value;
},
};
// good
const atom = {
value: 1,
addValue(value) {
return atom.value + value;
},
};
3.5 [强制] 使用对象属性值的简写
const lukeSkywalker = 'Luke Skywalker';
// bad
const obj = {
lukeSkywalker: lukeSkywalker,
};
// good
const obj = {
lukeSkywalker,
};
3.6 [推荐] 在对象属性声明前把简写的属性分组
const anakinSkywalker = 'Anakin Skywalker';
const lukeSkywalker = 'Luke Skywalker';
// bad
const obj = {
episodeOne: 1,
twoJedisWalkIntoACantina: 2,
lukeSkywalker,
episodeThree: 3,
mayTheFourth: 4,
anakinSkywalker,
};
// good
const obj = {
lukeSkywalker,
anakinSkywalker,
episodeOne: 1,
twoJedisWalkIntoACantina: 2,
episodeThree: 3,
mayTheFourth: 4,
};
4.数组
4.1 [强制] 使用字面量创建数组
// bad
const item = new Array();
// good
const item = [];
4.2 [强制] 向数组末尾增加元素时,使用 Array.push()
代替直接赋值
let magicBook = [];
// bad
magicBook[magicBook.length] = 'Expelliarmus';
// good
magicBook.push('Expelliarmus');
4.3 [强制] 浅拷贝(shallow copy)数组时,使用 Array.slice()
或拓展运算符 ...
复制数组
浅拷贝:由于对象和数组在赋值的时候都是引用传递,赋值的时候只是传递一个指针(见1.类型),浅拷贝的数组中的对象依旧是引用。
深拷贝(deep copy): 可以使用
jquery
的jQuery.extend
或者 npm的deep*copy
方法等。
let items = [{ id: 1 },{ id: 2}];
const len = items.length;
const itemsCopy = [];
let i;
// bad
for (i = 0; i < len; i++) {
itemsCopy[i] = items[i];
}
// good
itemsCopy = items.slice();
// good
const itemsCopy = [...items];
//is shallow copy?
items[0] == itemsCopy[0]; // => true 为浅拷贝
4.4 [推荐] 使用 Array.from()
把一个类数组对象转换成数组。
const foo = document.querySelectorAll('.foo');
const nodes = Array.from(foo);
5.字符串
5.1 [建议] 使用单引号包裹字符串
// bad
const name = "Harry Potter";
// good
const name = 'Harry Potter';
5.2[强制] 程序化生成字符串时,使用模板字符串代替字符串连接
// bad
function sayHi(name) {
return 'How are you, ' + name + '?';
}
// bad
function sayHi(name) {
return ['How are you, ', name, '?'].join();
}
// good
function sayHi(name) {
return `How are you, ${name}?`;
}
6.函数
6.1 [强制] 禁止在非函数代码块中(如 if
while
)声明函数
// bad
if (flag) {
function castSpell(){
// do something...
}
}
// good
function castAvada() {
return 'Avada Kedavra';
}
let castSpell;
if (flag) {
castSpell = castAvada;
}
6.2 [强制] 禁止把参数名命名为arguments
,会取代函数中的arguments
// bad
function nope(name, options, arguments) {
// ...stuff...
}
// good
function yup(name, options, args) {
// ...stuff...
}
6.3 [推荐] 不要使用 arguments
。可以选择 rest
语法 ...
替代
为什么?使用 ... 能明确你要传入的参数。另外 rest 参数是一个真正的数组,而 arguments 是一个类数组。
// bad
function concatenateAll() {
const args = Array.prototype.slice.call(arguments);
return args.join('');
}
// good
function concatenateAll(...args) {
return args.join('');
}
6.4 [强制] 禁止使用全局严格模式
// yes
function handel() {
"use strict";
}
// no
"use strict";
function handel() {
}
6.5 [推荐] 使用函数声明代替函数表达式。
为什么?因为函数声明是可命名的,所以他们在调用栈中更容易被识别。此外,函数声明会把整个函数提升(hoisted),而函数表达式只会把函数的引用变量名提升。这条规则使得箭头函数可以取代函数表达式。
// bad
const foo = function () {
};
// good
function foo() {
}
6.6 [推荐] 直接给函数的参数指定默认值,不要使用一个变化的函数参数。
// really bad
function handleThings(opts) {
// 不!我们不应该改变函数参数。
// 更加糟糕: 如果参数 opts 是 false 的话,它就会被设定为一个对象。
// 但这样的写法会造成一些 Bugs。
//(译注:例如当 opts 被赋值为空字符串,opts 仍然会被下一行代码设定为一个空对象。)
opts = opts || {};
// ...
}
// still bad
function handleThings(opts) {
if (opts === void 0) {
opts = {};
}
// ...
}
// good
function handleThings(opts = {}) {
// ...
}
6.7 [推荐] 箭头函数代替function
。
// bad
[1, 2, 3].map(function (x) {
const y = x + 1;
return x * y;
});
// good
[1, 2, 3].map((x) => {
const y = x + 1;
return x * y;
});
如果一个函数适合用一行写出并且只有一个参数,那就把花括号、圆括号和
return
都省略掉。如果不是,那就不要省略。
// good
[1, 2, 3].map(x => x * x);
// good
[1, 2, 3].reduce((total, n) => {
return total + n;
}, 0);
7.属性
7.1 [强制] 使用 .
来访问对象的属性
const luke = {
jedi: true,
age: 28,
};
// bad
const isJedi = luke['jedi'];
// good
const isJedi = luke.jedi;
7.2 [强制] 当通过变量访问属性时使用中括号 []
。
const luke = {
jedi: true,
age: 28,
};
function getProp(prop) {
return luke[prop];
}
const isJedi = getProp('jedi');
8.变量
8.1 [强制] 使用 let
const
替换代码中的var
,代码中不要出现魔幻数字
,用const
提前声明这些常量
// bad
var a = 10;
var arr = [1,2,3];
superPower = new SuperPower();
// good
const a = 10;
let arr = [1,2,3];
const superPower = new SuperPower();
8.2 [强制] 如果需要要声明一个全局变量,必须使用window.
// bad
spell = 'Expelliarmus';
// good
window.spell = 'Expelliarmus';
9.解构
9.1 [推荐] 使用解构存取和使用多属性对象。
为什么?因为解构能减少临时引用属性。
// bad
function getFullName(user) {
const firstName = user.firstName;
const lastName = user.lastName;
return `${firstName} ${lastName}`;
}
// good
function getFullName(obj) {
const { firstName, lastName } = obj;
return `${firstName} ${lastName}`;
}
// best
function getFullName({ firstName, lastName }) {
return `${firstName} ${lastName}`;
}
9.2 [推荐] 对数组使用解构赋值。
const arr = [1, 2, 3, 4];
// bad
const first = arr[0];
const second = arr[1];
// good
const [first, second] = arr;
9.3 [推荐] 需要回传多个值时,使用对象解构,而不是数组解构。
为什么?增加属性或者改变排序不会改变调用时的位置。
// bad
function processInput(input) {
// then a miracle occurs
return [left, right, top, bottom];
}
// 调用时需要考虑回调数据的顺序。
const [left, __, top] = processInput(input);
// good
function processInput(input) {
// then a miracle occurs
return { left, right, top, bottom };
}
// 调用时只选择需要的数据
const { left, right } = processInput(input);
10.构造器
10.1 [推荐] 总是使用 class
。避免直接操作 prototype
。
为什么? 因为
class
语法更为简洁更易读。
// bad
function Queue(contents = []) {
this._queue = [...contents];
}
Queue.prototype.pop = function() {
const value = this._queue[0];
this._queue.splice(0, 1);
return value;
}
// good
class Queue {
constructor(contents = []) {
this._queue = [...contents];
}
pop() {
const value = this._queue[0];
this._queue.splice(0, 1);
return value;
}
}
10.2 [推荐] 使用 extends
继承。
为什么?因为
extends
是一个内建的原型继承方法并且不会破坏instanceof
。
// bad
const inherits = require('inherits');
function PeekableQueue(contents) {
Queue.apply(this, contents);
}
inherits(PeekableQueue, Queue);
PeekableQueue.prototype.peek = function() {
return this._queue[0];
}
// good
class PeekableQueue extends Queue {
peek() {
return this._queue[0];
}
}
10.3 [推荐] 方法可以返回 this
来帮助链式调用。
// bad
Jedi.prototype.jump = function() {
this.jumping = true;
return true;
};
Jedi.prototype.setHeight = function(height) {
this.height = height;
};
const luke = new Jedi();
luke.jump(); // => true
luke.setHeight(20); // => undefined
// good
class Jedi {
jump() {
this.jumping = true;
return this;
}
setHeight(height) {
this.height = height;
return this;
}
}
const luke = new Jedi();
luke.jump()
.setHeight(20);
11.模块
11.1 [推荐] 总是使用模组 (import/export)
而不是其他非标准模块系统。
为什么?模块就是未来,让我们开始迈向未来吧。
// bad
const AirbnbStyleGuide = require('./AirbnbStyleGuide');
module.exports = AirbnbStyleGuide.es6;
// ok
import AirbnbStyleGuide from './AirbnbStyleGuide';
export default AirbnbStyleGuide.es6;
// best
import { es6 } from './AirbnbStyleGuide';
export default es6;
11.2 [强制] 不要使用通配符*
。
为什么?这样能确保你只有一个默认
export
。
// bad
import * as AirbnbStyleGuide from './AirbnbStyleGuide';
// good
import AirbnbStyleGuide from './AirbnbStyleGuide';
11.3 [推荐] 不要从 import
中直接 export
。
为什么?虽然一行代码简洁明了,但让
import
和export
各司其职让事情能保持一致。
// bad
// filename es6.js
export { es6 as default } from './airbnbStyleGuide';
// good
// filename es6.js
import { es6 } from './AirbnbStyleGuide';
export default es6;
12.Iterators and Generators
12.1 [推荐] 不要使用 iterators
。使用高阶函数例如 map()
和 reduce()
替代 for*of
。
为什么?这加强了我们不变的规则。处理纯函数的回调值更易读,这比它带来的副作用更重要。
const numbers = [1, 2, 3, 4, 5];
// bad
let sum = 0;
for (let num of numbers) {
sum += num;
}
sum === 15;
// good
let sum = 0;
numbers.forEach((num) => sum += num);
sum === 15;
// best (use the functional force)
const sum = numbers.reduce((total, num) => total + num, 0);
sum === 15;
12.2 现在还不要使用generators
。
为什么?因为它们现在还没法很好地编译到 ES5。
13比较运算符和等号
13.1 [强制] 优先使用 ===
和 !==
而不是 ==
和 !=
.
13.2 [推荐] 使用简写。
// bad
if (name !== '') {
// ...stuff...
}
// good
if (name) {
// ...stuff...
}
// bad
if (collection.length > 0) {
// ...stuff...
}
// good
if (collection.length) {
// ...stuff...
}
13.3条件表达式的强制类型转换遵循以下规则:
- 对象 被计算为
true
-
undefined
被计算为false
-
null
被计算为false
- 布尔值 被计算为 布尔的值
- 数字 如果是
+0
,*0
, 或是NaN
被计算为false
, 否则为true
- 字符串 如果是空字符串
''
则被计算为false
, 否则为true
if([0]) {
// true 因为数组是对象,对象被计算为true
}
if([]){
// true 因为数组是对象,对象被计算为true
}
14.代码块
14.1 [强制] 使用大括号包裹所有的多行代码块。
// bad
if (test)
return false;
// good
if (test) return false;
// good
if (test) {
return false;
}
// bad
function() { return false; }
// good
function() {
return false;
}
14.2 [强制] 如果通过 if
和 else
使用多行代码块,把 else
放在 if
代码块关闭括号的同一行。
// bad
if (test) {
thing1();
thing2();
}
else {
thing3();
}
// good
if (test) {
thing1();
thing2();
} else {
thing3();
}
15.注释
15.1 [强制] 使用/** ... */
作为多行注释。包含描述、指定所有参数和返回值的类型和值。
// 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;
}
15.2 [强制] 使用 //
作为单行注释。在评论对象上面另起一行使用单行注释。在注释前插入空行。
// bad
const active = true; // is current tab
// good
// is current tab
const active = true;
// bad
function getType() {
console.log('fetching type...');
// set the default type to 'no type'
const type = this._type || 'no type';
return type;
}
// good
function getType() {
console.log('fetching type...');
// set the default type to 'no type'
const type = this._type || 'no type';
return type;
}
15.3 [推荐] 给注释增加 FIXME
或 TODO
的前缀。
这可以帮助其他开发者快速了解这是一个需要复查的问题,或是给需要实现的功能提供一个解决方式。这将有别于常见的注释,因为它们是可操作的。使用
FIXME ** need to figure this out
或者TODO ** need to implement
。
使用 // FIXME:
标注问题。
class Calculator {
constructor() {
// FIXME: shouldn't use a global here
total = 0;
}
}
使用 // TODO:
标注问题的解决方式。
class Calculator {
constructor() {
// TODO: total should be configurable by an options param
this.total = 0;
}
}
16.空白
16.1 [强制] 使用 2 个空格
作为缩进。
// bad
function() {
∙∙∙∙const name;
}
// bad
function() {
∙const name;
}
// good
function() {
∙∙const name;
}
16.2 [强制] 在花括号前放一个空格。
// 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',
});
16.3 [强制] 在控制语句(if
、while
等)的小括号前放一个空格。在函数调用及声明中,不在函数的参数列表前加空格。
// bad
if(isJedi) {
fight ();
}
// good
if (isJedi) {
fight();
}
// bad
function fight () {
console.log ('Swooosh!');
}
// good
function fight() {
console.log('Swooosh!');
}
16.4 [强制] 使用空格把运算符隔开。
// bad
const x=y+5;
// good
const x = y + 5;
16.5 [强制] 在文件末尾插入一个空行。
// bad
(function(global) {
// ...stuff...
})(this);
// bad
(function(global) {
// ...stuff...
})(this);↵
↵
// good
(function(global) {
// ...stuff...
})(this);↵
16.6 [推荐] 在使用长方法链时进行缩进。使用前面的点 .
强调这是方法调用而不是新语句。
// 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
const leds = stage.selectAll('.led').data(data).enter().append('svg:svg').class('led', true)
.attr('width', (radius + margin) * 2).append('svg:g')
.attr('transform', 'translate(' + (radius + margin) + ',' + (radius + margin) + ')')
.call(tron.led);
// good
const 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);
16.7 [推荐] 在块末和新语句前插入空行。
// bad
if (foo) {
return bar;
}
return baz;
// good
if (foo) {
return bar;
}
return baz;
// bad
const obj = {
foo() {
},
bar() {
},
};
return obj;
// good
const obj = {
foo() {
},
bar() {
},
};
return obj;
17.逗号
17.1 [强制] 在变量或表达式后面添加逗号,
不要出现逗号,
在前
// bad
const hero = {
firstName: 'Ada'
, lastName: 'Lovelace'
, birthYear: 1815
, superPower: 'computers'
};
// good
const hero = {
firstName: 'Ada',
lastName: 'Lovelace',
birthYear: 1815,
superPower: 'computers',
};
17.2 [强制] 去掉不必要的逗号,这在IE67下会出现怪异行为。
const hero = {
name:'hongxin',
age:18,
}
const heros = [
'Batman',
'Superman',
]
18.分号
18.1 [推荐] 每条语句必须加分号。
// bad
(function() {
const name = 'Skywalker'
return name
})()
// good
(() => {
const name = 'Skywalker';
return name;
})();
// good (防止函数在两个 IIFE 合并时被当成一个参数)
;(() => {
const name = 'Skywalker';
return name;
})();
19.类型转换
19.1 [推荐] 字符串转换
// => this.reviewScore = 9;
// bad
const totalScore = this.reviewScore + '';
// good
const totalScore = String(this.reviewScore);
19.2 [推荐] 布尔转换
const age = 0;
// bad
const hasAge = new Boolean(age);
// good
const hasAge = Boolean(age);
// good
const hasAge = !!age;
19.3 [强制] 使用parseInt
转换数字时总是带上转换基数
// bad
parseInt('0x10'); // => 16
parseInt('09'); // => 0 (ie8下, ie9+及chrome为9)
// good
parseInt('09',10); // => 9
19.4 [谨慎] 小心使用位操作运算符。
数字会被当成 64 位值,但是位操作运算符总是返回 32 位的整数。位操作处理大于 32 位的整数值时还会导致意料之外的行为。最大的 32 位整数是 2,147,483,647
2147483647 >> 0 //=> 2147483647
2147483648 >> 0 //=> *2147483648
2147483649 >> 0 //=> *2147483647
20.命名规则
20.1 [强制] 避免单字母命名。命名应具备描述性。
// bad
function q() {
// ...stuff...
}
// good
function query() {
// ..stuff..
}
20.2 [强制] 以驼峰形式命名变量、函数和实例
变量和实例应尽量以名词为主,函数名尽量以动词+名词组合,返回布尔值的函数尽量以is、has打头
// good
const communityList = [];
function collectShells(){
return shells;
}
function isAnimal(){
return bool;
}
20.3 [强制] 常量必须全部大写,多个单词以下划线分隔
const LANEHUB = 10;
const LANEHUB_JAVASCRIPT = 100;
20.4 [强制] 使用帕斯卡式命名(大驼峰)构造函数或类。
当你导出单例、函数库、空对象时使用帕斯卡式命名
// bad
function user(options) {
this.name = options.name;
}
const bad = new user({
name: 'nope',
});
// good
class User {
constructor(options) {
this.name = options.name;
}
}
const good = new User({
name: 'yup',
});
20.5 [强制] 不要使用下划线 _
结尾或开头来命名属性和方法。
// bad
this.__firstName__ = 'Panda';
this.firstName_ = 'Panda';
this._firstName = 'Panda';
// good
this.firstName = 'Panda';
20.6 [强制] 如果你的文件只输出一个类,那你的文件名必须和类名完全保持一致。
// file contents
class CheckBox {
// ...
}
export default CheckBox;
// in some other file
// bad
import CheckBox from './checkBox';
// bad
import CheckBox from './check_box';
// good
import CheckBox from './CheckBox';
21.ECMAScript 规范 [学习]
ECMAScript 6(以下简称ES6)是JavaScript语言的下一代标准,于2015年6月批准通过。ECMAScript6的目标,是使得JavaScript语言可以用来编写复杂的大型应用程序,成为企业级开发语言。让代码更加准确,更易于阅读。
—— 推荐地址
JavaScript最强势的监工-正在迈向ES8,所以让我们来了解下ES7 and ES8(官方称ES2016 and ES2017),幸运的是,他们比ES6标准功能少好多好多
—— 推荐地址
网友评论