接见此原文地点:
http://galaxyteam.pub/didi-fe…别的迎接接见我们保护的
https://www.threejs.online 中文站
(迎接Star!)
本文译者:滴滴出行上海前端(FE)团队杨永乐同砚
基础范例:直接存取
string
number
boolean
null
undefined
symbol
const foo = 1;
let bar = foo;
bar = 9;
console.log(foo, bar); // => 1, 9
symbol
范例不能完整polyfilled,所以请郑重运用庞杂范例: 经由过程援用的体式格局存取
object
array
function
const foo = [1, 2];
const bar = foo;
bar[0] = 9;
console.log(foo[0], bar[0]); // => 9, 9
运用const
说明援用范例,防止运用var
。eslint 设置:prefer-const,no-const-assign
为何?这能确保你没法对援用从新赋值,也不会致使涌现 bug 或难以明白。
// bad
var a = 1;
var b = 2;
// good
const a = 1;
const b = 2;
假如必需对援用范例从新赋值,运用let
而非var
。eslint设置:no-var jscs: disallowVar
为何?比拟于
var
函数作用域,
let
块级作用域更轻易明白
// bad
var count = 1;
if (true) {
count += 1;
}
// good, use the let.
let count = 1;
if (true) {
count += 1;
}
注重let
和const
都是块级作用域
// const and let only exist in the blocks they are defined in.
{
let a = 1;
const b = 1;
}
console.log(a); // ReferenceError
console.log(b); // ReferenceError
运用字面值建立对象。eslint: no-new-object
// bad
const item = new Object();
// good
const item = {};
建立对象的动态属性时,运用盘算属性
为何?如许可以在一个处所定义对象一切的属性
function getKey(k) {
return `a key named ${k}`;
}
// bad
const obj = {
id: 5,
name: 'San Francisco',
};
obj[getKey('enabled')] = true;
// good
const obj = {
id: 5,
[getKey('enabled')]: true,
};
```
运用对象要领的简写情势。 eslint: object-shorthand jscs: requireEnhancedObjectLiterals
为何?要领定义简约清楚
// bad
const atom = {
value: 1,
addValue: function (value) {
return atom.value + value;
},
};
// good
const atom = {
value: 1,
addValue(value) {
return atom.value + value;
},
};
运用属性值简写情势。eslint: object-shorthand jscs: [requireEnhancedObjectLiterals]
为何?誊写越发简约,更有描述性。
const lukeSkywalker = 'Luke Skywalker';
// bad
const obj = {
lukeSkywalker: lukeSkywalker,
};
// good
const obj = {
lukeSkywalker,
};
对象声明时分类简写和非简写的属性名。
为何?更清楚的相识哪些属性是简写的。
const anakinSkywalker = 'Anakin Skywalker';
const lukeSkywalker = 'Luke Skywalker';
// bad
const obj = {
episodeOne: 1,
twoJediWalkIntoACantina: 2,
lukeSkywalker,
episodeThree: 3,
mayTheFourth: 4,
anakinSkywalker,
};
// good
const obj = {
lukeSkywalker,
anakinSkywalker,
episodeOne: 1,
twoJediWalkIntoACantina: 2,
episodeThree: 3,
mayTheFourth: 4,
};
只要对那些不合法的属性名标识符增添引号。eslint: quote-props jscs: disallowQuotedKeysInObjects
为何?对象属性更直观,可读性强。可以代码高亮显现,同时关于大多数的js引擎更轻易优化代码。
// bad
const bad = {
'foo': 3,
'bar': 4,
'data-blah': 5,
};
// good
const good = {
foo: 3,
bar: 4,
'data-blah': 5,
};
不要直接运用Object.prototype
上的要领,比方hasOwnProperty
, propertyIsEnumerable
, 和 isPrototypeOf
。
为何?这些要领能够受对象的其他属性影响。比方
{ hasOwnProperty: false }
也许 对象多是null(
Object.create(null)
)
// bad
console.log(object.hasOwnProperty(key));
const object = Object.create(null);
obj.hasOwnProperty(key) // Uncaught TypeError: obj.hasOwnProperty is not a function
// good
console.log(Object.prototype.hasOwnProperty.call(object, key));
// best
const has = Object.prototype.hasOwnProperty; // cache the lookup once, in module scope.
/* or */
import has from 'has'; // https://www.npmjs.com/package/has
// ...
console.log(has.call(object, key));
浅拷贝对象时引荐运用对象睁开操纵(object spread operator)而不是Object.assign
。运用对象盈余操纵符(object rest operator)猎取对象中盈余的属性。
为何?
Object.assign
运用不当会修正原对象
// very bad
const original = { a: 1, b: 2 };
const copy = Object.assign(original, { c: 3 }); // this mutates `original` ಠ_ಠ
delete copy.a; // so does this
// 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 }
运用字面量声明数组。eslint: no-array-constructor
// bad
const items = new Array();
// good
const items = [];
向数组增添元素时,运用Arrary#push
替换直接赋值。
const someStack = [];
// bad
someStack[someStack.length] = 'abracadabra';
// good
someStack.push('abracadabra');
运用数组睁开操纵符...
拷贝数组
// bad
const len = items.length;
const itemsCopy = [];
let i;
for (i = 0; i
}
// good
const itemsCopy = [...items];
将类数组对象(array-like)转换成数组时,运用...
而不是Array.from
const foo = document.querySelectorAll('.foo');
// good
const nodes = Array.from(foo);
// best
const nodes = [...foo];
当须要对可遍历对象举行map操纵时,运用Array.from
而不是睁开操纵符...
,防止新建一个暂时数组。
// bad
const baz = [...foo].map(bar);
// good
const baz = Array.from(foo, bar);
数组要领回调须要有返回值。假如函数体比较简单,可以直接用表达式,省略return
语句。 eslint: array-callback-return
// good
[1, 2, 3].map((x) => {
const y = x + 1;
return x * y;
});
// good
[1, 2, 3].map(x => x + 1);
// bad - no returned value means `memo` becomes undefined after the first iteration
[[0, 1], [2, 3], [4, 5]].reduce((memo, item, index) => {
const flatten = memo.concat(item);
memo[index] = flatten;
});
// good
[[0, 1], [2, 3], [4, 5]].reduce((memo, item, index) => {
const flatten = memo.concat(item);
memo[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;
});
假如数组有多行,请在翻开和封闭数组括号之前运用换行符
为何? 更具有可读性
// bad
const arr = [
[0, 1], [2, 3], [4, 5],
];
const objectInArray = [{
id: 1,
}, {
id: 2,
}];
const numberInArray = [
1, 2,
];
// good
const arr = [[0, 1], [2, 3], [4, 5]];
const objectInArray = [
{
id: 1,
},
{
id: 2,
},
];
const numberInArray = [
1,
2,
];
接见和运用对象的多个属性时用对象解构操纵。eslint: prefer-destructuring jscs: requireObjectDestructuring
为何?解构可以防止为这些属性建立暂时援用。
// bad
function getFullName(user) {
const firstName = user.firstName;
const lastName = user.lastName;
return `${firstName} ${lastName}`;
}
// good
function getFullName(user) {
const { firstName, lastName } = user;
return `${firstName} ${lastName}`;
}
// best
function getFullName({ firstName, lastName }) {
return `${firstName} ${lastName}`;
}
运用数组解构。eslint: prefer-destructuring jscs: requireArrayDestructuring
const arr = [1, 2, 3, 4];
// bad
const first = arr[0];
const secOnd= arr[1];
// good
const [first, second] = arr;
运用对象解构来完成多个返回值,而不是数组解构。jscs: disallowArrayDestructuringReturn
为何?你可以随时为返回值新增属性而不必体贴属性的递次。
// 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, top } = processInput(input);
字符串运用单引号。eslint: quotes jscs: validateQuoteMarks
// bad
const name = "Capt. Janeway";
// bad - 当须要插值也许换行时才运用模板笔墨
const name = `Capt. Janeway`;
// good
const name = 'Capt. Janeway';
不凌驾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.';
以编程体式格局构建字符串时,运用模板字符串而不是连接符。eslint: prefer-template template-curly-spacing jscs: requireTemplateStrings
为何?模板字符串更加简约,更具可读性。
// bad
function sayHi(name) {
return 'How are you, ' + name + '?';
}
// bad
function sayHi(name) {
return ['How are you, ', name, '?'].join();
}
// bad
function sayHi(name) {
return `How are you, ${ name }?`;
}
// good
function sayHi(name) {
return `How are you, ${name}?`;
}
eval()
要领,它有太多的题目。eslint: no-eval不要过量的转义字符串。eslint: no-useless-escape
为何?反斜杠影响代码可读性,只要在必要的时刻才运用。
// bad
const foo = '\'this\' \i\s \"quoted\"';
// good
const foo = '\'this\' is "quoted"';
const foo = `my name is '${name}'`;
运用定名函数表达式而不是函数声明。eslint: func-style jscs: disallowFunctionDeclarations
为何?函数声明会被提早。这意味着很能够在函数定义前援用该函数,然则不会报错。这不利于代码的可读性和可保护性。假如你发明一个函数定义的很大很庞杂,以至于阻碍了相识文件中的其他内容,那末是时刻把这个函数提取到本身的模块中去了!不要遗忘显现指定表达式的称号,只管它能从变量名中被揣摸出来(当代浏览器也许编译器(如Babel)支撑)。这能让毛病的挪用栈更清楚。(
议论)
// bad
function foo() {
// ...
}
// bad
const foo = function () {
// ...
};
// good
// 函数名和变量援用名差别
const short = function longUniqueMoreDescriptiveLexicalFoo() {
// ...
};
// Is it worse
const sum = function(a, b) {
return a + b;
};
// than this?
const my_sum = function sum(a, b) {
return a + b;
};
第一个函数没有
.name
属性,在debugging过程当中,它会是一个匿名函数。第二个函数有名字为
sum
,你可以检索到它,调试过程当中可以疾速定位。运用banel 和
babel-preset-env
设置,const foo = () => {}
会转换成var foo = function foo () {}
,而且从Node v6最先,const foo = () => {}
中的foo 也有.name
。所以它不再是匿名函数。(函数名字揣摸)
用圆括号包裹马上实行函数表达式(IIFE)。eslint: wrap-iife jscs: requireParenthesesAroundIIFE
为何? 马上实行函数表达式是单一实行单位-运用圆括号包裹挪用,简约明了的示意了这一点。请注重,在通用的模块中,你险些用不到IIFE。
// immediately-invoked function expression (IIFE)
(function () {
console.log('Welcome to the Internet. Please follow me.');
}());
注重:ECMA-262把block
定义为一组语句。然则函数声明不是语句。
// bad
if (currentUser) {
function test() {
console.log('Nope.');
}
}
// good
let test;
if (currentUser) {
test = () => {
console.log('Yup.');
};
}
永久不要把参数定名为arguments
。这将庖代本来函数作用域内的 arguments
对象。
// bad
function foo(name, options, arguments) {
// ...
}
// good
function foo(name, options, args) {
// ...
}
不要运用arguments
。可以挑选 rest 语法 ...
替换。
为何?运用
...
能明白你要传入的参数。别的 rest 参数是一个真正的数组,而
arguments
是一个类数组。
// bad
function concatenateAll() {
const args = Array.prototype.slice.call(arguments);
return args.join('');
}
// good
function concatenateAll(...args) {
return args.join('');
}
运用函数默许参数指定默许值,而不是用一个可变的函数参数
// really bad
function handleThings(opts) {
// 不!我们不应当转变函数参数
// 更蹩脚的是: 假如 opts 是 falsy (为''也许是false), 它依然会被赋值为对象,然则这能够会激发bug
opts = opts || {};
// ...
}
// still bad
function handleThings(opts) {
if (opts === void 0) {
opts = {};
}
// ...
}
// good
function handleThings(opts = {}) {
// ...
}
8.运用函数参数默许值的时防止副作用。
> 为何?如许的写法会让人疑心。
```Javascript
var b = 1;
// bad
function count(a = b++) {
console.log(a);
}
count(); // 1
count(); // 2
count(3); // 3
count(); // 3
```
参数默许值放在函数参数列表的末了。
// bad
function handleThings(opts = {}, name) {
// ...
}
// good
function handleThings(name, opts = {}) {
// ...
}
不要运用Function
组织器建立函数。 eslint: no-new-func
为何?经由过程这类体式格局建立的函数和运用
eval()
相似,会带来不确定的题目
// bad
var add = new Function('a', 'b', 'return a + b');
// still bad
var subtract = Function('a', 'b', 'return a - b');
函数名双方留白。eslint: space-before-function-paren [space-before-blocks]
为何?坚持代码一致性,当你增添也许删除名字时不须要分外增减空格。
// bad
const f = function(){};
const g = function (){};
const h = function() {};
// good
const x = function () {};
const y = function a() {};
不要修正参数。 eslint: no-param-reassign
为何?操纵参数对象会在原始挪用方中致使不可预知的变量副作用。
// bad
function f1(obj) {
obj.key = 1;
}
// good
function f2(obj) {
const key = Object.prototype.hasOwnProperty.call(obj, 'key') ? obj.key : 1;
}
不要给参数赋值。eslint: no-param-reassign
为何?从新分配参数能够会致使不测的行动,特别是在接见参数对象时。 它也能够致使优化题目,特别是在V8中。
// bad
function f1(a) {
a = 1;
// ...
}
function f2(a) {
if (!a) { a = 1; }
// ...
}
// good
function f3(a) {
const b = a || 1;
// ...
}
function f4(a = 1) {
// ...
}
运用睁开操纵符...
挪用可变参数函数。eslint: prefer-spread
为何?它更简约,你不须要供应上下文,而且组合运用
new
和
apply
不轻易。
// bad
const x = [1, 2, 3, 4, 5];
console.log.apply(console, x);
// good
const x = [1, 2, 3, 4, 5];
console.log(...x);
// bad
new (Function.prototype.bind.apply(Date, [null, 2016, 8, 5]));
// good
new Date(...[2016, 8, 5]);
带有多行函数署名或挪用的函数应当像本指南中的其他多行列表一样缩进:每行中包括一项,末了一个项目带有逗号。
// bad
function foo(bar,
baz,
quux) {
// ...
}
// good
function foo(
bar,
baz,
quux,
) {
// ...
}
// bad
console.log(foo,
bar,
baz);
// good
console.log(
foo,
bar,
baz,
);
当你必须要运用匿名函数(如在通报内联回调时),请运用箭头函数。eslint: prefer-arrow-callback, arrow-spacing jscs: requireArrowFunctions
为何?因为箭头函数制造了新的一个 this 实行环境,一般情况下都能满足你的需求,而且如许的写法更加简约。(参考
Arrow functions – Javascript | MDN )为何不?假如你有一个相称庞杂的函数,你也许可以把逻辑部份转移到一个函数声明上。
// 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 都省略掉。假如不是,那就不要省略。eslint: arrow-parens, arrow-body-style jscs: disallowParenthesesAroundArrowParam, requireShorthandArrowFunctions
为何?这是一个很好用的语法糖。在链式挪用中可读性很高。
// bad
[1, 2, 3].map(number => {
const nextNumber = number + 1;
`A string containing the ${nextNumber}.`;
});
// good
[1, 2, 3].map(number => `A string containing the ${number}.`);
// good
[1, 2, 3].map((number) => {
const nextNumber = number + 1;
return `A string containing the ${nextNumber}.`;
});
// good
[index]: number,
}));
// No implicit return with side effects
function foo(callback) {
const val = callback();
if (val === true) {
// Do something if callback returns true
}
}
let bool = false;
// bad
foo(() => bool = true);
// good
foo(() => {
bool = true;
});
```
假如表达式太长须要多行示意,请将其包括在括号中,增添可读性。
为何?它能消灭的标识函数的最先和完毕位置。
// bad
['get', 'post', 'put'].map(httpMethod => Object.prototype.hasOwnProperty.call(
httpMagicObjectWithAVeryLongName,
httpMethod,
)
);
// good
['get', 'post', 'put'].map(httpMethod => (
Object.prototype.hasOwnProperty.call(
httpMagicObjectWithAVeryLongName,
httpMethod,
)
));
假如函数只要一个参数而且函数体没有运用花括号,那就省略括号。不然,为了坚持清楚一致性,总在参数四周加上括号。老是运用括号也是可以接收的,在这类情况下运用eslint的 “always” option 也许不要在jscs中引入 disallowParenthesesAroundArrowParam。eslint: arrow-parens jscs: disallowParenthesesAroundArrowParam
为何? 不那末杂沓,可读性强。
// 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;
});
防止箭头函数语法(=>
)和比较运算符(<=
,=>
)一同运用时带来的疑心。
// bad
const itemHeight = item => item.height > 256 ? item.largeSize : item.smallSize;
// bad
const itemHeight = (item) => item.height > 256 ? item.largeSize : item.smallSize;
// good
const itemHeight = item => (item.height > 256 ? item.largeSize : item.smallSize);
// good
const itemHeight = (item) => {
const { height, largeSize, smallSize } = item;
return height > 256 ? largeSize : smallSize;
};
老是运用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;
}
}
运用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];
}
}
要领可以返回 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);
可以写一个自定义的 toString() 要领,但要确保它能一般运转而且不会引发副作用。
class Jedi {
constructor(optiOns= {}) {
this.name = options.name || 'no name';
}
getName() {
return this.name;
}
toString() {
return `Jedi - ${this.getName()}`;
}
}
类有默许组织器。一个空的组织函数也许只是重载父类组织函数是不必要的。eslint: no-useless-constructor
// 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';
}
}
防止反复的类成员。eslint: no-dupe-class-members
为何?反复的类成员声明中只要末了一个见效-反复的声明肯定是一个毛病。
// bad
class Foo {
bar() { return 1; }
bar() { return 2; }
}
// good
class Foo {
bar() { return 1; }
}
// good
class Foo {
bar() { return 2; }
}
老是运用模组 (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;
不要运用通配符 import
为何?如许确保只要一个默许的export
// bad
import * as AirbnbStyleGuide from './AirbnbStyleGuide';
// good
import AirbnbStyleGuide from './AirbnbStyleGuide';
不要直接从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;
同一个途径只运用一次import
。eslint: no-duplicate-imports
为何?雷同途径有多个
import
会致使代码难以保护。
// bad
import foo from 'foo';
// … some other imports … //
import { named1, named2 } from 'foo';
// good
import foo, { named1, named2 } from 'foo';
// good
import foo, {
named1,
named2,
} from 'foo';
不要export
可变的绑定。 eslint: import/no-mutable-exports
为何?防止不确定的可变量,特别是
export
可变的绑定。假如某些特殊情况须要运用这类场景,一般应当
export
常量援用。
// bad
let foo = 3;
export { foo };
// good
const foo = 3;
export { foo };
模块中只要单个export
,最好运用default export
。 eslint: import/prefer-default-export
为何?一个文件最好只做一件事,如许更具有可读性和可保护性。
// bad
export function foo() {}
// good
export default function foo() {}
将一切的import
语句放在文件的顶部。eslint: import/first
为何?因为
import
s会被提拔,最好坚持它们在顶部以防涌现不可预期的行动。
// bad
import foo from 'foo';
foo.init();
import bar from 'bar';
// good
import foo from 'foo';
import bar from 'bar';
foo.init();
多行import
应当和多行数组和对象一样有缩进。
为何?花括号须要遵照与指南中的每一个其他花括号雷同的缩进划定规矩,末端的逗号也一样。
// bad
import {longNameA, longNameB, longNameC, longNameD, longNameE} from 'path';
// good
import {
longNameA,
longNameB,
longNameC,
longNameD,
longNameE,
} from 'path';
制止在模块导入语句中运用Webpack加载器语法。eslint: import/no-webpack-loader-syntax
为何?在
import
中运用webpack 语法会将代码耦合进bundler中。引荐在
webpack.config.js
中设置loader 划定规矩。
// bad
import fooSass from 'css!sass!foo.scss';
import barCss from 'style!css!bar.css';
// good
import fooSass from 'foo.scss';
import barCss from 'bar.css';