# Airbnb 자바스크립트 스타일 가이드 2020-09-22
no-whitespace-before-property 부터 eslint 레퍼런스를 읽었습니다.
파일 목록에 분류되어 있는 포스트는 eslint 를 읽고 업데이트 한 게시글 입니다.
# Classes & Constructors
# 클래스 사용 > 프로토타입 직접조작
- 클래스 문법은 더 간결하고, 쉽다.
// 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;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 상속: extends 사용
- extends
- instanceof를 파괴하지 않고(?TODO) 프로포타입 상속을 위한 내장 방법
// 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];
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 클래스 메서드 체이닝: 메서드가 this 를 return
// 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);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# 클래스의 커스텀 toString() 메서드
- 사용해도 되지만 성공적으로 작동하거나 사이드 이펙트가 없는지 확인해야 한다.
class Jedi {
constructor(options = {}) {
this.name = options.name || 'no name';
}
getName() {
return this.name;
}
toString() {
return `Jedi - ${this.getName()}`;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
# default 생성자
- 빈 constructor 는 생략해도 된다.
- 빈 생성자를 가지는 클래스를 상속하는 클래스에서, 또한 빈 생성자를 가지고 있을 땐 constructor 를 써주지 않아도 된다.
// bad
class Jedi {
constructor() {}
getName() {
return this.name;
}
}
// bad
class Rey extends Jedi {
constructor(...args) {
super(...args);
}
}
class Jedi {
getName() {
return this.name;
}
}
class Rey extends Jedi { }
// good
class Rey extends Jedi {
constructor(...args) {
super(...args);
this.name = 'Rey';
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# 중복된 클래스 멤버를 가지는 클래스 X
- 중복된 클래스 멤버 선언은. 클래스 멤버를 사용할 때 마지막 것을 선택한다.
- 중복된 클래스 멤버를 가질 수 있는 것은 버그이다.
// bad
class Foo {
bar() { return 1; }
bar() { return 2; }
}
// good
class Foo {
bar() { return 1; }
}
// good
class Foo {
bar() { return 2; }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 클래스 메서드에서 this 를 사용하지 않을 경우 : static 메서드 사용
// bad
class Foo {
bar() {
console.log('bar');
}
}
// good - this 를 사용할 경우 -> 일반 메서드
class Foo {
bar() {
console.log(this.bar);
}
}
// good - constructor 를 생략할 수 있다.
class Foo {
constructor() {
// ...
}
}
// good - this 를 사용하지 않은 경우 -> static methods
class Foo {
static bar() {
console.log('bar');
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# Modules
# (import/export) > non-standard 모듈 시스템.
- (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;
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
# * wildcard import X
- default Export 인지 named export 인지 헷갈릴 수 있다.
// bad
import * as AirbnbStyleGuide from './AirbnbStyleGuide';
// good
import AirbnbStyleGuide from './AirbnbStyleGuide';
1
2
3
4
5
2
3
4
5
# from 으로 Import 후 직접적인 export X
- 간결하지만, export / import 의 일관성을 위해 분리한다.
// bad
// filename es6.js
export { es6 as default } from './AirbnbStyleGuide';
// good
// filename es6.js
import { es6 } from './AirbnbStyleGuide';
export default es6;
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
# 하나의 path : 하나의 import
- 하나의 path 에서 여러개의 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';
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
# mutable: export X
- 특수한 경우를 제외하고 상수 reference 만 export 한다.
// bad
let foo = 3;
export { foo };
// good
const foo = 3;
export { foo };
1
2
3
4
5
6
7
2
3
4
5
6
7
# 모듈에 export 가 하나만 있을 때: export default > named export
- 파일에 하나의 export 를 권장
- 가독성, 유지보수에 낫다.
// bad
export function foo() {}
// good
export default function foo() {}
1
2
3
4
5
2
3
4
5
# 모든 import 는 non-import 명령문 위에 두기
- import 는 호이스팅 된다.
- 호이스팅이 되는 것들은 아래에 두었을때 문제가 발생할 가능성이 있다는 말?
// bad
import foo from 'foo';
foo.init();
import bar from 'bar';
// good
import foo from 'foo';
import bar from 'bar';
foo.init();
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
# 여러줄의 imports : 들여쓰기(indent)
// bad
import {longNameA, longNameB, longNameC, longNameD, longNameE} from 'path';
// good
import {
longNameA,
longNameB,
longNameC,
longNameD,
longNameE,
} from 'path';
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
# import 문: Webpack loader 구문 X
- import 에서 webpack 구문을 사용하면 코드를 모듈 번들러에 연결한다 (TODO 웹팩에 대한 지식 부족..)
- webpack.config.js 에서 로더 구문을 사용하는 것이 좋다.
// 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';
1
2
3
4
5
6
7
2
3
4
5
6
7
# import/extensions : 자바스크립트 파일이름 extensions X
- extensions 포함의 문제점.
- 리팩토링이 금지된다.
- (? TODO .js -> .jsx .. 등 js 관련 확장자를 다 수정해 줘야하는 문제점?)
- 리팩토링이 금지된다.
// bad
import foo from './foo.js';
import bar from './bar.jsx';
import baz from './baz/index.jsx';
// good
import foo from './foo';
import bar from './bar';
import baz from './baz';
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
# Iterators and Generators
# 이터레이터 사용: 고차 함수 > 루프 for-in / for-of
- 고차 함수
- 불변 규칙을 강제하기 위함이다.
- 값을 리턴하는 순수 함수를 다루는 것이 더 쉽기 때문에 사이드 이펙트도 작을 수 있음.
- 배열 순회
- map()
- every()
- filter()
- find()
- findIndex()
- reduce()
- some() / ...
- 객체 -> 배열
- Object.keys()
- Object.values()
- Object.entries()
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;
// bad
const increasedByOne = [];
for (let i = 0; i < numbers.length; i++) {
increasedByOne.push(numbers[i] + 1);
}
// good
const increasedByOne = [];
numbers.forEach((num) => {
increasedByOne.push(num + 1);
});
// best (keeping it functional)
const increasedByOne = numbers.map((num) => num + 1);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# 현재 제너레이터는 ES5 트랜스파일링이 잘 되지 않는다.
# 제너레이터의 * 연산자의 적절한 공백 : function* foo()
- function, * -> 같은 개념적 키워드
- 는 함수에 대한 수정자가 아님
- function * 은 함수와 다른 고유 한 구성임 ??
// bad
function * foo() {
// ...
}
// bad
const bar = function * () {
// ...
};
// bad
const baz = function *() {
// ...
};
// bad
const quux = function*() {
// ...
};
// bad
function*foo() {
// ...
}
// bad
function *foo() {
// ...
}
// very bad
function
*
foo() {
// ...
}
// very bad
const wat = function
*
() {
// ...
};
// good
function* foo() {
// ...
}
// good
const foo = function* () {
// ...
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# Properties
# 프로퍼티 접근: dot 표기법 사용
const luke = {
jedi: true,
age: 28,
};
// bad
const isJedi = luke['jedi'];
// good
const isJedi = luke.jedi;
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
# 변수로 프로퍼티 접근 : [] 사용
const luke = {
jedi: true,
age: 28,
};
function getProp(prop) {
return luke[prop];
}
const isJedi = getProp('jedi');
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
# 지수 연산 : ** > Math.pow
// bad
const binary = Math.pow(2, 10);
// good
const binary = 2 ** 10;
1
2
3
4
5
2
3
4
5
# Variables
# 변수 선언 : 항상 const / let
- const / let
- 전역 변수가 생성되지 않는다.
- 전역 변수 공간을 어지럽히면 안된다.
// bad
superPower = new SuperPower();
// good
const superPower = new SuperPower();
1
2
3
4
5
2
3
4
5
# 하나의 변수 선언/할당 : 하나의 const/let
- 새로 변수를 선언하고 추가할 때 더 쉽다.
- 하나의 const/let 에 여러 변수를 나열하면, ; 와 , 을 고려해야 한다.
- 디버그를 할 때, 한줄로 쓰면 한번에 뛰어넘는다.
- 단계별로 관찰 할 수 없다.
// bad
const items = getItems(),
goSportsTeam = true,
dragonball = 'z';
// bad
// (compare to above, and try to spot the mistake)
const items = getItems(),
goSportsTeam = true;
dragonball = 'z';
// good
const items = getItems();
const goSportsTeam = true;
const dragonball = 'z';
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# const/ let 의 그룹화
- 그룹화는, 나중에 이전에 할당 된 변수 중 하나에 따라 변수를 할당해야 할 때 유용합니다. (?TODO)
// bad
let i, len, dragonball,
items = getItems(),
goSportsTeam = true;
// 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;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 합리적인 위치에 변수할당문 사용
- 필요한 위치 근처에 둔다.
- let/const 은 block 스코프 이기 때문임.
// bad - unnecessary function call
function checkName(hasName) {
const name = getName();
if (hasName === 'test') {
return false;
}
if (name === 'test') {
this.setName('');
return false;
}
return name;
}
// good
function checkName(hasName) {
if (hasName === 'test') {
return false;
}
const name = getName();
if (name === 'test') {
this.setName('');
return false;
}
return name;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# 변수 할당문의 체이닝 X
- 변수 할당문의 체이닝은 암묵적으로 글로벌 변수를 만들어 낸다.
// bad
(function example() {
let a = b = c = 1;
// let a = ( b = ( c = 1 ) );
// let 은 a 에게만 적용된다.
// b 와 c 는 전역 변수가 된다.
}());
console.log(a); // throws ReferenceError
console.log(b); // 1
console.log(c); // 1
// good
(function example() {
let a = 1;
let b = a;
let c = a;
}());
console.log(a); // throws ReferenceError
console.log(b); // throws ReferenceError
console.log(c); // throws ReferenceError
// `const` 도 마찬가지.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 단항 증감 (++, --) X
- 단항 증감은 세미콜론 자동 삽입의 대상이 된다.
- silent errors 의 원인이 될 수 있다.
num++
/num ++
보다num += 1
이 더 바람직하다.- 단항 증감을 사용하지 않으면,
- pre-incrementing/pre-decrementing 실수를 방지할 수 있다.
// bad
const array = [1, 2, 3];
let num = 1;
num++;
--num;
let sum = 0;
let truthyCount = 0;
for (let i = 0; i < array.length; i++) {
let value = array[i];
sum += value;
if (value) {
truthyCount++;
}
}
// good
const array = [1, 2, 3];
let num = 1;
num += 1;
num -= 1;
const sum = array.reduce((a, b) => a + b, 0);
const truthyCount = array.filter(Boolean).length;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# = 할당문 전후로 줄바꿈 X, 줄바꿈이 필요하다면 () 사용
- 할당문 전후로 줄바꿈이 발생하면 난독화를 일으킬 수 있다.
// bad
const foo =
superLongLongLongLongLongLongLongLongFunctionName();
// bad
const foo
= 'superLongLongLongLongLongLongLongLongString';
// good
const foo = (
superLongLongLongLongLongLongLongLongFunctionName()
);
// good
const foo = 'superLongLongLongLongLongLongLongLongString';
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 사용하지 않은 변수는 비허용된다.
- 선언되었지만 사용되지 않은 변수는 리팩토링이 완전하게 되지 않았다는 의미이다.
- 코드 공간을 불필요하게 차지함.
- 읽는 사람에게 혼동을 줌.
// bad
var some_unused_var = 42;
// y 는 쓰여지기만 했지 사용되지 않았다.
var y = 10;
y = 5;
// 사용되지 않은 변수
var z = 0;
z = z + 1;
// 사용되지 않은 함수
function getX(x, y) {
return x;
}
// good
function getXPlusY(x, y) {
return x + y;
}
var x = 1;
var y = a + 2;
alert(getXPlusY(x, y));
// 나머지 프로퍼티 연산자: 객체에서 특정 키를 의도적으로 빠뜨리고 추출하는 방법이다.
// 'type' 은 사용하지 않아도 괜찮다.
var { type, ...coords } = data;
// 'coords' 는 data 객체에서 'type' 프로퍼티가 빠진 객체다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# Hoisting
# var, const/let 선언의 호이스팅:
- var
- 선언문은 가장 가까운 함수 스코프의 최상단으로 호이스팅 된다.
- 할당문은 호이스팅 되지 않는다.
- const/let
- 선언에는 Temporal Dead Zones (TDZ) 가 존재한다.
typeof
는 안전하지 않다. (예시 참고)
// 아래 함수는 참조 에러를 일으킨다 (notDefined 라는 전역 변수가 존재하지 않을 때)
function example() {
console.log(notDefined); // => throws a ReferenceError
}
// declaredButNotAssigned 는 호이스팅이 일어나기 때문에 사용가능하다.
// Note: 하지만 true 할당은 호이스팅 되지 않음.
function example() {
console.log(declaredButNotAssigned); // => undefined
var declaredButNotAssigned = true;
}
// 인터프리터는 변수의 선언을 스코프의 최상단으로 호이스팅 한다.
function example() {
let declaredButNotAssigned;
console.log(declaredButNotAssigned); // => undefined
declaredButNotAssigned = true;
}
// const / let 의 예시
function example() {
console.log(declaredButNotAssigned); // => throws a ReferenceError
console.log(typeof declaredButNotAssigned); // => throws a ReferenceError
const declaredButNotAssigned = true;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 14.2 익명함수 표현식의 호이스팅 : 변수 이름 O, 함수할당 X
function example() {
console.log(anonymous); // => undefined
anonymous(); // => TypeError anonymous is not a function
var anonymous = function () {
console.log('anonymous function expression');
};
}
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
# 이름있는 함수 표현식의 호이스팅: 변수이름 O, 함수이름과 body X
function example() {
console.log(named); // => undefined
named(); // => TypeError named is not a function
superPower(); // => ReferenceError superPower is not defined
var named = function superPower() {
console.log('Flying');
};
}
// 할당된 변수이름과 함수 표현식의 이름기 같은 경우.
function example() {
console.log(named); // => undefined
named(); // => TypeError named is not a function
var named = function named() {
console.log('named');
};
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 함수 선언문의 호이스팅: 함수 이름, body O
function example() {
superPower(); // => Flying
function superPower() {
console.log('Flying');
}
}
1
2
3
4
5
6
7
2
3
4
5
6
7
# Comparison Operators & Equality
# (===) (!==) > (==)
# Conditional statements
if
와 같은 조건문
ToBoolean
의 추상 메서드로 강제변환을 사용하여 식을 평가한다.- 식을 평가하는 규칙은 다음과 같다.
- Objects -> true
- Undefined -> false
- Null -> false
- Booleans -> true / false
- Numbers
- false : +0, -0, NaN
- otherwise true
- Strings
- false : empty string ''
- otherwise true
if ([0] && []) {
// true
// 배열은 객체이다. 빈객체도 객체임
// 객체는 항상 true
}
1
2
3
4
5
2
3
4
5
# reference: boolean -> 축약 , 문자열/숫자 -> 명시적 비교
// bad
if (isValid === true) {
// ...
}
// good
if (isValid) {
// ...
}
// bad
if (name) {
// ...
}
// good
if (name !== '') {
// ...
}
// bad
if (collection.length) {
// ...
}
// good
if (collection.length > 0) {
// ...
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# Truth Equality and JavaScript by Angus Croll.🔗
# 어휘 선언이 있는 case/default 문 : { } braces 사용
- Lexical declarations [어휘 선언]
- let, const, function, and class
- 스위치 블록 스코프가 존재한다.
- case/default 에 { } 가 없다면 여러 case 절에서 동일한 것을 정의하려고 할 때 문제가 발생함.
// bad
switch (foo) {
case 1:
let x = 1;
break;
case 2:
const y = 2;
break;
case 3:
function f() {
// ...
}
break;
default:
class C {}
}
switch (1) {
case 1:
let v ;
break;
case 2:
let v; // Uncaught SyntaxError: Identifier 'v' has already been declared
break;
case 3:
function f() {
// ...
}
break;
default:
class C {}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// 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 {}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 삼항의 중첩 X : 기본적으로 단일행 표현식
// bad
const foo = maybe1 > maybe2
? "bar"
: value1 > value2 ? "baz" : null;
// split into 2 separated ternary(삼항) expressions
const maybeNull = value1 > value2 ? 'baz' : null;
// better
const foo = maybe1 > maybe2
? 'bar'
: maybeNull;
// best
const foo = maybe1 > maybe2 ? 'bar' : maybeNull;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 불필요한 삼항 표현식 X
// bad
const foo = a ? a : b;
const bar = c ? true : false;
const baz = c ? false : true;
// good
const foo = a || b;
const bar = !!c;
const baz = !c;
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
# 혼합 연산자 : () 로 감싸기
- 혼합연산자는 우선순위가 모호할 수 있다.
- +, -, ** 는 예외
- /, * 는 모호 하다.
// bad
const foo = a && b < 0 || c > 0 || d + 1 === 0;
// bad
const bar = a ** b - 5 % d;
// bad
// (a || b) && c 와 혼동될 수 있다.
if (a || b && c) {
return d;
}
// bad
const bar = a + b / c * d;
// good
const foo = (a && b < 0) || c > 0 || (d + 1 === 0);
// good
const bar = a ** b - (5 % d);
// good
if (a || (b && c)) {
return d;
}
// good
const bar = a + (b / c) * d;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# Blocks
# 여러줄의 블록 : {} 사용
// bad
if (test)
return false;
// good
if (test) return false;
// good
if (test) {
return false;
}
// bad
function foo() { return false; }
// good
function bar() {
return false;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# if/else 여러줄 블록 : if 의 닫기 } 와 같은 줄에 else 두기
// bad
if (test) {
thing1();
thing2();
}
else {
thing3();
}
// good
if (test) {
thing1();
thing2();
} else {
thing3();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# return 을 포함하는 if 블록 + else/else if
- return 을 포함하는 if 블록
- 후속 else 불필요
- 후속 else if 의 리턴 -> if 리턴으로 분리
// bad
function foo() {
if (x) {
return x;
} else {
return y;
}
}
// good
function foo() {
if (x) {
return x;
}
return y;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// bad
function cats() {
if (x) {
return x;
} else if (y) {
return y;
}
}
// good
function cats() {
if (x) {
return x;
}
if (y) {
return y;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// bad
function dogs() {
if (x) {
return x;
} else {
if (y) {
return y;
}
}
}
// good
function dogs(x) {
if (x) {
if (z) {
return y;
}
} else {
return z;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# Control Statements
- if, while etc
# 제어문의 아주 긴 조건 : 줄바꿈, 논리연산은 새 줄에 시작
- 연산을 새 줄에 시작하는 것
- 메서드 체이닝 패턴
- 가독성
// 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();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# 제어문 > 선택 연산자
// bad
!isRunning && startRunning();
// good
if (!isRunning) {
startRunning();
}
1
2
3
4
5
6
7
2
3
4
5
6
7
# Comments
# 멀티라인 주석 /** ... */ > //
// bad
// make() returns a new element
// based on the passed in tag name
//
// @param {String} tag
// @return {Element} element
function make(tag) {
// ...
return element;
}
// good
/**
* make() returns a new element
* based on the passed-in tag name
*/
function make(tag) {
// ...
return element;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# // : 한줄 주석
- 주석의 대상 위에 한줄로 표기
- 주석 위에는 한 줄 비우기
- 주석이 블록이 첫줄이 아닌경우 제외
// 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;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# 주석의 시작은 항상 공백
- 가독성
// 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;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# FIXME / TODO
- 실행 가능한 주석
- FIXME: 재검토해야 할 문제를 지적
- TODO: 구현해야하는 문제
class Calculator extends Abacus {
constructor() {
super();
// FIXME: 여기서 전역을 사용해서는 안됩니다.
total = 0;
}
}
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
class Calculator extends Abacus {
constructor() {
super();
// TODO: total은 옵션 매개 변수로 구성 할 수 있어야합니다.
this.total = 0;
}
}
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
# Whitespace
# 들여쓰기 indent : 2 space character
// bad
function foo() {
∙∙∙∙let name;
}
// bad
function bar() {
∙let name;
}
// good
function baz() {
∙∙let name;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
# space-before-blocks: 여는 { 의 앞에 공백 하나
// 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',
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# keyword-spacing : 제어문과 함수의 호출/선언
- 제어문 : 여는 ( 앞에 공백 하나
- 함수 호출/선언: 여는 ( 앞에 공백 X
// bad
if(isJedi) {
fight ();
}
// good
if (isJedi) {
fight();
}
// bad
function fight () {
console.log ('Swooosh!');
}
// good
function fight() {
console.log('Swooosh!');
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# space-infix-ops : 연산자 사이 공백
// bad
const x=y+5;
// good
const x = y + 5;
1
2
3
4
5
2
3
4
5
# eol-last : 파일 마지막 - 하나의 newline(개행) 문자로 끝내기
- 비어 있지 않은 파일의 후행 줄 바꿈은 일반적인 UNIX 관용구입니다.
- 후행 줄 바꿈의 이점은 쉘 프롬프트를 방해하지 않고 파일 및 출력 파일을 터미널에 연결하거나 추가하는 기능을 포함합니다.
// bad
import { es6 } from './AirbnbStyleGuide';
// ...
export default es6;
1
2
3
4
2
3
4
// bad
import { es6 } from './AirbnbStyleGuide';
// ...
export default es6;
↵
1
2
3
4
5
2
3
4
5
import { es6 } from './AirbnbStyleGuide';
// ...
export default es6;↵
1
2
3
2
3
# newline-per-chained-call 체이닝 마다 개행
- 긴 메서드 체이닝 (2개이상)
- 들여쓰기
- . dot 으로 시작하기
- . 으로 시작하는 것 새로운 문이 아닌 메서드 호출이라는 것을 강조할 수 있다.
// 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').classed('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);
// good
const leds = stage.selectAll('.led').data(data);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# no-whitespace-before-property 🔗 프로퍼티 앞에 공백 X
- javascript 는 객체와 속성 사이의 공백을 허용한다.
- 가독성을 저하하고 오류를 유발할 수 있다.
foo. bar .baz . quz
1
- 점 주위 (), [] 괄호 앞에 공백을 쓰지 않아야 된다.
- 체이닝의 개행에서만 허용 한다.
foo
.bar()
.baz()
.qux()
1
2
3
4
2
3
4
/*eslint no-whitespace-before-property: "error"*/
foo [bar] // foo.bar
foo. bar // foo[bar]
foo .bar // foo[ bar ]
foo. bar. baz // foo.bar.baz
foo. bar() // foo
.baz() // .bar().baz()
foo
.bar(). baz()
/*
foo
.bar()
.baz()
foo.
bar().
baz()
*/
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 블록 다음/ 다음 statement 앞 : 빈 줄
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;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# padded-blocks: 블록을 빈 줄로 채우지 말것
// 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);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# no-multiple-empty-lines: 멀티라인 빈줄 X
- 공백은 코드의 논리적 섹션을 분리하는데 유용하지만 과도한 공백은 화면을 더 많이 차지 한다.
- 코드를 읽을 때 필요한 스크롤을 줄이는 것을 목표로 한다.
// bad
class Person {
constructor(fullName, email, birthday) {
this.fullName = fullName;
this.email = email;
this.setAge(birthday);
}
setAge(birthday) {
const today = new Date();
const age = this.getAge(today, birthday);
this.age = age;
}
getAge(today, birthday) {
// ..
}
}
// good
class Person {
constructor(fullName, email, birthday) {
this.fullName = fullName;
this.email = email;
this.setAge(birthday);
}
setAge(birthday) {
const today = new Date();
const age = getAge(today, birthday);
this.age = age;
}
getAge(today, birthday) {
// ..
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# space-in-parens : ( ) 안에 공백 X
// bad
function bar( foo ) {
return foo;
}
// good
function bar(foo) {
return foo;
}
// bad
if ( foo ) {
console.log(foo);
}
// good
if (foo) {
console.log(foo);
}
// bad
foo( 'bar' );
var x = ( 1 + 2 ) * 3;
// good
foo('bar');
var x = (1 + 2) * 3;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# array-bracket-spacing : [ ] 안에 공백 X
// bad
const foo = [ 1, 2, 3 ];
console.log(foo[ 0 ]);
// good
const foo = [1, 2, 3];
console.log(foo[0]);
1
2
3
4
5
6
7
2
3
4
5
6
7
# object-curly-spacing : { } 안에 공백 O
// bad
const foo = {clark: 'kent'};
// good
const foo = { clark: 'kent' };
1
2
3
4
5
2
3
4
5
# 한줄에 100 자(공백 포함) 이상 X
// bad
const foo = jsonData && jsonData.foo && jsonData.foo.bar && jsonData.foo.bar.baz && jsonData.foo.bar.baz.quux && jsonData.foo.bar.baz.quux.xyzzy;
// bad
$.ajax({ method: 'POST', url: 'https://airbnb.com/', data: { name: 'John' } }).done(() => console.log('Congratulations!')).fail(() => console.log('You have failed this city.'));
// good
const foo = jsonData
&& jsonData.foo
&& jsonData.foo.bar
&& jsonData.foo.bar.baz
&& jsonData.foo.bar.baz.quux
&& jsonData.foo.bar.baz.quux.xyzzy;
// good
$.ajax({
method: 'POST',
url: 'https://airbnb.com/',
data: { name: 'John' },
})
.done(() => console.log('Congratulations!'))
.fail(() => console.log('You have failed this city.'));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# block-spacing : { 와 } 가 같은줄 : braces 내부 전후로 공백
// bad
function foo() {return true;}
if (foo) { bar = 0;}
// good
function foo() { return true; }
if (foo) { bar = 0; }
1
2
3
4
5
6
7
2
3
4
5
6
7
# comma-spacing : , 전에 공백 X / , 후 공백 O
// bad
var foo = 1,bar = 2;
var arr = [1 , 2];
// good
var foo = 1, bar = 2;
var arr = [1, 2];
1
2
3
4
5
6
7
2
3
4
5
6
7
# computed-property-spacing : 공백의 일관성
- 계산된 속성 : 공백을 넣거나 빼거나 둘중 하나로 통일해야 한다.
// bad
obj[foo ]
obj[ 'foo']
var x = {[ b ]: a}
obj[foo[ bar ]]
// good
obj[foo]
obj['foo']
var x = { [b]: a }
obj[foo[bar]]
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
# func-call-spacing : 함수 호출 공백 X
// bad
func ();
func
();
// good
func();
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
# key-spacing : 키 공백
- 개체 리터럴 속성에서 키와 값 사이의 간격
- 키는 붙이고 value 는 띄운다.
// bad
var obj = { foo : 42 };
var obj2 = { foo:42 };
// good
var obj = { foo: 42 };
1
2
3
4
5
6
2
3
4
5
6
# no-trailing-spaces : 줄 끝 후행 공백 X
- 후행 공백 : 공백, 탭 및 기타 유니 코드 공백 문자
- 소스 제어 시스템에서 diff 로 플래그가 지정되는 문제점.
// bad
var foo = 0;//•••••
var baz = 5;//••
// good
var foo = 0;
var baz = 5;
1
2
3
4
5
6
7
2
3
4
5
6
7
# no-multiple-empty-lines : 빈 멀티라인 X
- 오직 파일 끝 한줄의 개행 문자만 허용됨.
- 파일 시작에 개행 문자 X
// bad - 빈 멀티라인
var x = 1;
var y = 2;
// bad - 파일끝에 두 줄 이상의 개행문자
var x = 1;
var y = 2;
// bad - 파일 시작에 하나 이상의 개행문자
var x = 1;
var y = 2;
// good
var x = 1;
var y = 2;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# Commas
# Reference & Origin
https://github.com/airbnb/javascript 있는 포스트는 eslint 를 읽고 업데이트 한 게시글 입니다.
# Arrays
# 배열 생성: 리터럴 문법 []
// bad
const items = new Array();
// good
const items = [];
1
2
3
4
5
2
3
4
5
# 배열의 items 생성 : push
const someStack = [];
// bad
someStack[someStack.length] = 'abracadabra';
// good
someStack.push('abracadabra');
1
2
3
4
5
6
7
2
3
4
5
6
7
# 배열 복사 : ... spread operator
// bad
const len = items.length;
const itemsCopy = [];
let i;
for (i = 0; i < len; i += 1) {
itemsCopy[i] = items[i];
}
// good
const itemsCopy = [...items];
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
# iterable 객체 -> 배열 : spreads ... > Array.from.
const foo = document.querySelectorAll('.foo');
// good
const nodes = Array.from(foo);
// best
const nodes = [...foo];
1
2
3
4
5
6
7
2
3
4
5
6
7
iterable 객체 document.querySelectorAll('.foo')
의 __proto__
는 NodeList
# array-like 객체 -> 배열 : Array.from
let arrLike = { 0: 'foo', 1: 'bar', 2: 'baz', length: 3 };
/*
Object
0: "foo"
1: "bar"
2: "baz"
length: 3
__proto__: Object
*/
// bad
const arr = Array.prototype.slice.call(arrLike);
// good
const arr = Array.from(arrLike); // ["foo", "bar", "baz"]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
let arrLike = { '0': 'foo', 1: 'bar', 2: 'baz', length: 3 };
Array.from(arrLike); // ["foo", "bar", "baz"]
arrLike = { '212': 'foo', 1: 'bar', 2: 'baz', length: 3 };
Array.from(arrLike); // [undefined, "bar", "baz"]
1
2
3
4
5
2
3
4
5
length
프로퍼티가 존재.- index 번호가 0번부터 시작해서 1씩증가.
# iterable map : Array.from > spread ...
Array.from 은 중간에 배열을 생성하는 것을 방지한다.
const bar = (v) => v + 1;
const foo = [1, 2, 3];
// bad
const baz = [...foo].map(bar); // [2, 3, 4]
// good
const baz = Array.from(foo, bar) // [2, 3, 4]
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
# 배열 메서드의 callback 에서 return 문을 사용해야 한다
// good
[1, 2, 3].map((x) => {
const y = x + 1;
return x * y;
});
// good
[1, 2, 3].map((x) => x + 1);
// bad - callback 에서 return 문을 쓰지 않으면, 첫 이터레이션 이후 `acc` 의 값은 undefined 가 된다.
[[0, 1], [2, 3], [4, 5]].reduce((acc, item, index) => {
const flatten = acc.concat(item);
});
// good
[[0, 1], [2, 3], [4, 5]].reduce((acc, item, index) => {
const flatten = acc.concat(item);
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;
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# 여러 줄이있는 배열의 줄바꿈: 괄호를 열때, 닫기전 사용
// 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,
];
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# Destructuring
# 객체 다중 프로퍼티 사용 : destructuring
- 임시 references 생성을 줄여준다.
// 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}`;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 배열 destructuring
const arr = [1, 2, 3, 4];
// bad
const first = arr[0];
const second = arr[1];
// good
const [first, second] = arr;
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
# 리턴값이 여러개 일 때 : 객체 비구조화 > 배열 비구조화
- 객체 비구조화는, 추후에 코드에 새 속성을 추가 할 수 있다.
- 리턴 값을 순서에 상관없이 가져올 수 있다.
- 호출 할 때, 원하는 data 를 선택할 수 있다.
// 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 };
}
// 호출 할 때, 원하는 data 를 선택할 수 있다.
const { left, top } = processInput(input);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# Strings
# 문자열: single quotes ''
// bad
const name = "Capt. Janeway";
// bad - template literals: 줄바꿈, interpolation 일때 사용
const name = `Capt. Janeway`;
// good
const name = 'Capt. Janeway';
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
# 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.';
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
# 계산된 문자열을 만들 때 : 탬플릿 스트링 > +
- Template 스트링
- 가독성
- 줄 바꿈 -> 엔터
- interpolation
{something}
( 블록이 아님)
// 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}?`;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 문자열 eval() 사용 말것 : 많은 취약점
# 문자열 escape characters : Backslashes -> template string
- Backslashes 은 가독성 저하
- template string 으로 backslash 를 줄일 수 있다.
// bad
const foo = '\'this\' \i\s \"quoted\"';
// good
const foo = '\'this\' is "quoted"';
const foo = `my name is '${name}'`;
1
2
3
4
5
6
2
3
4
5
6
# Functions
# 이름있는 함수 표현식 > 함수 선언식
- 함수 선언식
- 함수 선언이 호이스트된다. (TODO airbnb 스타일 가이드에서의 호이스팅의 의미가 다른가?)
- 파일에서 함수가 정의되기 전에 함수를 참조할 수 있다
- 가독성과 유지 보수성을 해친다.
- 함수의 정의가 파일의 나머지 부분을 이해하는 데 방해가 될만큼 충분히 크거나 복잡하다면 -> 모듈로 추출
- 이름있는 함수 표현식
- This eliminates any assumptions made about the Error’s call stack.
- 에러 콜스택에서의 가정(유추해야 하는일)을 없앤다? 에러를 더 잘찾아낼 수 있다라는 의미로 해석.
// bad
function foo() {
// ...
}
// bad
const foo = function () {
// ...
};
// good
// lexical name distinguished from the variable-referenced invocation(s)
const short = function longUniqueMoreDescriptiveLexicalFoo() {
// ...
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# IIFE: parentheses 으로 감싸기
- 모듈 시스템에서의 IIFE 는 거의 사용되지 않는다.
// immediately-invoked function expression (IIFE)
(function () {
console.log('Welcome to the Internet. Please follow me.');
}());
1
2
3
4
2
3
4
# 비함수블럭에서 함수 선언 X -> 변수에 함수 할당.
- non-function block :
if, while
, etc - 브라우저마다 비함수블럭에서의 함수블럭 해석을 다르게 한다.
# ECMA-262, block : statements(명령문), 함수선언 : not statements
// bad
if (currentUser) {
function test() {
console.log('Nope.');
}
}
// good
let test;
if (currentUser) {
test = () => {
console.log('Yup.');
};
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
# 매개변수에 arguments 사용 X
- 함수의 arguments 프로퍼티보다 매개변수의 arguments 가 우선시 된다.
// bad
function foo(name, options, arguments) {
// ...
}
// good
function foo(name, options, args) {
// ...
}
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
# rest syntax ... > arguments X
- 함수에서 arguments 은 절대 사용하지 말것.
- 나머지 인수는 유사배열이 아닌 실제 배열이다.
// bad
function concatenateAll() {
const args = Array.prototype.slice.call(arguments);
return args.join('');
}
// good
function concatenateAll(...args) {
return args.join('');
}
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
# default parameter syntax > 함수 인자 변경
// really bad
function handleThings(opts) {
// 절대 함수의 인자를 변경해선 안된다.
// opts 가 거짓이면 원하는 객체로 설정되지만 미묘한 버그가 발생할 수 있습니다.
opts = opts || {};
// ...
}
// still bad
function handleThings(opts) {
if (opts === void 0) {
opts = {};
}
// ...
}
// good
function handleThings(opts = {}) {
// ...
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 사이드 이펙트를 유발하는 default parameter 사용 말것.
var b = 1;
// bad
function count(a = b++) {
console.log(a);
}
count(); // 1
count(); // 2
count(3); // 3
count(); // 3
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
# default parameters 는 마지막에 적는다.
// bad
function handleThings(opts = {}, name) {
// ...
}
// good
function handleThings(name, opts = {}) {
// ...
}
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
# 함수 객체 생성 : 함수 생성자 X
- 생성자로 함수 생성
- 문자열을 'eval()' 와 유사하게 평가한다. (TODO 'return a + b' 이 eval 와 유사하게 평가될 수 있다는 의미인가?)
- 취약점
// bad
var add = new Function('a', 'b', 'return a + b');
// still bad
var subtract = Function('a', 'b', 'return a - b');
1
2
3
4
5
2
3
4
5
# 함수 시그니처의 space 유지
- 함수시그니처
- functions 그리고 methods 의 입력과 출력을 정의
- 일관성
- 이름 추가/제거 -> 공백 추가/제거 의 동작을 안해도됨
// bad
const f = function(){};
const g = function (){};
const h = function() {};
const a = function a() {};
// good
const x = function () {};
const y = function a() {};
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
# 매개 변수로 전달 된 객체 조작 금지
- 함수의 (original)caller 에게 변수 부작용의 가능성.
// bad
function f1(obj) {
obj.key = 1;
}
// good
function f2(obj) {
const key = Object.prototype.hasOwnProperty.call(obj, 'key') ? obj.key : 1;
}
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
# 매개변수 재할당 금지
- 매개변수 재할당
- arguments 객체 접근 -> 예기치 않은 행동 발생
- 최적화 이슈, especially in 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) {
// ...
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 가변 함수 : spread operator ... > apply
- variadic functions (가변 함수)
- 가변 개수의 인수 허용
- apply 메서드로 context(this) 를 제공해 줄 필요 없어 깨끗하다.
- 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]);
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
# 함수의 여러줄의 시그니쳐/호출 : 들여쓰기, 한줄에 하나씩
// bad
function foo(bar,
baz,
quux) {
// ...
}
// good
function foo(
bar,
baz,
quux,
) {
// ...
}
// bad
console.log(foo,
bar,
baz);
// good
console.log(
foo,
bar,
baz,
);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# Arrow Functions
# 익명 함수: 화살표함수 사용
- 익명 함수를 사용해야 할 때 중 하나는 인라인 콜백(inline callback)을 사용할 때 이다.
- 화살표 함수의 함수의 실행 문맥(this)은 함수를 호출한 곳의 컨텍스트(this) 를 이어받을 수 있다.
- 만약 function 키워드를 사용한 익명함수를 인라인 콜백에서 썼을 경우의 this 는 실행컨텍스트를 예측하기가 어렵다.
- 화살표 함수는 간결하다.
- 만약 인라인 콜백이 복잡하여 졌을 때, 콜백의 로직을 함수 외부에 선언하는 작업이 필요해 질 것이다.
- 이 때 외부에서, reference 에 함수 표현식을 할당하면, 인라인 콜백 대신 reference 로 대신할 수 있다.
// 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;
});
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
# 명령문이 1개인 화살표 함수 : 중괄호 생략 -> 암시적 반환
- 중괄호를 사용해야 하는경우 반환을 하기 위해선 return 문을 써야 한다.
- Syntactic sugar : 가독성
// 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 + 1}.`);
// good
[1, 2, 3].map((number) => {
const nextNumber = number + 1;
return `A string containing the ${nextNumber}.`;
});
// good : obj 를 중괄호 없이 리턴하고 싶을땐, obj 를 () 으로 감싼다.
[1, 2, 3].map((number, index) => ({
[index]: number,
}));
// 일반함수는 암시적 리턴이 없다. side effect 가 있을 수 있다?
function foo(callback) {
const val = callback();
if (val === true) {
// Do something if callback returns true
}
}
let bool = false;
// bad , 'bool = true' 명령문의 결과값을 반환할 의도가 없기 때문에 중괄호 생략으로 한줄로 작성하는 것은 부적절 하다.
foo(() => bool = true);
// good
foo(() => {
bool = true;
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# 여러줄로 이뤄진 1개의 명령문의 암시적 반환: () 으로 감싼다.
- 함수의 시작과 끝을 명확히 할 수 있다.
// bad
['get', 'post', 'put'].map((httpMethod) => Object.prototype.hasOwnProperty.call(
httpMagicObjectWithAVeryLongName,
httpMethod,
)
);
// good
['get', 'post', 'put'].map((httpMethod) => (
Object.prototype.hasOwnProperty.call(
httpMagicObjectWithAVeryLongName,
httpMethod,
)
));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
# 화살표 함수의 인자: 항상 () 로 감싸기
- 명확성, 일관성
- 새로운 인자를 첨삭할 때 () 의 변동을 줄여준다.
x
->(x, y)
, 혹은 그 반대를 하면 () 의 변동이 있다.
// bad
[1, 2, 3].map(x => x * x);
// good
[1, 2, 3].map((x) => x * x);
// bad
[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!`
));
// 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;
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 화살표함수의 (=>) 와 비교 연산자 (<=, >=) 의 혼동 : 명령문을 () 로 감싼다.
// 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;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
# 암시적 반환할 때, body 의 위치
// bad
(foo) =>
bar;
(foo) =>
(bar);
// good
(foo) => bar;
(foo) => (bar);
(foo) => (
bar
)
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
# Classes & Constructors
# 클래스 사용 > 프로토타입 직접조작
- 클래스 문법은 더 간결하고, 쉽다.
// 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;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 상속: extends 사용
- extends
- instanceof를 파괴하지 않고(?TODO) 프로포타입 상속을 위한 내장 방법
// 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];
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 클래스 메서드 체이닝: 메서드가 this 를 return
// 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);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# 클래스의 커스텀 toString() 메서드
- 사용해도 되지만 성공적으로 작동하거나 사이드 이펙트가 없는지 확인해야 한다.
class Jedi {
constructor(options = {}) {
this.name = options.name || 'no name';
}
getName() {
return this.name;
}
toString() {
return `Jedi - ${this.getName()}`;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
# default 생성자
- 빈 constructor 는 생략해도 된다.
- 빈 생성자를 가지는 클래스를 상속하는 클래스에서, 또한 빈 생성자를 가지고 있을 땐 constructor 를 써주지 않아도 된다.
// bad
class Jedi {
constructor() {}
getName() {
return this.name;
}
}
// bad
class Rey extends Jedi {
constructor(...args) {
super(...args);
}
}
class Jedi {
getName() {
return this.name;
}
}
class Rey extends Jedi { }
// good
class Rey extends Jedi {
constructor(...args) {
super(...args);
this.name = 'Rey';
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# 중복된 클래스 멤버를 가지는 클래스 X
- 중복된 클래스 멤버 선언은. 클래스 멤버를 사용할 때 마지막 것을 선택한다.
- 중복된 클래스 멤버를 가질 수 있는 것은 버그이다.
// bad
class Foo {
bar() { return 1; }
bar() { return 2; }
}
// good
class Foo {
bar() { return 1; }
}
// good
class Foo {
bar() { return 2; }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 클래스 메서드에서 this 를 사용하지 않을 경우 : static 메서드 사용
// bad
class Foo {
bar() {
console.log('bar');
}
}
// good - this 를 사용할 경우 -> 일반 메서드
class Foo {
bar() {
console.log(this.bar);
}
}
// good - constructor 를 생략할 수 있다.
class Foo {
constructor() {
// ...
}
}
// good - this 를 사용하지 않은 경우 -> static methods
class Foo {
static bar() {
console.log('bar');
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# Modules
# (import/export) > non-standard 모듈 시스템.
- (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;
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
# * wildcard import X
- default Export 인지 named export 인지 헷갈릴 수 있다.
// bad
import * as AirbnbStyleGuide from './AirbnbStyleGuide';
// good
import AirbnbStyleGuide from './AirbnbStyleGuide';
1
2
3
4
5
2
3
4
5
# from 으로 Import 후 직접적인 export X
- 간결하지만, export / import 의 일관성을 위해 분리한다.
// bad
// filename es6.js
export { es6 as default } from './AirbnbStyleGuide';
// good
// filename es6.js
import { es6 } from './AirbnbStyleGuide';
export default es6;
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
# 하나의 path : 하나의 import
- 하나의 path 에서 여러개의 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';
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
# mutable: export X
- 특수한 경우를 제외하고 상수 reference 만 export 한다.
// bad
let foo = 3;
export { foo };
// good
const foo = 3;
export { foo };
1
2
3
4
5
6
7
2
3
4
5
6
7
# 모듈에 export 가 하나만 있을 때: export default > named export
- 파일에 하나의 export 를 권장
- 가독성, 유지보수에 낫다.
// bad
export function foo() {}
// good
export default function foo() {}
1
2
3
4
5
2
3
4
5
# 모든 import 는 non-import 명령문 위에 두기
- import 는 호이스팅 된다.
- 호이스팅이 되는 것들은 아래에 두었을때 문제가 발생할 가능성이 있다는 말?
// bad
import foo from 'foo';
foo.init();
import bar from 'bar';
// good
import foo from 'foo';
import bar from 'bar';
foo.init();
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
# 여러줄의 imports : 들여쓰기(indent)
// bad
import {longNameA, longNameB, longNameC, longNameD, longNameE} from 'path';
// good
import {
longNameA,
longNameB,
longNameC,
longNameD,
longNameE,
} from 'path';
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
# import 문: Webpack loader 구문 X
- import 에서 webpack 구문을 사용하면 코드를 모듈 번들러에 연결한다 (TODO 웹팩에 대한 지식 부족..)
- webpack.config.js 에서 로더 구문을 사용하는 것이 좋다.
// 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';
1
2
3
4
5
6
7
2
3
4
5
6
7
# import/extensions : 자바스크립트 파일이름 extensions X
- extensions 포함의 문제점.
- 리팩토링이 금지된다.
- (? TODO .js -> .jsx .. 등 js 관련 확장자를 다 수정해 줘야하는 문제점?)
- 리팩토링이 금지된다.
// bad
import foo from './foo.js';
import bar from './bar.jsx';
import baz from './baz/index.jsx';
// good
import foo from './foo';
import bar from './bar';
import baz from './baz';
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
# Iterators and Generators
# 이터레이터 사용: 고차 함수 > 루프 for-in / for-of
- 고차 함수
- 불변 규칙을 강제하기 위함이다.
- 값을 리턴하는 순수 함수를 다루는 것이 더 쉽기 때문에 사이드 이펙트도 작을 수 있음.
- 배열 순회
- map()
- every()
- filter()
- find()
- findIndex()
- reduce()
- some() / ...
- 객체 -> 배열
- Object.keys()
- Object.values()
- Object.entries()
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;
// bad
const increasedByOne = [];
for (let i = 0; i < numbers.length; i++) {
increasedByOne.push(numbers[i] + 1);
}
// good
const increasedByOne = [];
numbers.forEach((num) => {
increasedByOne.push(num + 1);
});
// best (keeping it functional)
const increasedByOne = numbers.map((num) => num + 1);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# 현재 제너레이터는 ES5 트랜스파일링이 잘 되지 않는다.
# 제너레이터의 * 연산자의 적절한 공백 : function* foo()
- function, * -> 같은 개념적 키워드
- 는 함수에 대한 수정자가 아님
- function * 은 함수와 다른 고유 한 구성임 ??
// bad
function * foo() {
// ...
}
// bad
const bar = function * () {
// ...
};
// bad
const baz = function *() {
// ...
};
// bad
const quux = function*() {
// ...
};
// bad
function*foo() {
// ...
}
// bad
function *foo() {
// ...
}
// very bad
function
*
foo() {
// ...
}
// very bad
const wat = function
*
() {
// ...
};
// good
function* foo() {
// ...
}
// good
const foo = function* () {
// ...
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# Properties
# 프로퍼티 접근: dot 표기법 사용
const luke = {
jedi: true,
age: 28,
};
// bad
const isJedi = luke['jedi'];
// good
const isJedi = luke.jedi;
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
# 변수로 프로퍼티 접근 : [] 사용
const luke = {
jedi: true,
age: 28,
};
function getProp(prop) {
return luke[prop];
}
const isJedi = getProp('jedi');
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
# 지수 연산 : ** > Math.pow
// bad
const binary = Math.pow(2, 10);
// good
const binary = 2 ** 10;
1
2
3
4
5
2
3
4
5
# Variables
# 변수 선언 : 항상 const / let
- const / let
- 전역 변수가 생성되지 않는다.
- 전역 변수 공간을 어지럽히면 안된다.
// bad
superPower = new SuperPower();
// good
const superPower = new SuperPower();
1
2
3
4
5
2
3
4
5
# 하나의 변수 선언/할당 : 하나의 const/let
- 새로 변수를 선언하고 추가할 때 더 쉽다.
- 하나의 const/let 에 여러 변수를 나열하면, ; 와 , 을 고려해야 한다.
- 디버그를 할 때, 한줄로 쓰면 한번에 뛰어넘는다.
- 단계별로 관찰 할 수 없다.
// bad
const items = getItems(),
goSportsTeam = true,
dragonball = 'z';
// bad
// (compare to above, and try to spot the mistake)
const items = getItems(),
goSportsTeam = true;
dragonball = 'z';
// good
const items = getItems();
const goSportsTeam = true;
const dragonball = 'z';
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# const/ let 의 그룹화
- 그룹화는, 나중에 이전에 할당 된 변수 중 하나에 따라 변수를 할당해야 할 때 유용합니다. (?TODO)
// bad
let i, len, dragonball,
items = getItems(),
goSportsTeam = true;
// 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;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 합리적인 위치에 변수할당문 사용
- 필요한 위치 근처에 둔다.
- let/const 은 block 스코프 이기 때문임.
// bad - unnecessary function call
function checkName(hasName) {
const name = getName();
if (hasName === 'test') {
return false;
}
if (name === 'test') {
this.setName('');
return false;
}
return name;
}
// good
function checkName(hasName) {
if (hasName === 'test') {
return false;
}
const name = getName();
if (name === 'test') {
this.setName('');
return false;
}
return name;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# 변수 할당문의 체이닝 X
- 변수 할당문의 체이닝은 암묵적으로 글로벌 변수를 만들어 낸다.
// bad
(function example() {
let a = b = c = 1;
// let a = ( b = ( c = 1 ) );
// let 은 a 에게만 적용된다.
// b 와 c 는 전역 변수가 된다.
}());
console.log(a); // throws ReferenceError
console.log(b); // 1
console.log(c); // 1
// good
(function example() {
let a = 1;
let b = a;
let c = a;
}());
console.log(a); // throws ReferenceError
console.log(b); // throws ReferenceError
console.log(c); // throws ReferenceError
// `const` 도 마찬가지.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 단항 증감 (++, --) X
- 단항 증감은 세미콜론 자동 삽입의 대상이 된다.
- silent errors 의 원인이 될 수 있다.
num++
/num ++
보다num += 1
이 더 바람직하다.- 단항 증감을 사용하지 않으면,
- pre-incrementing/pre-decrementing 실수를 방지할 수 있다.
// bad
const array = [1, 2, 3];
let num = 1;
num++;
--num;
let sum = 0;
let truthyCount = 0;
for (let i = 0; i < array.length; i++) {
let value = array[i];
sum += value;
if (value) {
truthyCount++;
}
}
// good
const array = [1, 2, 3];
let num = 1;
num += 1;
num -= 1;
const sum = array.reduce((a, b) => a + b, 0);
const truthyCount = array.filter(Boolean).length;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# = 할당문 전후로 줄바꿈 X, 줄바꿈이 필요하다면 () 사용
- 할당문 전후로 줄바꿈이 발생하면 난독화를 일으킬 수 있다.
// bad
const foo =
superLongLongLongLongLongLongLongLongFunctionName();
// bad
const foo
= 'superLongLongLongLongLongLongLongLongString';
// good
const foo = (
superLongLongLongLongLongLongLongLongFunctionName()
);
// good
const foo = 'superLongLongLongLongLongLongLongLongString';
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 사용하지 않은 변수는 비허용된다.
- 선언되었지만 사용되지 않은 변수는 리팩토링이 완전하게 되지 않았다는 의미이다.
- 코드 공간을 불필요하게 차지함.
- 읽는 사람에게 혼동을 줌.
// bad
var some_unused_var = 42;
// y 는 쓰여지기만 했지 사용되지 않았다.
var y = 10;
y = 5;
// 사용되지 않은 변수
var z = 0;
z = z + 1;
// 사용되지 않은 함수
function getX(x, y) {
return x;
}
// good
function getXPlusY(x, y) {
return x + y;
}
var x = 1;
var y = a + 2;
alert(getXPlusY(x, y));
// 나머지 프로퍼티 연산자: 객체에서 특정 키를 의도적으로 빠뜨리고 추출하는 방법이다.
// 'type' 은 사용하지 않아도 괜찮다.
var { type, ...coords } = data;
// 'coords' 는 data 객체에서 'type' 프로퍼티가 빠진 객체다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# Hoisting
# var, const/let 선언의 호이스팅:
- var
- 선언문은 가장 가까운 함수 스코프의 최상단으로 호이스팅 된다.
- 할당문은 호이스팅 되지 않는다.
- const/let
- 선언에는 Temporal Dead Zones (TDZ) 가 존재한다.
typeof
는 안전하지 않다. (예시 참고)
// 아래 함수는 참조 에러를 일으킨다 (notDefined 라는 전역 변수가 존재하지 않을 때)
function example() {
console.log(notDefined); // => throws a ReferenceError
}
// declaredButNotAssigned 는 호이스팅이 일어나기 때문에 사용가능하다.
// Note: 하지만 true 할당은 호이스팅 되지 않음.
function example() {
console.log(declaredButNotAssigned); // => undefined
var declaredButNotAssigned = true;
}
// 인터프리터는 변수의 선언을 스코프의 최상단으로 호이스팅 한다.
function example() {
let declaredButNotAssigned;
console.log(declaredButNotAssigned); // => undefined
declaredButNotAssigned = true;
}
// const / let 의 예시
function example() {
console.log(declaredButNotAssigned); // => throws a ReferenceError
console.log(typeof declaredButNotAssigned); // => throws a ReferenceError
const declaredButNotAssigned = true;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 14.2 익명함수 표현식의 호이스팅 : 변수 이름 O, 함수할당 X
function example() {
console.log(anonymous); // => undefined
anonymous(); // => TypeError anonymous is not a function
var anonymous = function () {
console.log('anonymous function expression');
};
}
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
# 이름있는 함수 표현식의 호이스팅: 변수이름 O, 함수이름과 body X
function example() {
console.log(named); // => undefined
named(); // => TypeError named is not a function
superPower(); // => ReferenceError superPower is not defined
var named = function superPower() {
console.log('Flying');
};
}
// 할당된 변수이름과 함수 표현식의 이름기 같은 경우.
function example() {
console.log(named); // => undefined
named(); // => TypeError named is not a function
var named = function named() {
console.log('named');
};
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 함수 선언문의 호이스팅: 함수 이름, body O
function example() {
superPower(); // => Flying
function superPower() {
console.log('Flying');
}
}
1
2
3
4
5
6
7
2
3
4
5
6
7
# Comparison Operators & Equality
# (===) (!==) > (==)
# Conditional statements
if
와 같은 조건문
ToBoolean
의 추상 메서드로 강제변환을 사용하여 식을 평가한다.- 식을 평가하는 규칙은 다음과 같다.
- Objects -> true
- Undefined -> false
- Null -> false
- Booleans -> true / false
- Numbers
- false : +0, -0, NaN
- otherwise true
- Strings
- false : empty string ''
- otherwise true
if ([0] && []) {
// true
// 배열은 객체이다. 빈객체도 객체임
// 객체는 항상 true
}
1
2
3
4
5
2
3
4
5
# reference: boolean -> 축약 , 문자열/숫자 -> 명시적 비교
// bad
if (isValid === true) {
// ...
}
// good
if (isValid) {
// ...
}
// bad
if (name) {
// ...
}
// good
if (name !== '') {
// ...
}
// bad
if (collection.length) {
// ...
}
// good
if (collection.length > 0) {
// ...
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# Truth Equality and JavaScript by Angus Croll.🔗
# 어휘 선언이 있는 case/default 문 : { } braces 사용
- Lexical declarations [어휘 선언]
- let, const, function, and class
- 스위치 블록 스코프가 존재한다.
- case/default 에 { } 가 없다면 여러 case 절에서 동일한 것을 정의하려고 할 때 문제가 발생함.
// bad
switch (foo) {
case 1:
let x = 1;
break;
case 2:
const y = 2;
break;
case 3:
function f() {
// ...
}
break;
default:
class C {}
}
switch (1) {
case 1:
let v ;
break;
case 2:
let v; // Uncaught SyntaxError: Identifier 'v' has already been declared
break;
case 3:
function f() {
// ...
}
break;
default:
class C {}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// 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 {}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 삼항의 중첩 X : 기본적으로 단일행 표현식
// bad
const foo = maybe1 > maybe2
? "bar"
: value1 > value2 ? "baz" : null;
// split into 2 separated ternary(삼항) expressions
const maybeNull = value1 > value2 ? 'baz' : null;
// better
const foo = maybe1 > maybe2
? 'bar'
: maybeNull;
// best
const foo = maybe1 > maybe2 ? 'bar' : maybeNull;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 불필요한 삼항 표현식 X
// bad
const foo = a ? a : b;
const bar = c ? true : false;
const baz = c ? false : true;
// good
const foo = a || b;
const bar = !!c;
const baz = !c;
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
# 혼합 연산자 : () 로 감싸기
- 혼합연산자는 우선순위가 모호할 수 있다.
- +, -, ** 는 예외
- /, * 는 모호 하다.
// bad
const foo = a && b < 0 || c > 0 || d + 1 === 0;
// bad
const bar = a ** b - 5 % d;
// bad
// (a || b) && c 와 혼동될 수 있다.
if (a || b && c) {
return d;
}
// bad
const bar = a + b / c * d;
// good
const foo = (a && b < 0) || c > 0 || (d + 1 === 0);
// good
const bar = a ** b - (5 % d);
// good
if (a || (b && c)) {
return d;
}
// good
const bar = a + (b / c) * d;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# Blocks
# 여러줄의 블록 : {} 사용
// bad
if (test)
return false;
// good
if (test) return false;
// good
if (test) {
return false;
}
// bad
function foo() { return false; }
// good
function bar() {
return false;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# if/else 여러줄 블록 : if 의 닫기 } 와 같은 줄에 else 두기
// bad
if (test) {
thing1();
thing2();
}
else {
thing3();
}
// good
if (test) {
thing1();
thing2();
} else {
thing3();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# return 을 포함하는 if 블록 + else/else if
- return 을 포함하는 if 블록
- 후속 else 불필요
- 후속 else if 의 리턴 -> if 리턴으로 분리
// bad
function foo() {
if (x) {
return x;
} else {
return y;
}
}
// good
function foo() {
if (x) {
return x;
}
return y;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// bad
function cats() {
if (x) {
return x;
} else if (y) {
return y;
}
}
// good
function cats() {
if (x) {
return x;
}
if (y) {
return y;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// bad
function dogs() {
if (x) {
return x;
} else {
if (y) {
return y;
}
}
}
// good
function dogs(x) {
if (x) {
if (z) {
return y;
}
} else {
return z;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# Control Statements
- if, while etc
# 제어문의 아주 긴 조건 : 줄바꿈, 논리연산은 새 줄에 시작
- 연산을 새 줄에 시작하는 것
- 메서드 체이닝 패턴
- 가독성
// 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();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# 제어문 > 선택 연산자
// bad
!isRunning && startRunning();
// good
if (!isRunning) {
startRunning();
}
1
2
3
4
5
6
7
2
3
4
5
6
7
# Comments
# 멀티라인 주석 /** ... */ > //
// bad
// make() returns a new element
// based on the passed in tag name
//
// @param {String} tag
// @return {Element} element
function make(tag) {
// ...
return element;
}
// good
/**
* make() returns a new element
* based on the passed-in tag name
*/
function make(tag) {
// ...
return element;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# // : 한줄 주석
- 주석의 대상 위에 한줄로 표기
- 주석 위에는 한 줄 비우기
- 주석이 블록이 첫줄이 아닌경우 제외
// 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;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# 주석의 시작은 항상 공백
- 가독성
// 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;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# FIXME / TODO
- 실행 가능한 주석
- FIXME: 재검토해야 할 문제를 지적
- TODO: 구현해야하는 문제
class Calculator extends Abacus {
constructor() {
super();
// FIXME: 여기서 전역을 사용해서는 안됩니다.
total = 0;
}
}
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
class Calculator extends Abacus {
constructor() {
super();
// TODO: total은 옵션 매개 변수로 구성 할 수 있어야합니다.
this.total = 0;
}
}
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
# Whitespace
# 들여쓰기 indent : 2 space character
// bad
function foo() {
∙∙∙∙let name;
}
// bad
function bar() {
∙let name;
}
// good
function baz() {
∙∙let name;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
# space-before-blocks: 여는 { 의 앞에 공백 하나
// 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',
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# keyword-spacing : 제어문과 함수의 호출/선언
- 제어문 : 여는 ( 앞에 공백 하나
- 함수 호출/선언: 여는 ( 앞에 공백 X
// bad
if(isJedi) {
fight ();
}
// good
if (isJedi) {
fight();
}
// bad
function fight () {
console.log ('Swooosh!');
}
// good
function fight() {
console.log('Swooosh!');
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# space-infix-ops : 연산자 사이 공백
// bad
const x=y+5;
// good
const x = y + 5;
1
2
3
4
5
2
3
4
5
# eol-last : 파일 마지막 - 하나의 newline(개행) 문자로 끝내기
- 비어 있지 않은 파일의 후행 줄 바꿈은 일반적인 UNIX 관용구입니다.
- 후행 줄 바꿈의 이점은 쉘 프롬프트를 방해하지 않고 파일 및 출력 파일을 터미널에 연결하거나 추가하는 기능을 포함합니다.
// bad
import { es6 } from './AirbnbStyleGuide';
// ...
export default es6;
1
2
3
4
2
3
4
// bad
import { es6 } from './AirbnbStyleGuide';
// ...
export default es6;
↵
1
2
3
4
5
2
3
4
5
import { es6 } from './AirbnbStyleGuide';
// ...
export default es6;↵
1
2
3
2
3
# newline-per-chained-call 체이닝 마다 개행
- 긴 메서드 체이닝 (2개이상)
- 들여쓰기
- . dot 으로 시작하기
- . 으로 시작하는 것 새로운 문이 아닌 메서드 호출이라는 것을 강조할 수 있다.
// 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').classed('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);
// good
const leds = stage.selectAll('.led').data(data);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# no-whitespace-before-property 🔗 프로퍼티 앞에 공백 X
- javascript 는 객체와 속성 사이의 공백을 허용한다.
- 가독성을 저하하고 오류를 유발할 수 있다.
foo. bar .baz . quz
1
- 점 주위 (), [] 괄호 앞에 공백을 쓰지 않아야 된다.
- 체이닝의 개행에서만 허용 한다.
foo
.bar()
.baz()
.qux()
1
2
3
4
2
3
4
/*eslint no-whitespace-before-property: "error"*/
foo [bar] // foo.bar
foo. bar // foo[bar]
foo .bar // foo[ bar ]
foo. bar. baz // foo.bar.baz
foo. bar() // foo
.baz() // .bar().baz()
foo
.bar(). baz()
/*
foo
.bar()
.baz()
foo.
bar().
baz()
*/
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 블록 다음/ 다음 statement 앞 : 빈 줄
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;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# padded-blocks: 블록을 빈 줄로 채우지 말것
// 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);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# no-multiple-empty-lines: 멀티라인 빈줄 X
- 공백은 코드의 논리적 섹션을 분리하는데 유용하지만 과도한 공백은 화면을 더 많이 차지 한다.
- 코드를 읽을 때 필요한 스크롤을 줄이는 것을 목표로 한다.
// bad
class Person {
constructor(fullName, email, birthday) {
this.fullName = fullName;
this.email = email;
this.setAge(birthday);
}
setAge(birthday) {
const today = new Date();
const age = this.getAge(today, birthday);
this.age = age;
}
getAge(today, birthday) {
// ..
}
}
// good
class Person {
constructor(fullName, email, birthday) {
this.fullName = fullName;
this.email = email;
this.setAge(birthday);
}
setAge(birthday) {
const today = new Date();
const age = getAge(today, birthday);
this.age = age;
}
getAge(today, birthday) {
// ..
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# space-in-parens : ( ) 안에 공백 X
// bad
function bar( foo ) {
return foo;
}
// good
function bar(foo) {
return foo;
}
// bad
if ( foo ) {
console.log(foo);
}
// good
if (foo) {
console.log(foo);
}
// bad
foo( 'bar' );
var x = ( 1 + 2 ) * 3;
// good
foo('bar');
var x = (1 + 2) * 3;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# array-bracket-spacing : [ ] 안에 공백 X
// bad
const foo = [ 1, 2, 3 ];
console.log(foo[ 0 ]);
// good
const foo = [1, 2, 3];
console.log(foo[0]);
1
2
3
4
5
6
7
2
3
4
5
6
7
# object-curly-spacing : { } 안에 공백 O
// bad
const foo = {clark: 'kent'};
// good
const foo = { clark: 'kent' };
1
2
3
4
5
2
3
4
5
# 한줄에 100 자(공백 포함) 이상 X
// bad
const foo = jsonData && jsonData.foo && jsonData.foo.bar && jsonData.foo.bar.baz && jsonData.foo.bar.baz.quux && jsonData.foo.bar.baz.quux.xyzzy;
// bad
$.ajax({ method: 'POST', url: 'https://airbnb.com/', data: { name: 'John' } }).done(() => console.log('Congratulations!')).fail(() => console.log('You have failed this city.'));
// good
const foo = jsonData
&& jsonData.foo
&& jsonData.foo.bar
&& jsonData.foo.bar.baz
&& jsonData.foo.bar.baz.quux
&& jsonData.foo.bar.baz.quux.xyzzy;
// good
$.ajax({
method: 'POST',
url: 'https://airbnb.com/',
data: { name: 'John' },
})
.done(() => console.log('Congratulations!'))
.fail(() => console.log('You have failed this city.'));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# block-spacing : { 와 } 가 같은줄 : braces 내부 전후로 공백
// bad
function foo() {return true;}
if (foo) { bar = 0;}
// good
function foo() { return true; }
if (foo) { bar = 0; }
1
2
3
4
5
6
7
2
3
4
5
6
7
# comma-spacing : , 전에 공백 X / , 후 공백 O
// bad
var foo = 1,bar = 2;
var arr = [1 , 2];
// good
var foo = 1, bar = 2;
var arr = [1, 2];
1
2
3
4
5
6
7
2
3
4
5
6
7
# computed-property-spacing : 공백의 일관성
- 계산된 속성 : 공백을 넣거나 빼거나 둘중 하나로 통일해야 한다.
// bad
obj[foo ]
obj[ 'foo']
var x = {[ b ]: a}
obj[foo[ bar ]]
// good
obj[foo]
obj['foo']
var x = { [b]: a }
obj[foo[bar]]
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
# func-call-spacing : 함수 호출 공백 X
// bad
func ();
func
();
// good
func();
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
# key-spacing : 키 공백
- 개체 리터럴 속성에서 키와 값 사이의 간격
- 키는 붙이고 value 는 띄운다.
// bad
var obj = { foo : 42 };
var obj2 = { foo:42 };
// good
var obj = { foo: 42 };
1
2
3
4
5
6
2
3
4
5
6
# no-trailing-spaces : 줄 끝 후행 공백 X
- 후행 공백 : 공백, 탭 및 기타 유니 코드 공백 문자
- 소스 제어 시스템에서 diff 로 플래그가 지정되는 문제점.
// bad
var foo = 0;//•••••
var baz = 5;//••
// good
var foo = 0;
var baz = 5;
1
2
3
4
5
6
7
2
3
4
5
6
7
# no-multiple-empty-lines : 빈 멀티라인 X
- 오직 파일 끝 한줄의 개행 문자만 허용됨.
- 파일 시작에 개행 문자 X
// bad - 빈 멀티라인
var x = 1;
var y = 2;
// bad - 파일끝에 두 줄 이상의 개행문자
var x = 1;
var y = 2;
// bad - 파일 시작에 하나 이상의 개행문자
var x = 1;
var y = 2;
// good
var x = 1;
var y = 2;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# Commas
# comma-style: 컴마로 시작 X
// bad
const story = [
once
, upon
, aTime
];
// good
const story = [
once,
upon,
aTime,
];
// bad
const hero = {
firstName: 'Ada'
, lastName: 'Lovelace'
, birthYear: 1815
, superPower: 'computers'
};
// good
const hero = {
firstName: 'Ada',
lastName: 'Lovelace',
birthYear: 1815,
superPower: 'computers',
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# comma-dangle : 마지막행 쉼표 -> 깨끗한 diff
- babel 과같은 트랜스컴파일러도 추가 후행쉼표를 제거해 준다.
// bad - git diff without trailing comma
const hero = {
firstName: 'Florence',
- lastName: 'Nightingale'
+ lastName: 'Nightingale',
+ inventorOf: ['coxcomb chart', 'modern nursing']
};
// good - git diff with trailing comma
const hero = {
firstName: 'Florence',
lastName: 'Nightingale',
+ inventorOf: ['coxcomb chart', 'modern nursing'],
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
// bad
const hero = {
firstName: 'Dana',
lastName: 'Scully'
};
const heroes = [
'Batman',
'Superman'
];
// good
const hero = {
firstName: 'Dana',
lastName: 'Scully',
};
const heroes = [
'Batman',
'Superman',
];
// bad
function createHero(
firstName,
lastName,
inventorOf
) {
// does nothing
}
// good
function createHero(
firstName,
lastName,
inventorOf,
) {
// does nothing
}
// good (rest 뒤에는 comma 가 없어야 한다)
function createHero(
firstName,
lastName,
inventorOf,
...heroArgs
) {
// does nothing
}
// bad
createHero(
firstName,
lastName,
inventorOf
);
// good
createHero(
firstName,
lastName,
inventorOf,
);
// good (note that a comma must not appear after a "rest" element)
createHero(
firstName,
lastName,
inventorOf,
...heroArgs
);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# Semicolons
# semi 로 명시적으로 명령문 종료
- JavaScript 는 각 문 끝에 세미콜론이 필요하지 않음
- 자동 세미콜론 삽입 (ASI): 대부분 JS 엔진, 세미콜론 위치 판단 -> 자동 추가
- ASI 몇 가지 특이한 동작 -> 잘못 해석하면 코드가 깨진다.
- ASI 의 시기를 알고, ESLint 가 이러한 잠재적으로 예상치 못한 경우로부터 코드를 보호하도록하는 것이 가장 좋습니다.
\n
문자는 아래에 해당하지 않을 때 세미콜론을 붙인다.- 닫히지 않은 괄호 :
(
,[
,{
paren, array literal, or object literal .
,,
로 끝남- 다음 토큰을 증가/감소 하는
--
/++
{
가 없는 :for()
,while()
,do
,if()
,else
:- 다음 줄이
[
,(
,+
,*
,/
,-
,,
,.
, 으로 시작할 때 - some other binary operator that can only be found between two tokens in a single expression.
- single expression 에서, 두 토큰 사이의 이항 연산자(?)
- 닫히지 않은 괄호 :
// bad - raises exception
const luke = {}
const leia = {}
[luke, leia].forEach((jedi) => jedi.father = 'vader')
// bad - raises exception
const reaction = "No! That’s impossible!"
(async function meanwhileOnTheFalcon() {
// handle `leia`, `lando`, `chewie`, `r2`, `c3p0`
// ...
}())
// bad - ASI, return 이 단독줄에 있을 때 `undefined` 를 반환함.
function foo() {
return
'search your feelings, you know it to be foo'
}
// good
const luke = {};
const leia = {};
[luke, leia].forEach((jedi) => {
jedi.father = 'vader';
});
// 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';
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# Type Casting & Coercion
타입 캐스팅 & 강제 형변환
# 명령문의 시작부분에서 강제 형변환 하기
# no-new-wrappers : Strings
원시형 래퍼 타입 : String, Number, Boolean 사용
- 메서드와 같은 객체와 유사한 기능을 제공
var text = "Hello world".substring(2);
- 연관된 래퍼 유형의 객체가 생성 된 다음 소멸된다.
new wrapper instance
var stringObject = new String("Hello world"); var numberObject = new Number(33); var booleanObject = new Boolean(false);
1
2
3new wrapper String, Number, Boolean 를 사용하지 않는 것에 대한 사례
var stringObject = new String("Hello world"); console.log(typeof stringObject); // "object" var text = "Hello world"; console.log(typeof text); // "string" var booleanObject = new Boolean(false); if (booleanObject) { // all objects are truthy! console.log("This executes"); }
1
2
3
4
5
6
7
8
9
10
// => this.reviewScore = 9;
// 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);
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
# no-new-wrappers : Numbers
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);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 성능 Bitshift > parseInt
// good
/**
* parseInt 가 코드상에서 느리게 작동한다면
* 문자열 -> 숫자 강제 변환 시 Bitshifting 가 많이 빠르다.
*/
const val = inputValue >> 0;
1
2
3
4
5
6
2
3
4
5
6
# bitshift 주의점
- Numbers 는 64 bit 값
- bitshift 는 32 bit integer 를 리턴함.
- 32 bit Int, 2,147,483,647 보다 더 큰 값에서 예상치 못한 값으로 변환됌.
2147483647 >> 0; // => 2147483647
2147483648 >> 0; // => -2147483648
2147483649 >> 0; // => -2147483647
1
2
3
2
3
# no-new-wrappers : Booleans
const age = 0;
// bad
const hasAge = new Boolean(age);
// good
const hasAge = Boolean(age);
// best
const hasAge = !!age;
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
# Naming Conventions
# id-length : 이름 설명 > 한글자 이름
// bad
function q() {
// ...
}
// good
function query() {
// ...
}
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
# camelcase : naming objects, functions, instances.
// bad
const OBJEcttsssss = {};
const this_is_my_object = {};
function c() {}
// good
const thisIsMyObject = {};
function thisIsMyFunction() {}
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
# new-cap : 생성자 & 클래스 이름에만 PascalCase 사용
// 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',
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# no-underscore-dangle : 후행/선행 밑줄 X
- 자바 스크립트는 속성이나 방법 측면에서 프라이버시 개념이 없다
- 선행 밑줄은 "비공개"를 의미하는 일반적인 규칙이지만 실제로 이러한 속성은 완전히 공개되어 있다.
- 이 규칙은 개발자가 변경 사항이 'breaking' 으로 간주되지 않거나 테스트가 필요하지 않다고 잘못 생각하게 만들 수 있다.
- tl; dr : "비공개"상태를 원하는 경우 눈에 띄게 표시되지 않아야합니다.
// bad
this.__firstName__ = 'Panda';
this.firstName_ = 'Panda';
this._firstName = 'Panda';
// good
this.firstName = 'Panda';
// good, in environments where WeakMaps are available
// see https://kangax.github.io/compat-table/es6/#test-WeakMap
const firstNames = new WeakMap();
firstNames.set(this, 'Panda');
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
# Function#bind : 화살표 함수 > reference 에 this 저장
// 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);
};
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# default export as filename
// file 1 contents
class CheckBox {
// ...
}
// PascalCase filename
export default CheckBox;
// file 2 contents camelCase filename
export default function fortyTwo() { return 42; }
// file 3 contents camelCase filename
export default function insideDirectory() {}
// in some other file
// 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 './CheckBox'; // PascalCase export/import/filename
import fortyTwo from './fortyTwo'; // camelCase export/import/filename
import insideDirectory from './insideDirectory'; // camelCase export/import/directory name/implicit "index"
// ^ supports both insideDirectory.js and insideDirectory/index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# camelCase : export-default a 함수
function makeStyleGuide() {
// ...
}
export default makeStyleGuide;
1
2
3
4
5
2
3
4
5
# PascalCase : export a 생성자 / 클래스 / singleton / 함수 라이브러리 / bare object
const AirbnbStyleGuide = {
es6: {
},
};
export default AirbnbStyleGuide;
1
2
3
4
5
6
2
3
4
5
6
# 줄임말 / 이니셜 : 모두 대문자/소문자 일치
// bad
import SmsContainer from './containers/SmsContainer';
// bad
const HttpRequests = [
// ...
];
// good
import SMSContainer from './containers/SMSContainer';
// good
const HTTPRequests = [
// ...
];
// also good
const httpRequests = [
// ...
];
// best
import TextMessageContainer from './containers/TextMessageContainer';
// best
const requests = [
// ...
];
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# optionally uppercase : export / const / can trust it to never change
- UPPERCASE_VARIABLES 는 프로그래머에게 변수 (및 해당 속성)가 변경되지 않도록 신뢰할 수 있음을 알려줌
- exported 상수 외, 파일 내 상수에는 대문자를 사용하지 마십시오.
- exported objects, 대문자를 사용하고 모든 중첩 속성이 변경되지 않도록 유지합니다.
// bad
const PRIVATE_VARIABLE = '파일 내에서 불필요하게 대문자를 사용해서는 안됩니다.';
// bad
export const THING_TO_BE_CHANGED = '분명히 대문자가 아니어야합니다';
// bad
export let REASSIGNABLE_VARIABLE = '대문자 변수와 함께 let을 사용하지 마십시오.';
// ---
// 허용되지만 의미 값을 제공하지 않습니다.
export const apiKey = 'SOMEKEY';
// 대부분의 경우 더 좋음
export const API_KEY = 'SOMEKEY';
// ---
// bad - 불필요한 대문자 키 : semantic 값인 경우에만 대문자.
export const MAPPING = {
KEY: 'value'
};
// good
export const MAPPING = {
key: 'value'
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# Accessors
# 프로퍼티에 대한 접근자 함수는 불필요
# getters/setters -> unexpected side effects, test, maintain
접근자 함수 만들기
// bad
class Dragon {
get age() {
// ...
}
set age(value) {
// ...
}
}
// good
class Dragon {
getAge() {
// ...
}
setAge(value) {
// ...
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# isVal() or hasVal() : boolean property/method
// bad
if (!dragon.age()) {
return false;
}
// good
if (!dragon.hasAge()) {
return false;
}
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
# 일관성 있는 get() and set() functions
class Jedi {
constructor(options = {}) {
const lightsaber = options.lightsaber || 'blue';
this.set('lightsaber', lightsaber);
}
set(key, val) {
this[key] = val;
}
get(key) {
return this[key];
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
# Events
# 이벤트에 data payload 추가 : object literal(hash) > raw value
이벤트 페이로드에 더 많은 데이터를 추가 할 수 있습니다.
// 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
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# Standard Library
Standard Library 는 기능적으로 손상되었지만 레거시 이유로 남아 있는 utilities 를 가지고 있다.
# no-restricted-globals : Number.isNaN > global isNaN
- global isNaN
- non-numbers 를 numbers 로 강제 변환 한다.
- NaN을 강제하는 모든 것에 대해 true 를 반환
// bad
isNaN('1.2'); // false
isNaN('1.2.3'); // true
// good
Number.isNaN('1.2.3'); // false
Number.isNaN(Number('1.2.3')); // true
1
2
3
4
5
6
7
2
3
4
5
6
7
# no-restricted-globals : Number.isFinite > global isFinite
- global isFinite
- non-numbers 를 numbers 로 강제 변환 한다.
- 유한 수로 강제되는 모든 것에 대해 true 를 반환
// bad
isFinite('2e3'); // true
// good
Number.isFinite('2e3'); // false
Number.isFinite(parseInt('2e3', 10)); // true
1
2
3
4
5
6
2
3
4
5
6
# Testing
function foo() {
return true;
}
1
2
3
2
3
No, but seriously
- 많은 작은 순수 함수를 작성, mutation 이 발생하는 위치를 최소화
- stubs / mocks 에 주의하기 - 테스트를 더 취약하게 만들 수 있음
- Airbnb 는 주로 mocha / jest 사용, tape : 가끔 작은 개별 모듈에서 사용됨
- 100 % 테스트 커버리지는 도달하기가 항상 실용적이지는 않더라도 노력하기에 좋은 목표입니다.
- 버그를 수정할 때마다 회귀 테스트를 작성하십시오. 회귀 테스트없이 수정 된 버그는 거의 확실하게 향후 다시 break 될 것입니다.
# Reference & Origin
https://github.com/airbnb/javascript