一、前言
如果你写的代码只是给自己看,不需要与多人共同协作;如果你写的代码不打算开源;如果你不进行code review;如果你不打算担任前端工程师这一岗位,只是单纯业余爱好;满足以上任一条件,我建议你编码不需要规范,怎么随性怎么来,编码规范束缚你的自由!
显然,大多数人都不是以上的情况!那么javascript编码规范就能解决以上的问题,还能够增强代码的简洁性、可读性、可扩展性。项目做到后期,每修改一次,所耗费的成本就越高,编码规范能节省这样的成本,并且能很好拓展升级原有系统功能。javascript编码规范也是开源社区大家约定俗成的规则!
二、具体规范
1、当你访问一个基本类型时,直接操作它的值。 当你访问一个复杂类型时,直接操作其值的引用。
2、使用 const
定义你的所有引用;避免使用 var
。如果你必须重新赋值你的引用, 使用 let
代替 var
。
3、使用字面量语法创建对象、数组
// bad
const item = new Object();
// bad
const items = new Array();
// good
const item = {};
// good
const items = [];
4、属性值、对象方法简写
// bad
const value = 1;
const atom = {
value: value,
addValue: function (newValue) {
return atom.value + newValue;
},
};
// good
const value = 1;
const atom = {
value,
addValue(newValue) {
return atom.value + newValue;
},
};
5、声明对象时,将简写的属性放在前面
const anakinSkywalker = 'Anakin Skywalker';
const lukeSkywalker = 'Luke Skywalker';
// bad
const obj = {
lukeSkywalker,
episodeThree: 3,
mayTheFourth: 4,
anakinSkywalker,
};
// good
const obj = {
lukeSkywalker,
anakinSkywalker,
episodeThree: 3,
mayTheFourth: 4,
};
6、使用对象扩展操作符(spread operator)浅拷贝对象,而不是用 Object.assign 方法。使用数组展开符 ...
来拷贝数组。
// very bad
const original = { a: 1, b: 2 };
const copy = Object.assign(original, { c: 3 }); // 变异的 `original` ಠ_ಠ
delete copy.a; // 这....
// bad
const original = { a: 1, b: 2 };
const copy = Object.assign({}, original, { c: 3 }); // copy => { a: 1, b: 2, c: 3 }
// good
const original = { a: 1, b: 2 };
const copy = { ...original, c: 3 }; // copy => { a: 1, b: 2, c: 3 }
const { a, ...noA } = copy; // noA => { b: 2, c: 3 }
// bad
const len = items.length;
const itemsCopy = [];
let i;
for (i = 0; i < len; i += 1) {
itemsCopy[i] = items[i];
}
// good
const itemsCopy = [...items];
7、使用 Array#push 代替直接赋值来给数组添加项。
const someStack = [];
// bad
someStack[someStack.length] = 'abracadabra';
// good
someStack.push('abracadabra');
8、使用 Array.from 将一个类数组(array-like)对象转换成一个数组。
const arrLike = { 0: 'foo', 1: 'bar', 2: 'baz', length: 3 };
// bad
const arr = Array.prototype.slice.call(arrLike);
// good
const arr = Array.from(arrLike);
9、使用 Array.from 代替展开符 ...
映射迭代器,因为它避免了创建一个中间数组。
// bad
const baz = [...foo].map(bar);
// good
const baz = Array.from(foo, bar);
10、在数组回调函数中使用 return 语句。 如果函数体由单个语句的返回表达式组成,并且无副作用,那么可以省略返回值
// good
[1, 2, 3].map((x) => {
const y = x + 1;
return x * y;
});
// good
[1, 2, 3].map(x => x + 1);
// bad - 没有返回值,意味着在第一次迭代后 `acc` 没有被定义
[[0, 1], [2, 3], [4, 5]].reduce((acc, item, index) => {
const flatten = acc.concat(item);
acc[index] = flatten;
});
// good
[[0, 1], [2, 3], [4, 5]].reduce((acc, item, index) => {
const flatten = acc.concat(item);
acc[index] = flatten;
return flatten;
}, []);
// bad
inbox.filter((msg) => {
const { subject, author } = msg;
if (subject === 'Mockingbird') {
return author === 'Harper Lee';
} else {
return false;
}
});
// good
inbox.filter((msg) => {
const { subject, author } = msg;
if (subject === 'Mockingbird') {
return author === 'Harper Lee';
}
return false;
});
11、不应该用字符串跨行连接符的格式来跨行编写,这样会使当前行长度超过100个字符。
// bad
const 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
const 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
const 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.';
12、 使用命名的函数表达式代替函数声明。
// bad
function foo() {
// ...
}
// also good *
const foo = function () {
// ...
};
// good
const short = function longUniqueMoreDescriptiveLexicalFoo() {
// ...
};
13、 把立即执行函数包裹在圆括号里。
// immediately-invoked function expression (IIFE) 立即调用的函数表达式
(function () {
console.log('Welcome to the Internet. Please follow me.');
}());
14、永远不要给一个参数命名为 arguments
。 这将会覆盖函数默认的 arguments
对象
// bad
function foo(name, options, arguments) {
// ...
}
// good
function foo(name, options, args) {
// ...
}
15、使用 rest 语法 ...
代替 arguments
。
16、总是把默认参数放在最后
// bad
function handleThings(opts = {}, name) {
// ...
}
// good
function handleThings(name, opts = {}) {
// ...
}
17、函数声明语句中需要空格。
// bad
const f = function(){};
const g = function (){};
const h = function() {};
// good
const x = function () {};
const y = function a() {};
18、调用或者书写一个包含多个参数的函数应该像这个指南里的其他多行代码写法一样: 每行值包含一个参数,并且最后一行也要以逗号结尾。
// bad
function foo(bar,
baz,
quux) {
// ...
}
// bad
console.log(foo,
bar,
baz);
// good
function foo(
bar,
baz,
quux,
) {
// ...
}
// good
console.log(
foo,
bar,
baz,
);
19、箭头函数表达式跨越多个行,用括号将其括起来,以获得更好的可读性。
// bad
['get', 'post', 'put'].map(httpMethod => Object.prototype.hasOwnProperty.call(
httpMagicObjectWithAVeryLongName,
httpMethod,
)
);
// good
['get', 'post', 'put'].map(httpMethod => (
Object.prototype.hasOwnProperty.call(
httpMagicObjectWithAVeryLongName,
httpMethod,
)
));
20、如果你的函数只有一个参数并且函数体没有大括号,就删除圆括号。 否则,为了保证清晰和一致性,请给参数加上括号。
// bad
[1, 2, 3].map((x) => x * x);
// good
[1, 2, 3].map(x => x * x);
// good
[1, 2, 3].map(number => (
`A long string with the ${number}. It’s so long that we don’t want it to take up space on the .map line!`
));
// bad
[1, 2, 3].map(x => {
const y = x + 1;
return x * y;
});
// good
[1, 2, 3].map((x) => {
const y = x + 1;
return x * y;
});
21、在箭头函数用隐式 return 时强制将函数体的位置约束在箭头后
// bad
(foo) =>
bar;
(foo) =>
(bar);
// good
(foo) => bar;
(foo) => (bar);
(foo) => (
bar
);
22、类有默认的构造方法。一个空的构造函数或只是代表父类的构造函数是不需要写的。
// bad
class Jedi {
constructor() {}
getName() {
return this.name;
}
}
// bad
class Rey extends Jedi {
// 这种构造函数是不需要写的
constructor(...args) {
super(...args);
}
}
// good
class Rey extends Jedi {
constructor(...args) {
super(...args);
this.name = 'Rey';
}
}
23、使用ES6的模块 (import
/export
) 语法来定义模块,不要用require
24、不要使用import * 通配符
25、不要导出可变的引用。
// bad
let foo = 3;
export { foo };
// good
const foo = 3;
export { foo };
26、在只有单一导出的模块里,用 export default 更好
// bad
export function foo() {}
// good
export default function foo() {}
27、将所有的 import
s 语句放在其他语句之前
28、多行引入应该像多行数组和对象字面量一样缩进。
// bad
import {longNameA, longNameB, longNameC, longNameD, longNameE} from 'path';
// good
import {
longNameA,
longNameB,
longNameC,
longNameD,
longNameE,
} from 'path';
29、计算指数时,可以使用 **
运算符
// bad
const binary = Math.pow(2, 10);
// good
const binary = 2 ** 10;
30、变量应先声明再使用,禁止引用任何未声明的变量,除非你明确知道引用的变量存在于当前作用域链上。禁止不带任何关键词定义变量,这样做将会创建一个全局变量,污染全局命名空间,造成程序意料之外的错误。
/ bad, 这会创建一个全局变量
superPower = new SuperPower();
// good
const superPower = new SuperPower();
// bad, 容易污染外部变量
let superPower = 'a';
(function() {
superPower = 'b';
})();
console.log(superPower);
// good
let superPower = 'a';
(function() {
let superPower = 'b';
})();
console.log(superPower);
// bad, 更常见的情况是这样的,在 for 循环里的 i 将会污染外部的变量 i
let i = 1;
(function() {
for (i = 0; i < 10; i++) {
console.log('inside', i);
}
console.log('outside', i)
})();
console.log('global', i);
// good
let i = 1;
(function() {
// i 的作用域在 for 循环内
for (let i = 0; i < 10; i++) {
console.log('inside i', i);
}
// 如果真的需要在 for 循环外使用循环变量,应该先定义在外部
let j;
for (j = 0; j < 10; j++) {
console.log('inside j:', j);
}
console.log('outside j', j);
})();
console.log('global', i);
31、声明多个变量应该分开声明,避免使用 ,
一次声明多个变量。
// bad
const items = getItems(),
goSportsTeam = true,
dragonball = 'z';
// bad
const items = getItems(),
goSportsTeam = true;
// 定义成了全局变量
dragonball = 'z';
// good
const items = getItems();
const goSportsTeam = true;
const dragonball = 'z';
32、把 const
声明语句放在一起,把 let
声明语句放在一起。
// bad
let i;
const items = getItems();
let dragonball;
const goSportsTeam = true;
let len;
// good
const goSportsTeam = true;
const items = getItems();
let dragonball;
let i;
let length;
33、 不要链式变量赋值。
// bad
(function example() {
/*
* JavaScript 把它解释为
* let a = ( b = ( c = 1 ) );
* let 关键词只适用于变量 a,变量 b 和变量 c 则变成了全局变量。
*/
let a = b = c = 1;
}());
// throws ReferenceError
console.log(a);
// 1
console.log(b);
// 1
console.log(c);
// good
(function example() {
let a = 1;
let b = a;
let c = a;
}());
// throws ReferenceError
console.log(a);
// throws ReferenceError
console.log(b);
// throws ReferenceError
console.log(c);
// 对于 `const` 也一样
34、避免在赋值语句 =
前后换行。如果你的代码单行长度超过了 max-len 定义的长度而不得不换行,那么使用括号包裹
// bad
const foo
= 'superLongLongLongLongLongLongLongLongString';
// bad
const bar =
superLongLongLongLongLongLongLongLongFunctionName();
// bad
const fullHeight = borderTop +
innerHeight +
borderBottom;
// bad
const anotherHeight = borderTop +
innerHeight +
borderBottom;
// bad
const thirdHeight = (
borderTop +
innerHeight +
borderBottom
);
// good - max-len 会忽略字符串,直接写后面即可。
const foo = 'superLongLongLongLongLongLongLongLongString';
// good
const bar = (
superLongLongLongLongLongLongLongLongFunctionName()
);
// good
const fullHeight = borderTop
+ innerHeight
+ borderBottom;
// good
const anotherHeight = borderTop
+ innerHeight
+ borderBottom;
// good
const thirdHeight = (
borderTop
+ innerHeight
+ borderBottom
);
35、使用 ===
和 !==
而不是 ==
和 !=
36、条件语句,例如 if
语句使用 ToBoolean
的抽象方法来计算表达式的结果,并始终遵循以下简单的规则:
-
Objects 的取值为: true,{} 和 [] 的取值也为: true
-
Undefined 的取值为: false
-
Null 的取值为: false
-
Booleans 的取值为: 布尔值的取值
-
Numbers 的取值为:如果为 +0, -0, or NaN 值为 false 否则为 true
-
Strings 的取值为: 如果是一个空字符串
''
值为 false 否则为 true
37、 对于布尔值(在明确知道是布尔值的情况下)使用简写,但是对于字符串和数字进行显式比较
// bad
if (isValid === true) {
// ...
}
// good
if (isValid) {
// ...
}
// bad
if (name) {
// ...
}
// good
if (name !== '') {
// ...
}
// bad
if (collection.length) {
// ...
}
// good
if (collection.length > 0) {
// ...
}
38、在 case
和 default
的子句中,如果存在声明 (例如. let
, const
, function
, 和 class
),使用大括号来创建块级作用域。
// bad
switch (foo) {
case 1:
let x = 1;
break;
case 2:
const y = 2;
break;
case 3:
function f() {
// ...
}
break;
default:
class C {}
}
// good
switch (foo) {
case 1: {
let x = 1;
break;
}
case 2: {
const y = 2;
break;
}
case 3: {
function f() {
// ...
}
break;
}
case 4:
bar();
break;
default: {
class C {}
}
}
39、避免不必要的三元表达式。三元表达式不应该嵌套,通常是单行表达式,如果确实需要多行表达式,那么应该考虑使用条件语句。
40、当有多行代码块的时候,应使用大括号包裹
// bad
if (test)
return false;
// bad
let condition = true;
let test = 1;
// 在缩进不规范的时候,容易造成误解
if (condition)
condition = false;
test = 2;
// good
if (test) return false;
// good
if (test) {
return false;
}
// bad
function foo() { return false; }
// good
function bar() {
return false;
}
41、如果你的控制语句 (if
, while
等) 太长或者超过了一行最大长度的限制,则可以将每个条件(或组)放入一个新的行。 逻辑运算符应该在行的开始。
// bad
if ((foo === 123 || bar === 'abc') && doesItLookGoodWhenItBecomesThatLong() && isThisReallyHappening()) {
thing1();
}
// bad
if (foo === 123 &&
bar === 'abc') {
thing1();
}
// bad
if (foo === 123
&& bar === 'abc') {
thing1();
}
// bad
if (
foo === 123 &&
bar === 'abc'
) {
thing1();
}
// good
if (
foo === 123
&& bar === 'abc'
) {
thing1();
}
// good
if (
(foo === 123 || bar === 'abc')
&& doesItLookGoodWhenItBecomesThatLong()
&& isThisReallyHappening()
) {
thing1();
}
// good
if (foo === 123 && bar === 'abc') {
thing1();
}
42、使用 /* ... */
来进行多行注释。
43、使用 //
进行单行注释。 将单行注释放在需要注释的行的上方新行。 建议在注释之前放一个空行,除非它在块的第一行。但如果一段代码每行都包含注释,允许不加分行。
// 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;
}
// also good
function getType() {
// set the default type to 'no type'
const type = this.type || 'no type';
return type;
}
44、 用一个空格开始所有的注释,使它更容易阅读。
// bad
//is current tab
const active = true;
// good
// is current tab
const active = true;
// bad
/**
*make() returns a new element
*based on the passed-in tag name
*/
function make(tag) {
// ...
return element;
}
// good
/**
* make() returns a new element
* based on the passed-in tag name
*/
function make(tag) {
// ...
return element;
}
45、使用 tabs (空格字符) 设置为2个空格。
// bad
function foo() {
∙∙∙∙let name;
}
// bad
function bar() {
∙let name;
}
// good
function baz() {
∙∙let name;
}
46、在花括号前放置一个空格。
// 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',
});
47、在控制语句中的左括号前放置1个空格(if,while等)。在函数调用和声明中,参数列表和函数名之间不能留空格
// bad
if(isJedi) {
fight ();
}
// good
if (isJedi) {
fight();
}
// bad
function fight () {
console.log ('Swooosh!');
}
// good
function fight() {
console.log('Swooosh!');
}
48、运算符左右设置各设置一个空格
// bad
const x=y+5;
// good
const x = y + 5;
49、在块和下一个语句之前留下一空白行。
// 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;
// bad
const arr = [
function foo() {
},
function bar() {
},
];
return arr;
// good
const arr = [
function foo() {
},
function bar() {
},
];
return arr;
50、不要在块的开头使用空白行。
// bad
function bar() {
console.log(foo);
}
// bad
if (baz) {
console.log(qux);
} else {
console.log(foo);
}
// bad
class Foo {
constructor(bar) {
this.bar = bar;
}
}
// good
function bar() {
console.log(foo);
}
// good
if (baz) {
console.log(qux);
} else {
console.log(foo);
}
51、不要在中括号中添加空格, 在花括号内添加空格
// bad
const foo = [ 1, 2, 3 ];
console.log(foo[ 0 ]);
// good
const foo = [1, 2, 3];
console.log(foo[0]);
// bad
const foo = {clark: 'kent'};
// good
const foo = { clark: 'kent' };
52、逗号之前避免使用空格,逗号之后需要使用空格
// bad
const arr = [1 , 2];
// good
const arr = [1, 2];
53、在对象的属性和值之间的冒号前不加空格,冒号后加空格
// bad
var obj = { foo : 42 };
var obj2 = { foo:42 };
// good
var obj = { foo: 42 };
54、对于 jQuery 对象一律使用 $
符作为前缀。
// bad
const sidebar = $('.sidebar');
// good
const $sidebar = $('.sidebar');
// good
const $sidebarBtn = $('.sidebar-btn');
55、缓存 jQuery 查询,节省 DOM 查询开销。
// bad
function setSidebar() {
$('.sidebar').hide();
// ...
$('.sidebar').css({
'background-color': 'pink',
});
}
// good
function setSidebar() {
const $sidebar = $('.sidebar');
$sidebar.hide();
// ...
$sidebar.css({
'background-color': 'pink',
});
}
56、 使用 String()
函数将变量转成字符串
// bad
const totalScore = new String(this.reviewScore); // typeof totalScore is "object" not "string"
// bad
const totalScore = this.reviewScore + ''; // invokes this.reviewScore.valueOf()
// bad
const totalScore = this.reviewScore.toString(); // isn’t guaranteed to return a string
// good
const totalScore = String(this.reviewScore);
57、数字类型转换推荐用 Number()
或者 parseInt()
函数,其中 parseInt()
需显式标明底数。
const inputValue = '4';
// bad
const val = new Number(inputValue);
// bad
const val = +inputValue;
// bad
const val = inputValue >> 0;
// bad
const val = parseInt(inputValue);
// good
const val = Number(inputValue);
// good
const val = parseInt(inputValue, 10);
58、用两个叹号来转换布尔类型
const age = 0;
// bad
const hasAge = new Boolean(age);
// good
const hasAge = Boolean(age);
// best
const hasAge = !!age;
59、使用驼峰命名法(camelCase)命名对象、函数和实例,只有在命名构造器或者类的时候,才用帕斯卡命名法(PascalCase),即首字母大写
// bad
const OBJEcttsssss = {};
const this_is_my_object = {};
function c() {}
// good
const thisIsMyObject = {};
function thisIsMyFunction() {}
// 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',
});
60、变量命名时不要使用前置或者后置下划线
61、变量命名时不要使用前置或者后置下划线
// bad
function foo() {
const self = this;
return function () {
console.log(self);
};
}
// bad
function foo() {
const that = this;
return function () {
console.log(that);
};
}
// good
function foo() {
return () => {
console.log(this);
};
}
62、文件名建议使用 kebab-case,后缀为小写
// bad
import CheckBox from './checkBox'; // PascalCase import/export, camelCase filename
import FortyTwo from './FortyTwo'; // PascalCase import/filename, camelCase export
import InsideDirectory from './InsideDirectory'; // PascalCase import/filename, camelCase export
// bad
import CheckBox from './check_box'; // PascalCase import/export, snake_case filename
import forty_two from './forty_two'; // snake_case import/filename, camelCase export
import inside_directory from './inside_directory'; // snake_case import, camelCase export
import index from './inside_directory/index'; // requiring the index file explicitly
import insideDirectory from './insideDirectory/index'; // requiring the index file explicitly
// good
import CheckBox from './check-box'; // kebab-case export/import/filename
import fortyTwo from './forty-two'; // kebab-case export/import/filename
import insideDirectory from './inside-directory'; // kebab-case export/import/directory name/implicit "index"
63、导出默认函数时使用驼峰命名法,并且文件名应该和方法名相同。文件名建议使用 kebab-case,后缀为小写。当导出构造器 / 类 / 单例 / 函数库 / 对象时应该使用帕斯卡命名法(首字母大写)
function makeStyleGuide() {
// ...
}
export default makeStyleGuide;
const TStyleGuide = {
es6: {
},
};
export default TStyleGuide;
64、缩略词和缩写都必须是全部大写或者全部小写,可读性更好。
// bad
import SmsContainer from './containers/sms-container';
// bad
const HttpRequests = [
// ...
];
// good
import SMSContainer from './containers/sms-container';
// good
const HTTPRequests = [
// ...
];
65、无论原生 DOM 事件还是像 Backbone 事件这样的自定义事件,在给事件传递参数时,参数应该使用对象而不是单个值。这样未来如果参数有所变化,就不用修改事件回调函数本身。
// bad
$(this).trigger('listingUpdated', listing.id);
// ...
$(this).on('listingUpdated', (e, listingID) => {
// do something with listingID
});
// good
$(this).trigger('listingUpdated', { listingID: listing.id });
// ...
$(this).on('listingUpdated', (e, data) => {
// do something with data.listingID
});
66、必须加分号
// bad - 可能异常
const jedis = {}
['luke', 'leia'].some((name) => {
jedis[name] = true
return true
})
// bad - 可能异常
const reaction = "No! That's impossible!"
(async function meanwhileOnTheFalcon() {
// handle `leia`, `lando`, `chewie`, `r2`, `c3p0`
// ...
}())
// bad - 返回 `undefined` 而不是下一行的值 - 当 `return` 单独一行的时候 ASI 总是会发生
function foo() {
return
'search your feelings, you know it to be foo'
}
// good
const jedis = {};
['luke', 'leia'].some((name) => {
jedis[name] = true;
return true;
});
// good
const reaction = "No! That's impossible!";
(async function meanwhileOnTheFalcon() {
// handle `leia`, `lando`, `chewie`, `r2`, `c3p0`
// ...
}());
// good
function foo() {
return 'search your feelings, you know it to be foo';
}