@chris-ren
2016-04-27T07:59:02.000000Z
字数 19618
阅读 1208
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
使用字面量语法创建对象
//badvar item = new Object();// goodvar item = {};
不要使用保留字作为键
// badvar superman = {class: 'superhero',default: { clark: 'kent' },private: true};// goodvar superman = {klass: 'superhero',defaults: { clark: 'kent' },hidden: true};
// badvar items = new Array();// goodvar items = [];
var someStack = [];// badsomeStack[someStack.length] = 'abracadabra';// goodsomeStack.push('abracadabra');
var len = items.length,itemsCopy = [],i;// badfor (i = 0; i < len; i++) {itemsCopy[i] = items[i];}// gooditemsCopy = items.slice();
function trigger() {var args = [].slice.apply(arguments);...}
// badvar name = "Bob Parr";// goodvar name = 'Bob Parr';// badvar fullName = "Bob " + this.lastName;// goodvar fullName = 'Bob ' + this.lastName;
// 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.';
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;// 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] = 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.');})();
// badif (currentUser) {function test() {console.log('Nope.');}}// goodif (currentUser) {var test = function test() {console.log('Yup.');};}
// badfunction nope(name, options, arguments) {// ...stuff...}// goodfunction yup(name, options, args) {// ...stuff...}
var luke = {jedi: true,age: 28};function getProp(prop) {return luke[prop];}var isJedi = getProp('jedi');
// badsuperPower = new SuperPower();// goodvar superPower = new SuperPower();
// badvar items = getItems();var goSportsTeam = true;var dragonball = 'z';// goodvar items = getItems(),goSportsTeam = true,dragonball = 'z';
// badvar i, len, dragonball,items = getItems(),goSportsTeam = true;// badvar i, items = getItems(),dragonball,goSportsTeam = true,len;// goodvar items = getItems(),goSportsTeam = true,dragonball,length,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;}
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;}
// 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;}
function Calculator() {// FIXME: shouldn't use a global heretotal = 0;return this;}
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'});
// badvar x=y+5;// goodvar x = y + 5;
// badvar once, upon, aTime;// goodvar 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'};
// 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;(function() {var name = 'Skywalker';return name;})();
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);// 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;// badvar hasAge = new Boolean(age);// goodvar hasAge = Boolean(age);// goodvar hasAge = !!age;
// badthis.__firstName__ = 'Panda';this.firstName_ = 'Panda';// goodthis._firstName = 'Panda';
// badif (!dragon.age()) {return false;}// goodif (!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 变量
// badconst variables;const globalObj = null; // 不是常量let globalObj = null;for (var i = 0; i < 5; i++) {console.log(i);}console.log(i); // 4// goodlet variables;var globalObj = null;for (let i = 0; i < 5; i++) {console.log(i);}console.log(i); // ReferenceError: i is not defined
•1.2 常量
// badlet someNum = 123;const AnotherStr = '不变的字符串';let arr = ['不', '变', '数', '组'];var ANOTHER_OBJ = {'不变对象': true};// goodconst someNum = 123;const anotherStr = '不变的字符串';const arr = ['不', '变', '数', '组'];const anotherObj = {'不变对象': true};
•2.1 处理多行字符串,使用模板字符串,以反引号( ` )标示,可读性更强,代码更易编写。注意排版引起空格的问题,使用场景为声明HTML模板字符串
// badconst tmpl = '<div class="content"> \n' +'<h1>这是换行了。</h1> \n' +'</div>';// goodconst tmpl = `<div class="content"><h1>这是换行了。</h1></div>`;
•2.2 处理字符串拼接变量时,使用模板字符串
// badfunction sayHi(name) {return 'How are you, ' + name + '?';}// goodfunction sayHi(name) {return `How are you, ${name}?`;}
•3.1 嵌套结构的对象层数不能超过3层
// badlet obj = {'one': [{'newTwo': [{'three': ['four': '太多层了,头晕晕']}]}]};// goodlet obj = {'one': ['two',{'twoObj': '结构清晰'}]};
•3.2 解构语句中统一不使用圆括号
// bad[(a)] = [11]; // a未定义let { a: (b) } = {}; // 解析出错// goodlet [a, b] = [11, 22];
•3.3 对象解构
•3.3.1 若函数形参为对象时,使用对象解构赋值
// badfunction someFun(opt) {let opt1 = opt.opt1;let opt2 = opt.opt2;console.log(op1);}// goodfunction someFun(opt) {let { opt1, opt2 } = opt;console.log(`$(opt1) 加上 $(opt2)`);}function someFun({ opt1, opt2 }) {console.log(opt1);}
•3.3.2 若函数有多个返回值时,使用对象解构,不使用数组解构,避免添加顺序的问题
// badfunction anotherFun() {const one = 1, two = 2, three = 3;return [one, two, three];}const [one, three, two] = anotherFun(); // 顺序乱了// one = 1, two = 3, three = 2// goodfunction 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;// badlet temp;temp = x;x = y;y = temp;// good[x, y] = [y, x]; // 交换变量
•3.4.2 将数组成员赋值给变量时,使用数组解构
const arr = [1, 2, 3, 4, 5];// badconst one = arr[0];const two = arr[1];// goodconst [one, two] = arr;
•4.1 将类数组(array-like)对象与可遍历对象(如 Set , Map )转为真正数组,采用 Array.from 进行转换
// badfunction foo() {let args = Array.prototype.slice.call(arguments);}// goodfunction foo() {let args = Array.from(arguments);}
•4.2 数组去重
// goodfunction deduplication(arr) {return Array.from(new Set(arr));}
•4.3 数组拷贝
- 采用数组扩展 ... 形式
const items = [1, 2, 3];// badconst len = items.length;let copyTemp = [];for (let i = 0; i < len; i++) {copyTemp[i] = items[i];}// goodlet copyTemp = [...items];
•4.4 将一组数值转为数组
// badlet arr1 = new Array(2); // [undefined x 2]let arr2 = new Array(1, 2, 3); // [1, 2, 3]// goodlet arr1 = Array.of(2); // [2]let arr2 = Array.of(1, 2, 3); // [1, 2, 3]
•5.1 当要用函数表达式或匿名函数时,使用箭头函数(Arrow Functions),箭头函数更加简洁,并且绑定了this
// badconst 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);}};// goodconst 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 箭头函数书写约定
// goodconst foo = x => x + x; // 注意此处会隐性return x + xconst foo = (x) => {return x + x; // 若函数体有花括号语句块时须进行显性的return};[1, 2, 3].map( x => x * x);
•5.1.2 用箭头函数返回一个对象,应用括号包裹
// badlet test = x => { x: x }; // 花括号会变成语句块,不表示对象// goodlet test = x => ({ x: x }); // 使用括号可正确return {x:x}
•5.2 立即调用函数,应该使用箭头函数
// bad(function() {console.log('哈');})();// good(() => {console.log('哈');})();
•5.3 不使用 arguments , 采用rest语法 ... 代替
// badfunction foo() {let args = Array.prototype.slice.call(arguments);return args.join('');}// goodfunction foo(...args) {return args.join('');}
•5.4 函数参数指定默认值
// badfunction foo(opts) {opts = opts || {};// 此处有将0,''等假值转换掉为默认值的副作用}// goodfunction foo(opts = {}) {console.log('更加简洁,安全');}
•5.5 对象中的函数方法使用缩写形式,更加简洁,函数方法不要使用箭头函数,避免this指向的混乱
// badconst shopObj = {des: '对象模块写法',foo: function() {console.log(this.des);}};const shopObj = {des: '对象模块写法',foo: () => {console.log(this.des); // 此处会变成undefined.des,因为指向顶层模块的this}};// goodconst des = '对象模块写法'; // 使用对象属性值简写方式const shopObj = {des,foo() {console.log(this.des);}};
•6.1 类名应使用帕斯卡写法( PascalCased )
// goodclass SomeClass {}
•6.1.1 类名与花括号须保留一个空格间距,类中的方法定义时,方法名与左括号 ( 之间【不保留】空格间距,类中的方法定义时,右括号 ) 须与花括号 { 【保留】一个空格间距
// badclass Foo{constructor(){// 右括号 `)` 须与花括号 `{` 仅保留一个空格间距}sayHi() {}_say () {// 方法名与左括号 `(` 之间【不保留】空格间距}}// goodclass Foo {constructor() {// 右括号 `)` 须与花括号 `{` 仅保留一个空格间距}sayHi() {}_say() {// 方法名与左括号 `(` 之间【不保留】空格间距}}
•6.2 定义类时,方法的顺序如下:
// goodclass 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
// badfunction Foo() {}const foo = new Foo();// goodclass Foo {}const foo = new Foo();
•6.4 使用真正意思上的类Class写法,不使用 prototype 进行模拟扩展,Class更加简洁,易维护
// badfunction Dog(names = []) {this._names = [...names];}Dog.prototype.bark = function() {const currName = this._names[0];alert(`one one ${currName}`);}// goodclass Dog {constructor(names = []) {this._names = [...names];}bark() {const currName = this._names[0];alert(`one one ${currName}`);}}
•6.5 class应先定义后使用
// badlet foo = new Foo();class SubFoo extends Foo {}class Foo {}// goodclass Foo {}let foo = new Foo();class SubFoo extends Foo {}
•6.6 this 的注意事项
class Foo {constructor(x, y) {this.x = x;this.y = y;}}// badclass SubFoo extends Foo {constructor(x, y, z) {this.z = z; // 引用错误super(x, y);}}// goodclass 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 来做模块加载导出,不使用非标准模块写法
// badconst colors = require('./colors');module.exports = color.lightRed;// goodimport { lightRed } from './colors';export default lightRed;
•7.1.1 import / export 后面采用花括号 { } 引入模块的写法时,须在花括号内左右各保留一个空格
// badimport {lightRed} from './colors';import { lightRed} from './colors';// goodimport { lightRed } from './colors';
•7.2 应确保每个module有且只有一个默认导出模块,方便调用方使用
// badconst lightRed = '#F07';export lightRed;// goodconst lightRed = '#F07';export default lightRed;
•7.3 import 不使用统配符 * 进行整体导入,确保模块与模块之间的关系比较清晰
// badimport * as colors from './colors';// goodimport colors from './colors';
•7.4 不要将 import 与 export 混合在一行,分开导入与导出,让结构更清晰,可读性更强
// badexport { lightRed as default } from './colors';// goodimport { lightRed } from './colors';export default lightRed;
•7.5 多变量要导出时应采用对象解构形式,export 置于底部,使欲导出变量更加清晰
// badexport const lightRed = '#F07';export const black = '#000';export const white = '#FFF';// goodconst lightRed = '#F07';const black = '#000';const white = '#FFF';export default { lightRed, black, white };
1.基本规则
2.命名
3.声明
4.对齐
5.引号
6.空格
7.属性
8.括号
9.标签
10.方法
11.顺序
// badconst Listing = React.createClass({// ...render() {return <div>{this.state.hello}</div>;}});// goodclass Listing extends React.Component {// ...render() {return <div>{this.state.hello}</div>;}}
如果你没有使用state或refs,最好不用类,而是使用普通函数(不是箭头函数)
// badclass 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>);// goodfunction Listing({ hello }) {return <div>{hello}</div>;}
// badimport reservationCard from './ReservationCard';// goodimport ReservationCard from './ReservationCard';// badconst ReservationItem = <ReservationCard />;// goodconst reservationItem = <ReservationCard />;
// badconst Footer = require('./Footer/Footer.jsx')// badconst Footer = require('./Footer/index.jsx')// goodconst Footer = require('./Footer')
// badexport default React.createClass({displayName: 'ReservationCard',// stuff goes here});// goodexport default class ReservationCard extends React.Component {}
// bad<Foo superLongParam="bar"anotherSuperLongParam="baz" />// good<FoosuperLongParam="bar"anotherSuperLongParam="baz"/>// if props fit in one line then keep it on the same line<Foo bar="bar" />// children get indented normally<FoosuperLongParam="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<FooUserName="hello"phone_number={12345678}/>// good<FoouserName="hello"phoneNumber={12345678}/>
// badrender() {return <MyComponent className="long body" foo="bar"><MyChild /></MyComponent>;}// goodrender() {return (<MyComponent className="long body" foo="bar"><MyChild /></MyComponent>);}// good, when single linerender() {const body = <div>hello</div>;return <MyComponent>{body}</MyComponent>;}
// bad<Foo className="stuff"></Foo>// good<Foo className="stuff" />
// bad<Foobar="bar"baz="baz" />// good<Foobar="bar"baz="baz"/>
function ItemList(props) {return (<ul>{props.items.map((item, index) => (<Itemkey={item.key}onClick={() => doSomethingWith(item.name, index)}/>))}</ul>);}
// badclass extends React.Component {onClickDiv() {// do stuff}render() {return <div onClick={this.onClickDiv.bind(this)} />}}// goodclass extends React.Component {constructor(props) {super(props);this.onClickDiv = this.onClickDiv.bind(this);}onClickDiv() {// do stuff}render() {return <div onClick={this.onClickDiv} />}}
// badReact.createClass({_onClickSubmit() {// do stuff}// other stuff});// goodclass 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