@chris-ren
2016-04-27T07:59:02.000000Z
字数 19618
阅读 1094
ECMAScript
JavaScript
react
规范
本文主要介绍JavaScript编码规范以及基于JavaScript规范,针对ES6和React拟定的相关规范约定。
原文链接(需要查看详细信息请参照):https://github.com/airbnb/javascript
1.类型
2.对象
3.数组
4.字符串
5.函数
6.属性
7.变量
8.条件表达式和等号
9.块
10.注释
11.空白
12.逗号
13.分号
14.类型转换
15.命名约定
16.存取器
17.事件
基本类型:访问基本类型时,应该直接操作类型值
- string
- number
- boolean
- null
- undefined
var foo = 1,
bar = foo;
bar = 9;
console.log(foo, bar); // => 1, 9
复杂类型:访问复杂类型时,应该操作其引用
- object
- array
- function
var foo = [1, 2],
bar = foo;
bar[0] = 9;
console.log(foo[0], bar[0]); // => 9, 9
使用字面量语法创建对象
//bad
var item = new Object();
// good
var item = {};
不要使用保留字作为键
// bad
var superman = {
class: 'superhero',
default: { clark: 'kent' },
private: true
};
// good
var superman = {
klass: 'superhero',
defaults: { clark: 'kent' },
hidden: true
};
// bad
var items = new Array();
// good
var items = [];
var someStack = [];
// bad
someStack[someStack.length] = 'abracadabra';
// good
someStack.push('abracadabra');
var len = items.length,
itemsCopy = [],
i;
// bad
for (i = 0; i < len; i++) {
itemsCopy[i] = items[i];
}
// good
itemsCopy = items.slice();
function trigger() {
var args = [].slice.apply(arguments);
...
}
// bad
var name = "Bob Parr";
// good
var name = 'Bob Parr';
// bad
var fullName = "Bob " + this.lastName;
// good
var fullName = 'Bob ' + this.lastName;
// bad
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.';
// bad
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.';
// good
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.';
var items,
messages,
length, 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;
// bad
function inbox(messages) {
items = '<ul>';
for (i = 0; i < length; i++) {
items += '<li>' + messages[i].message + '</li>';
}
return items + '</ul>';
}
// good
function inbox(messages) {
items = [];
for (i = 0; i < length; i++) {
items[i] = messages[i].message;
}
return '<ul><li>' + items.join('</li><li>') + '</li></ul>';
}
// 匿名函数表达式
var anonymous = function() {
return true;
};
// 有名函数表达式
var named = function named() {
return true;
};
// 立即调用函数表达式
(function() {
console.log('Welcome to the Internet. Please follow me.');
})();
// bad
if (currentUser) {
function test() {
console.log('Nope.');
}
}
// good
if (currentUser) {
var test = function test() {
console.log('Yup.');
};
}
// bad
function nope(name, options, arguments) {
// ...stuff...
}
// good
function yup(name, options, args) {
// ...stuff...
}
var luke = {
jedi: true,
age: 28
};
function getProp(prop) {
return luke[prop];
}
var isJedi = getProp('jedi');
// bad
superPower = new SuperPower();
// good
var superPower = new SuperPower();
// bad
var items = getItems();
var goSportsTeam = true;
var dragonball = 'z';
// good
var items = getItems(),
goSportsTeam = true,
dragonball = 'z';
// bad
var i, len, dragonball,
items = getItems(),
goSportsTeam = true;
// bad
var i, items = getItems(),
dragonball,
goSportsTeam = true,
len;
// good
var items = getItems(),
goSportsTeam = true,
dragonball,
length,
i;
/ bad
function() {
test();
console.log('doing stuff..');
//..other stuff..
var name = getName();
if (name === 'test') {
return false;
}
return name;
}
// good
function() {
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;
}
return true;
}
// good
function() {
if (!arguments.length) {
return false;
}
var name = getName();
return true;
}
if ([0]) {
// true
// An array is an object, objects evaluate to true
}
// bad
if (name !== '') {
// ...stuff...
}
// good
if (name) {
// ...stuff...
}
// bad
if (collection.length > 0) {
// ...stuff...
}
// good
if (collection.length) {
// ...stuff...
}
// bad
if (test)
return false;
// good
if (test) return false;
// good
if (test) {
return false;
}
// bad
function() { return false; }
// good
function() {
return false;
}
// 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;
}
// bad
var active = true; // is current tab
// good
// is current tab
var active = true;
// bad
function getType() {
console.log('fetching type...');
// set the default type to 'no type'
var type = this._type || 'no type';
return type;
}
// good
function getType() {
console.log('fetching type...');
// set the default type to 'no type'
var type = this._type || 'no type';
return type;
}
function Calculator() {
// FIXME: shouldn't use a global here
total = 0;
return this;
}
function Calculator() {
// TODO: total should be configurable by an options param
this.total = 0;
return this;
}
// bad
function() {
∙∙∙∙var name;
}
// bad
function() {
∙var name;
}
// good
function() {
∙∙var name;
}
// 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'
});
// bad
var x=y+5;
// good
var x = y + 5;
// bad
var once
, upon
, aTime;
// good
var once,
upon,
aTime;
// bad
var hero = {
firstName: 'Bob'
, lastName: 'Parr'
, heroName: 'Mr. Incredible'
, superPower: 'strength'
};
// good
var hero = {
firstName: 'Bob',
lastName: 'Parr',
heroName: 'Mr. Incredible',
superPower: 'strength'
};
// bad
var hero = {
firstName: 'Kevin',
lastName: 'Flynn',
};
var heroes = [
'Batman',
'Superman',
];
// good
var hero = {
firstName: 'Kevin',
lastName: 'Flynn'
};
var heroes = [
'Batman',
'Superman'
];
// bad
(function() {
var name = 'Skywalker'
return name
})()
// good
(function() {
var name = 'Skywalker';
return name;
})();
// good
;(function() {
var name = 'Skywalker';
return name;
})();
var inputValue = '4';
// bad
var val = new Number(inputValue);
// bad
var val = +inputValue;
// bad
var val = inputValue >> 0;
// bad
var val = parseInt(inputValue);
// good
var val = Number(inputValue);
// good
var val = parseInt(inputValue, 10);
// 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;
var age = 0;
// bad
var hasAge = new Boolean(age);
// good
var hasAge = Boolean(age);
// good
var hasAge = !!age;
// bad
this.__firstName__ = 'Panda';
this.firstName_ = 'Panda';
// good
this._firstName = 'Panda';
// bad
if (!dragon.age()) {
return false;
}
// good
if (!dragon.hasAge()) {
return false;
}
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];
};
// 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
});
1.声明 Declarations
2.字符串 Strings
3.解构 Destructuring
4.数组 Arrays
5.函数 Functions
6.类 Classes
7.模块 Modules
•1.1 变量
// bad
const variables;
const globalObj = null; // 不是常量
let globalObj = null;
for (var i = 0; i < 5; i++) {
console.log(i);
}
console.log(i); // 4
// good
let variables;
var globalObj = null;
for (let i = 0; i < 5; i++) {
console.log(i);
}
console.log(i); // ReferenceError: i is not defined
•1.2 常量
// bad
let someNum = 123;
const AnotherStr = '不变的字符串';
let arr = ['不', '变', '数', '组'];
var ANOTHER_OBJ = {
'不变对象': true
};
// good
const someNum = 123;
const anotherStr = '不变的字符串';
const arr = ['不', '变', '数', '组'];
const anotherObj = {
'不变对象': true
};
•2.1 处理多行字符串,使用模板字符串,以反引号( ` )标示,可读性更强,代码更易编写。注意排版引起空格的问题,使用场景为声明HTML模板字符串
// bad
const tmpl = '<div class="content"> \n' +
'<h1>这是换行了。</h1> \n' +
'</div>';
// good
const tmpl = `
<div class="content">
<h1>这是换行了。</h1>
</div>`;
•2.2 处理字符串拼接变量时,使用模板字符串
// bad
function sayHi(name) {
return 'How are you, ' + name + '?';
}
// good
function sayHi(name) {
return `How are you, ${name}?`;
}
•3.1 嵌套结构的对象层数不能超过3层
// bad
let obj = {
'one': [
{
'newTwo': [
{
'three': [
'four': '太多层了,头晕晕'
]
}
]
}
]
};
// good
let obj = {
'one': [
'two',
{
'twoObj': '结构清晰'
}
]
};
•3.2 解构语句中统一不使用圆括号
// bad
[(a)] = [11]; // a未定义
let { a: (b) } = {}; // 解析出错
// good
let [a, b] = [11, 22];
•3.3 对象解构
•3.3.1 若函数形参为对象时,使用对象解构赋值
// bad
function someFun(opt) {
let opt1 = opt.opt1;
let opt2 = opt.opt2;
console.log(op1);
}
// good
function someFun(opt) {
let { opt1, opt2 } = opt;
console.log(`$(opt1) 加上 $(opt2)`);
}
function someFun({ opt1, opt2 }) {
console.log(opt1);
}
•3.3.2 若函数有多个返回值时,使用对象解构,不使用数组解构,避免添加顺序的问题
// bad
function anotherFun() {
const one = 1, two = 2, three = 3;
return [one, two, three];
}
const [one, three, two] = anotherFun(); // 顺序乱了
// one = 1, two = 3, three = 2
// good
function anotherFun() {
const one = 1, two = 2, three = 3;
return { one, two, three };
}
const { one, three, two } = anotherFun(); // 不用管顺序
// one = 1, two = 2, three = 3
•3.3.3 已声明的变量不能用于解构赋值(语法错误)
// 语法错误
let a;
{ a } = { b: 123};
•3.4 数组解构
•3.4.1 交换变量的值
let x = 1;
let y = 2;
// bad
let temp;
temp = x;
x = y;
y = temp;
// good
[x, y] = [y, x]; // 交换变量
•3.4.2 将数组成员赋值给变量时,使用数组解构
const arr = [1, 2, 3, 4, 5];
// bad
const one = arr[0];
const two = arr[1];
// good
const [one, two] = arr;
•4.1 将类数组(array-like)对象与可遍历对象(如 Set , Map )转为真正数组,采用 Array.from 进行转换
// bad
function foo() {
let args = Array.prototype.slice.call(arguments);
}
// good
function foo() {
let args = Array.from(arguments);
}
•4.2 数组去重
// good
function deduplication(arr) {
return Array.from(new Set(arr));
}
•4.3 数组拷贝
- 采用数组扩展 ... 形式
const items = [1, 2, 3];
// bad
const len = items.length;
let copyTemp = [];
for (let i = 0; i < len; i++) {
copyTemp[i] = items[i];
}
// good
let copyTemp = [...items];
•4.4 将一组数值转为数组
// bad
let arr1 = new Array(2); // [undefined x 2]
let arr2 = new Array(1, 2, 3); // [1, 2, 3]
// good
let arr1 = Array.of(2); // [2]
let arr2 = Array.of(1, 2, 3); // [1, 2, 3]
•5.1 当要用函数表达式或匿名函数时,使用箭头函数(Arrow Functions),箭头函数更加简洁,并且绑定了this
// bad
const foo = function(x) {
console.log(foo.name); // 返回'' ,函数表达式默认省略name属性
};
[1, 2, 3].map(function(x) {
return x + 1;
});
var testObj = {
name: 'testObj',
init() {
var _this = this; // 保存定义时的this引用
document.addEventListener('click', function() {
return _this.doSth();
}, false);
},
doSth() {
console.log(this.name);
}
};
// good
const foo = (x) => {
console.log(foo.name); // 返回'foo'
};
[1, 2, 3].map( (x) => {
return x + 1;
});
var testObj = {
name: 'testObj',
init() {
// 箭头函数自动绑定定义时所在的对象
document.addEventListener('click', () => this.doSth(), false);
},
doSth() {
console.log(this.name);
}
};
•5.1.1 箭头函数书写约定
// good
const foo = x => x + x; // 注意此处会隐性return x + x
const foo = (x) => {
return x + x; // 若函数体有花括号语句块时须进行显性的return
};
[1, 2, 3].map( x => x * x);
•5.1.2 用箭头函数返回一个对象,应用括号包裹
// bad
let test = x => { x: x }; // 花括号会变成语句块,不表示对象
// good
let test = x => ({ x: x }); // 使用括号可正确return {x:x}
•5.2 立即调用函数,应该使用箭头函数
// bad
(function() {
console.log('哈');
})();
// good
(() => {
console.log('哈');
})();
•5.3 不使用 arguments , 采用rest语法 ... 代替
// bad
function foo() {
let args = Array.prototype.slice.call(arguments);
return args.join('');
}
// good
function foo(...args) {
return args.join('');
}
•5.4 函数参数指定默认值
// bad
function foo(opts) {
opts = opts || {};// 此处有将0,''等假值转换掉为默认值的副作用
}
// good
function foo(opts = {}) {
console.log('更加简洁,安全');
}
•5.5 对象中的函数方法使用缩写形式,更加简洁,函数方法不要使用箭头函数,避免this指向的混乱
// bad
const shopObj = {
des: '对象模块写法',
foo: function() {
console.log(this.des);
}
};
const shopObj = {
des: '对象模块写法',
foo: () => {
console.log(this.des); // 此处会变成undefined.des,因为指向顶层模块的this
}
};
// good
const des = '对象模块写法'; // 使用对象属性值简写方式
const shopObj = {
des,
foo() {
console.log(this.des);
}
};
•6.1 类名应使用帕斯卡写法( PascalCased )
// good
class SomeClass {
}
•6.1.1 类名与花括号须保留一个空格间距,类中的方法定义时,方法名与左括号 ( 之间【不保留】空格间距,类中的方法定义时,右括号 ) 须与花括号 { 【保留】一个空格间距
// bad
class Foo{
constructor(){
// 右括号 `)` 须与花括号 `{` 仅保留一个空格间距
}
sayHi() {
}
_say () {
// 方法名与左括号 `(` 之间【不保留】空格间距
}
}
// good
class Foo {
constructor() {
// 右括号 `)` 须与花括号 `{` 仅保留一个空格间距
}
sayHi() {
}
_say() {
// 方法名与左括号 `(` 之间【不保留】空格间距
}
}
•6.2 定义类时,方法的顺序如下:
// good
class SomeClass {
constructor() {
// constructor
}
get aval() {
// public getter
}
set aval(val) {
// public setter
}
doSth() {
// 公用方法
}
get _aval() {
// private getter
}
set _aval() {
// private setter
}
_doSth() {
// 私有方法
}
}
•6.3 如果不是class类,不使用 new
// bad
function Foo() {
}
const foo = new Foo();
// good
class Foo {
}
const foo = new Foo();
•6.4 使用真正意思上的类Class写法,不使用 prototype 进行模拟扩展,Class更加简洁,易维护
// bad
function Dog(names = []) {
this._names = [...names];
}
Dog.prototype.bark = function() {
const currName = this._names[0];
alert(`one one ${currName}`);
}
// good
class Dog {
constructor(names = []) {
this._names = [...names];
}
bark() {
const currName = this._names[0];
alert(`one one ${currName}`);
}
}
•6.5 class应先定义后使用
// bad
let foo = new Foo();
class SubFoo extends Foo {
}
class Foo {
}
// good
class Foo {
}
let foo = new Foo();
class SubFoo extends Foo {
}
•6.6 this 的注意事项
class Foo {
constructor(x, y) {
this.x = x;
this.y = y;
}
}
// bad
class SubFoo extends Foo {
constructor(x, y, z) {
this.z = z; // 引用错误
super(x, y);
}
}
// good
class SubFoo extends Foo {
constructor(x, y, z) {
super(x, y);
this.z = z; // this 放在 super 后调用
}
setHeight(height) {
this.height = height;
return this;
}
}
•7.1 使用 import / export 来做模块加载导出,不使用非标准模块写法
// bad
const colors = require('./colors');
module.exports = color.lightRed;
// good
import { lightRed } from './colors';
export default lightRed;
•7.1.1 import / export 后面采用花括号 { } 引入模块的写法时,须在花括号内左右各保留一个空格
// bad
import {lightRed} from './colors';
import { lightRed} from './colors';
// good
import { lightRed } from './colors';
•7.2 应确保每个module有且只有一个默认导出模块,方便调用方使用
// bad
const lightRed = '#F07';
export lightRed;
// good
const lightRed = '#F07';
export default lightRed;
•7.3 import 不使用统配符 * 进行整体导入,确保模块与模块之间的关系比较清晰
// bad
import * as colors from './colors';
// good
import colors from './colors';
•7.4 不要将 import 与 export 混合在一行,分开导入与导出,让结构更清晰,可读性更强
// bad
export { lightRed as default } from './colors';
// good
import { lightRed } from './colors';
export default lightRed;
•7.5 多变量要导出时应采用对象解构形式,export 置于底部,使欲导出变量更加清晰
// bad
export const lightRed = '#F07';
export const black = '#000';
export const white = '#FFF';
// good
const lightRed = '#F07';
const black = '#000';
const white = '#FFF';
export default { lightRed, black, white };
1.基本规则
2.命名
3.声明
4.对齐
5.引号
6.空格
7.属性
8.括号
9.标签
10.方法
11.顺序
// bad
const Listing = React.createClass({
// ...
render() {
return <div>{this.state.hello}</div>;
}
});
// good
class Listing extends React.Component {
// ...
render() {
return <div>{this.state.hello}</div>;
}
}
如果你没有使用state或refs,最好不用类,而是使用普通函数(不是箭头函数)
// bad
class Listing extends React.Component {
render() {
return <div>{this.props.hello}</div>;
}
}
// bad (since arrow functions do not have a "name" property)
const Listing = ({ hello }) => (
<div>{hello}</div>
);
// good
function Listing({ hello }) {
return <div>{hello}</div>;
}
// bad
import reservationCard from './ReservationCard';
// good
import ReservationCard from './ReservationCard';
// bad
const ReservationItem = <ReservationCard />;
// good
const reservationItem = <ReservationCard />;
// bad
const Footer = require('./Footer/Footer.jsx')
// bad
const Footer = require('./Footer/index.jsx')
// good
const Footer = require('./Footer')
// bad
export default React.createClass({
displayName: 'ReservationCard',
// stuff goes here
});
// good
export default class ReservationCard extends React.Component {
}
// bad
<Foo superLongParam="bar"
anotherSuperLongParam="baz" />
// good
<Foo
superLongParam="bar"
anotherSuperLongParam="baz"
/>
// if props fit in one line then keep it on the same line
<Foo bar="bar" />
// children get indented normally
<Foo
superLongParam="bar"
anotherSuperLongParam="baz"
>
<Spazz />
</Foo>
对于JSX使用双引号,对其它所有 JS 属性使用单引号。eslint规则: jsx-quotes
为什么?因为 JSX 属性不能包含被转移的引号,并且双引号使得如"don't"一样的连接词很容易被输入。常规的 HTML 属性也应该使用双引号而不是单引号,JSX 属性反映了这个约定。
// bad
<Foo bar='bar' />
// good
<Foo bar="bar" />
// bad
<Foo style={{ left: "20px" }} />
// good
<Foo style={{ left: '20px' }} />
// bad
<Foo/>
// very bad
<Foo />
// bad
<Foo
/>
// good
<Foo />
// bad
<Foo bar={ baz } />
// good
<Foo bar={baz} />
// bad
<Foo
UserName="hello"
phone_number={12345678}
/>
// good
<Foo
userName="hello"
phoneNumber={12345678}
/>
// bad
render() {
return <MyComponent className="long body" foo="bar">
<MyChild />
</MyComponent>;
}
// good
render() {
return (
<MyComponent className="long body" foo="bar">
<MyChild />
</MyComponent>
);
}
// good, when single line
render() {
const body = <div>hello</div>;
return <MyComponent>{body}</MyComponent>;
}
// bad
<Foo className="stuff"></Foo>
// good
<Foo className="stuff" />
// bad
<Foo
bar="bar"
baz="baz" />
// good
<Foo
bar="bar"
baz="baz"
/>
function ItemList(props) {
return (
<ul>
{props.items.map((item, index) => (
<Item
key={item.key}
onClick={() => doSomethingWith(item.name, index)}
/>
))}
</ul>
);
}
// bad
class extends React.Component {
onClickDiv() {
// do stuff
}
render() {
return <div onClick={this.onClickDiv.bind(this)} />
}
}
// good
class extends React.Component {
constructor(props) {
super(props);
this.onClickDiv = this.onClickDiv.bind(this);
}
onClickDiv() {
// do stuff
}
render() {
return <div onClick={this.onClickDiv} />
}
}
// bad
React.createClass({
_onClickSubmit() {
// do stuff
}
// other stuff
});
// good
class extends React.Component {
onClickSubmit() {
// do stuff
}
// other stuff
});
1.optional static methods
2.constructor
3.getChildContext
4.componentWillMount
5.componentDidMount
6.componentWillReceiveProps
7.shouldComponentUpdate
8.componentWillUpdate
9.componentDidUpdate
10.componentWillUnmount
11.clickHandlers or eventHandlers like onClickSubmit() or onChangeDescription()
12.getter methods for render like getSelectReason() or getFooterContent()
13.Optional render methods like renderNavigation() or renderProfilePicture()
14.render
import React, { PropTypes } from 'react';
const propTypes = {
id: PropTypes.number.isRequired,
url: PropTypes.string.isRequired,
text: PropTypes.string,
};
const defaultProps = {
text: 'Hello World',
};
class Link extends React.Component {
static methodsAreOk() {
return true;
}
render() {
return <a href={this.props.url} data-id={this.props.id}>{this.props.text}</a>
}
}
Link.propTypes = propTypes;
Link.defaultProps = defaultProps;
export default Link;
1.displayName
2.propTypes
3.contextTypes
4.childContextTypes
5.mixins
6.statics
7.defaultProps
8.getDefaultProps
9.getInitialState
10.getChildContext
11.componentWillMount
12.componentDidMount
13.componentWillReceiveProps
14.shouldComponentUpdate
15.componentWillUpdate
16.componentDidUpdate
17.componentWillUnmount
18.clickHandlers or eventHandlers like onClickSubmit() or onChangeDescription()
19.getter methods for render like getSelectReason() or getFooterContent()
20.Optional render methods like renderNavigation() or renderProfilePicture()
21.render