본문은 Airbnb JavaScript Style Guide를 번역 및 함축한 글입니다. 오역이 있을 수 있을 수 있습니다.
또한 함축한 내용이므로 좀 더 자세한 내용(코드 별 반례)나 추가적인 자료가 궁금하다면 본문을 참고해주세요.
참고: 이 가이드는 사용자가 Babel과 babel-preset-airbnb 또는 이와 동등한 것을 사용한다고 가정합니다. 또한 사용자가 어플리케이션에 airbnb-browser-shims와 함께 shims/polyfills 또는 이와 동등한 것을 설치했다고 가정합니다.
const foo = 1;
let bar = foo;
bar = 9; // 값을 직접 조작
console.log(foo, bar); // => 1, 9
const foo = [1, 2];
const bar = foo;
bar[0] = 9;
console.log(foo[0], bar[0]); // => 9, 9
// var -> const
const a = 1;
// var -> let
let count = 1;
count += 1;
{
let a = 1;
}
// const도 같은 결과
console.log(a); // ReferenceError
// 객체 생성
const item = {};
function getKey(k) {
return `a key named ${k}`;
}
const obj = {
id: 5,
name: 'Kozel',
[getKey('enabled')]: true;
};
const atom = {
value: 1,
// 단축 구문
addValue(value) {
return atom.value + value;
},
};
const lukeSkywalker = 'Luke Skywalker';
const obj = {
// lukeSkywalker: lukeSkywalker,
lukeSkywalker,
};
const anakinSkywalker = 'Anakin Skywalker';
const lukeSkywalker = 'Luke Skywalker';
const obj = {
lukeSkywalker,
anakinSkywalker,
episodeThree: 3,
mayTheFourth: 4,
};
const good = {
foo: 3,
bar: 4,
'data-blah': 5,
};
console.log(Object.prototype.hasOwnProperty.call(object, key));
// best
const has = Object.prototype.hasOwnProperty; // 모듈스코프에서 한 번 캐시하세요.
console.log(has.call(object, key));
/* or */
import has from 'has'; // https://www.npmjs.com/package/has
console.log(has(object, key));
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 }
const items = [];
const someStack = [];
someStack.push('abracadabra');
...
을 사용하세요.const itemsCopy = [...items];
...
을 사용하세요.const foo = document.querySelectorAll('.foo');
const nodes = Array.from(foo);
// best
const nodes = [...foo];
const arrLike = { 0: 'foo', 1: 'bar', 2: 'baz', length: 3 };
const arr = Array.from(arrLike);
...
대신 Array.from을 사용하세요.// bad
const baz = [...foo].map(bar);
// good
const baz = Array.from(foo, bar);
// map
[1, 2, 3].map((x) => {
return x + 1;
}
// map
[1, 2, 3].map(x => x + 1);
// reduce
[[0, 1], [2, 3], [4, 5]].reduce((acc, item, index) => {
const flatten = acc.concat(item);
acc[index] = flatten;
return flatten;
});
// filter
inbox.filter((msg) => {
const { subject, author } = msg;
if (subject === 'Mockingbird') {
return author === 'Harper Lee';
}
return false;
});
const arr = [[0, 1], [2, 3], [4, 5]];
const objectInArray = [
{
id: 1,
},
{
id: 2,
},
];
const numberInArray = [
1,
2,
];
function getFullName(user) {
const { firstName, lastName } = user;
return `${firstName} ${lastName}`;
}
// best
function getFullName({ firstName, lastName }) {
return `${firstName} ${lastName}`;
}
const arr = [1, 2, 3, 4];
// first = arr[0], second = arr[1]
const [first, second] = arr;
function processInput(input) {
return { left, right, top, bottom };
}
// 반환되는 데이터의 순서 상관 없이 필요한 데이터만 선택하면 됩니다.
const { left, top } = processInput(input);
''
를 사용하세요.const name = 'Kozel';
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.';
function sayHi(name) {
// 단 '${ name }'와 같이 공백의 사용은 지양해주세요.
return `How are you, ${name}?`;
}
const foo = '\'this\' is "quoted"';
const foo = `my name is '${name}'`;
// 변수 참조 호출과 구분되는 이름
const short = function longUniqueMoreDescriptiveLexicalFoo() {
// ...
};
(function () {
console.log('Welcome to the Internet. Please follow me.');
}());
// bad
if (currentUser) {
function test() {
console.log('Nope.');
}
}
// good
let test;
if (currentUser) {
test = () => {
console.log('Yup.');
};
}
// args -> arguments
function foo(name, options, args) {
// ...
}
...
를 사용하세요....
을 사용하면 몇 개의 매개변수를 이용하고 싶은지 확실히 할 수 있습니다. // 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 = opts || {};
}
// still bad
function handleThings(opts) {
if (opts === void 0) {
opts = {};
}
}
// good
function handleThings(opts = {}) {
// ...
}
var b = 1;
// bad
function count(a = b++) {
console.log(a);
}
count(); // 1
count(); // 2
count(3); // 3
count(); // 3
function handleThings(name, opts = {}) {
// ...
}
// bad
var add = new Function('a', 'b', 'return a + b');
// still bad
var subtract = Function('a', 'b', 'return a - b');
// function (공백) () (공백) {};
const x = function () {};
const y = function a() {};
function f2(obj) {
const key = Object.prototype.hasOwnProperty.call(obj, 'key') ? obj.key : 1;
}
function f3(a) {
const b = a || 1;
// ...
}
function f4(a = 1) {
// ...
}
...
을 사용하세요.// 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,
);
// 'function (x)' -> '(x) =>'
[1, 2, 3].map((x) => {
const y = x + 1;
return x * y;
});
[1, 2, 3].map(number => `A string containing the ${number}.`);
[1, 2, 3].map((number) => {
const nextNumber = number + 1;
return `A string containing the ${nextNumber}.`;
});
[1, 2, 3].map((number, index) => ({
[index]: number,
}));
// 암시적 반환없이 사이드 이펙트를 수반합니다
function foo(callback) {
const val = callback();
if (val === true) {
// callback이 참을 반환하면 뭔가를 수행합니다
}
}
let bool = false;
// good
foo(() => {
bool = true;
});
['get', 'post', 'put'].map(httpMethod => (
Object.prototype.hasOwnProperty.call(
httpMagicObjectWithAVeryLongName,
httpMethod,
)
));
// 'x' => '(x)'
[1, 2, 3].map((x) => x * x);
[1, 2, 3].map((x) => {
const y = x + 1;
return x * y;
});
const itemHeight = item => (item.height > 256 ? item.largeSize : item.smallSize);
const itemHeight = (item) => {
const { height, largeSize, smallSize } = item;
return height > 256 ? largeSize : smallSize;
};
// bad
(foo) =>
(bar);
// good
(foo) => bar;
(foo) => (bar);
(foo) => (
bar
)
class
구문은 간결하고 의미를 알기 쉽기 때문입니다.class Queue {
constructor(contents = []) {
this.queue = [...contents];
}
pop() {
const value = this.queue[0];
this.queue.splice(0, 1);
return value;
}
}
instanceof
를 파괴하지 않고 프로토타입 상속을 하기 위해 내장된 방법이기 때문입니다.class PeekableQueue extends Queue {
peek() {
return this.queue[0];
}
}
class Jedi {
jump() {
this.jumping = true;
return this;
}
setHeight(height) {
this.height = height;
return this;
}
}
const luke = new Jedi();
luke.jump()
.setHeight(20);
class Jedi {
constructor(options = {}) {
this.name = options.name || 'no name';
}
getName() {
return this.name;
}
toString() {
return `Jedi - ${this.getName()}`;
}
}
class Rey extends Jedi {
constructor(...args) {
super(...args);
this.name = 'Rey';
}
}
// bad
class Foo {
bar() { return 1; }
bar() { return 2; }
}
// this를 사용했습니다
class Foo {
bar() {
console.log(this.bar);
}
}
// constructor가 면제됩니다
class Foo {
constructor() {
// ...
}
}
// 정적 메소드는 this를 사용하지 않는다고 예상할 수 있습니다
class Foo {
static bar() {
console.log('bar');
}
}
// ok
import AirbnbStyleGuide from './AirbnbStyleGuide';
export default AirbnbStyleGuide.es6;
// best
import { es6 } from './AirbnbStyleGuide';
export default es6;
// bad
import * as AirbnbStyleGuide from './AirbnbStyleGuide';
// good
import AirbnbStyleGuide from './AirbnbStyleGuide';
// filename es6.js
// bad
export { es6 as default } from './AirbnbStyleGuide';
// good
import { es6 } from './AirbnbStyleGuide';
export default es6;
// bad
import foo from 'foo';
// … 또 다른 imports … //
import { named1, named2 } from 'foo';
// good
import foo, { named1, named2 } from 'foo';
// bad
let foo = 3;
export { foo };
// good
const foo = 3;
export { foo };
// bad
export function foo() {}
// good
export default function foo() {}
import foo from 'foo';
import bar from 'bar';
foo.init();
import {
longNameA,
longNameB,
longNameC,
longNameD,
longNameE,
} from 'path';
// bad
import fooSass from 'css!sass!foo.scss';
// good
import fooSass from 'foo.scss';
// bad
import foo from './foo.js';
import baz from './baz/index.jsx';
// good
import foo from './foo';
import baz from './baz';
고급함수는 불변 규칙을 적용합니다. 사이드 이펙트에 대해 추측하는 것보다 값을 반환하는 순수 함수를 다루는 것이 더 간단합니다.
배열을 이터레이트할 때 map() / every() / filter() / find() / findIndex() / reduce() / some() / ... 를 사용하세요.
배열을 생성할 때는 Object.keys() / Object.values() / Object.entries()를 사용해서 모든 객체를 이터레이트 할 수 있습니다.
const numbers = [1, 2, 3, 4, 5];
// 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;
// good
const increasedByOne = [];
numbers.forEach((num) => {
increasedByOne.push(num + 1);
});
// best (keeping it functional)
const increasedByOne = numbers.map(num => num + 1);
function* foo() {
// ...
}
const foo = function* () {
// ...
};
const luke = {
jedi: true,
};
const isJedi = luke.jedi;
const luke = {
jedi: true,
};
function getProp(prop) {
return luke[prop];
}
const isJedi = getProp('jedi');
// bad
const binary = Math.pow(2, 10);
// good
const binary = 2 ** 10;
// bad
superPower = new SuperPower();
// good
const superPower = new SuperPower();
// bad
const items = getItems(),
goSportsTeam = true,
dragonball = 'z';
// good
const items = getItems();
const goSportsTeam = true;
const dragonball = 'z';
const goSportsTeam = true;
const items = getItems();
let dragonball;
let i;
let length;
// 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;
}
// bad
(function example() {
// 자바스크립트는 이것을 let a = ( b = ( c = 1 ) ); 로 해석합니다.
// let 키워드는 변수 a에만 적용되고 b와 c는 전역 변수가 됩니다.
let a = b = c = 1;
}());
console.log(a); // throws ReferenceError
console.log(b); // 1
console.log(c); // 1
eslint 문서에 따르면, 단항 증감 구문은 자동으로 세미콜론을 삽입하며, 어플리케이션에서 값을 증감할 때 오류를 일으킬 수 있습니다.
또한 num += 1과 같은 구문을 통해 값을 변경하는 것이 num++이나 num ++와 같은 구문을 사용하는 것보다 더 의미있는 일이라고 생각합니다.
단항 증감 구문을 사용하지 않으면 프로그램에서 예기치 않은 동작을 일으키는 전위 증감 연산을 막을 수 있습니다.
const array = [1, 2, 3];
let num = 1;
num += 1;
num -= 1;
=
주위에서 줄바꿈을 하는 것은 할당 값을 모호하게 합니다.const foo = (
superLongLongLongLongLongLongLongLongFunctionName()
);
const foo = 'superLongLongLongLongLongLongLongLongString';
// (전역 변수 notDefined가 존재하지 않는다고 판정한 경우)
// 동작하지 않습니다
function example() {
console.log(notDefined); // => throws a ReferenceError
}
// 그 변수를 참조하는 코드의 뒤에서 그 변수를 선언한 경우
// 변수가 호이스트된 상태에서 동작합니다.
// 주의:`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;
}
function example() {
console.log(anonymous); // => undefined
anonymous(); // => TypeError anonymous is not a function
var anonymous = function () {
console.log('anonymous function expression');
};
}
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() {
superPower(); // => Flying
function superPower() {
console.log('Flying');
}
}
''
은, false로 평가됩니다.if ([0] && []) {
// true
// 배열(빈 배열 포함)은 객체이며, 객체는 참으로 평가됩니다.
}
// 불리언 비교
if (isValid) {
// ...
}
// 문자열
if (name !== '') {
// ...
}
// 숫자
if (collection.length > 0) {
// ...
}
// 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 {}
}
}
const foo = maybe1 > maybe2
? 'bar'
: maybeNull;
// best
const foo = maybe1 > maybe2 ? 'bar' : maybeNull;
// 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;
/
와 *
은 섞일 경우 순서가 모호할 수 있으므로 괄호로 감싸는 것을 추천합니다. const foo = (a && b < 0) || c > 0 || (d + 1 === 0);
const bar = (a ** b) - (5 % d);
if (a || (b && c)) {
return d;
}
const bar = a + b / c * d;
// good
if (test) return false;
// good
if (test) {
return false;
}
// bad
function foo() { return false; }
// bad
if (test) {
thing1();
}
else {
thing2();
}
// good
if (test) {
thing1();
} else {
thing2();
}
// good
function foo() {
if (x) {
return x;
}
return y;
}
// good
function cats() {
if (x) {
return x;
}
if (y) {
return y;
}
}
// good
function dogs(x) {
if (x) {
if (z) {
return y;
}
} else {
return z;
}
}
// bad
if ((foo === 123 || bar === 'abc') && doesItLookGoodWhenItBecomesThatLong() && isThisReallyHappening()) {
thing1();
}
// bad
if (foo === 123
&& bar === 'abc') {
thing1();
}
// good
if (
(foo === 123 || bar === 'abc')
&& doesItLookGoodWhenItBecomesThatLong()
&& isThisReallyHappening()
) {
thing1();
}
// good
if (foo === 123 && bar === 'abc') {
thing1();
}
// bad
!isRunning && startRunning();
// good
if (!isRunning) {
startRunning();
}
/**
* make()는 전달된 태그명을 기반으로
* 새로운 요소를 반환한다.
*/
function make(tag) {
// ...
return element;
}
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;
}
// bad
/**
*make()는 전달된 태그명을 기반으로
*새로운 요소를 반환한다.
*/
function make(tag) {
// ...
return element;
}
// good
/**
* make()는 전달된 태그명을 기반으로
* 새로운 요소를 반환한다.
*/
function make(tag) {
// ...
return element;
}
FIXME -- 해결이 필요
또는 TODO -- 구현이 필요
를 뜻합니다.class Calculator extends Abacus {
constructor() {
super();
// FIXME: 전역 변수를 사용해서는 안 됨
total = 0;
}
}
class Calculator extends Abacus {
constructor() {
super();
// TODO: total은 옵션 파라메터로 설정해야함
this.total = 0;
}
}
// bad
function foo() {
∙∙∙∙let name;
}
// good
function baz() {
∙∙let name;
}
// bad
function test(){
console.log('test');
}
// good
function test() {
console.log('test');
}
if (isJedi) {
fight();
}
function fight() {
console.log('Swooosh!');
}
// bad
const x=y+5;
// good
const x = y + 5;
// bad
import { es6 } from './AirbnbStyleGuide';
// ...
export default es6;
// good
import { es6 } from './AirbnbStyleGuide';
// ...
export default es6;↵
// 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);
const arr = [
function foo() {
},
function bar() {
},
];
return arr;
// bad
class Foo {
constructor(bar) {
this.bar = bar;
}
}
// good
if (baz) {
console.log(qux);
} else {
console.log(foo);
}
// bad
class Person {
constructor(fullName, email, birthday) {
this.fullName = fullName;
this.email = email;
this.setAge(birthday);
}
}
// good
class Person {
constructor(fullName, email, birthday) {
this.fullName = fullName;
this.email = email;
this.setAge(birthday);
}
}
// bad
if ( foo ) {
console.log(foo);
}
// good
if (foo) {
console.log(foo);
}
// bad
const foo = [ 1, 2, 3 ];
console.log(foo[ 0 ]);
// good
const foo = [1, 2, 3];
console.log(foo[0]);
// bad
const foo = {clark: 'kent'};
// good
const foo = { clark: 'kent' };
// bad
const foo = jsonData && jsonData.foo && jsonData.foo.bar && jsonData.foo.bar.baz && jsonData.foo.bar.baz.quux && jsonData.foo.bar.baz.quux.xyzzy;
// good
const foo = jsonData
&& jsonData.foo
&& jsonData.foo.bar
&& jsonData.foo.bar.baz
&& jsonData.foo.bar.baz.quux
&& jsonData.foo.bar.baz.quux.xyzzy;
// bad
function foo() {return true;}
if (foo) { bar = 0;}
// good
function foo() { return true; }
if (foo) { bar = 0; }
// bad
var foo = 1,bar = 2;
var arr = [1 , 2];
// good
var foo = 1, bar = 2;
var arr = [1, 2];
// bad
obj[foo ]
obj[ 'foo']
var x = {[ b ]: a}
obj[foo[ bar ]]
// good
obj[foo]
obj['foo']
var x = { [b]: a }
obj[foo[bar]]
// bad
func ();
// good
func();
// bad
var obj = { foo : 42 };
var obj2 = { foo:42 };
// good
var obj = { foo: 42 };
// bad - 여러 개의 빈 줄
var x = 1;
var y = 2;
// bad - 파일 끝에 2개 이상의 빈 줄
var x = 1;
var y = 2;
// bad - 파일 시작에 1개 이상의 빈 줄
var x = 1;
var y = 2;
// good
var x = 1;
var y = 2;
// bad
const story = [
once
, upon
, aTime
];
// good
const story = [
once,
upon,
aTime,
];
// bad - 마지막에 쉼표가 없는 경우 git diff
const hero = {
firstName: 'Florence',
- lastName: 'Nightingale'
+ lastName: 'Nightingale',
+ inventorOf: ['coxcomb chart', 'modern nursing']
};
// good - 마지막에 쉼표가 있는 경우 git diff
const hero = {
firstName: 'Florence',
lastName: 'Nightingale',
+ inventorOf: ['coxcomb chart', 'modern nursing'],
};
// bad
const hero = {
firstName: 'Dana',
lastName: 'Scully'
};
// good
const hero = {
firstName: 'Dana',
lastName: 'Scully',
};
// bad
function createHero(
firstName,
lastName,
inventorOf
) {
// does nothing
}
// good ("나머지" 요소 뒤에 쉼표가 없다는 점에 주의하세요)
function createHero(
firstName,
lastName,
inventorOf,
...heroArgs
) {
// does nothing
}
자바스크립트가 세미콜론이 없는 줄바꿈을 만났을 때, 자동 세미콜론 삽입 규칙에 따라 그 줄바꿈을 구문의 끝으로 간주할지 여부를 결정하고, (이름이 암시하듯) 세미콜론을 줄바꿈 이전에 삽입합니다.
ASI는 몇가지 별난 동작을 포함하고 있지만, 만약 자바스크립트가 줄바꿈을 잘못 해석한다면 코드가 망가져버릴 것입니다.
이 규칙은 새로운 기능이 자바스크립트의 일부가 되면서 더 복잡해집니다. 구문의 끝을 명시하고, 빠뜨린 세미콜론을 잡도록 linter를 설정하면 문제가 발생하는 것을 막을 수 있습니다.
// bad - 예외 발생
const luke = {}
const leia = {}
[luke, leia].forEach(jedi => jedi.father = 'vader')
// good
const luke = {};
const leia = {};
[luke, leia].forEach((jedi) => {
jedi.father = 'vader';
});
// => 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);
parseInt
함수는 특정 기수(radix)에 따른 문자열 인자의 내용을 해석하고, 이에 따라 판단되는 정수 값을 만듭니다. undefined
또는 0
이라면 10
으로 취급되며, 숫자가 0x
또는 0X
문자로 시작하는 경우엔 16진수로 취급됩니다. 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);
/**
* parseInt was the reason my code was slow.
* Bitshifting the String to coerce it to a
* Number made it a lot faster.
* 코드가 느린 원인은 parseInt였음.
* 비트 시프트를 통해 문자열을 강제 형변환하여
* 성능을 개선시킴.
*/
const val = inputValue >> 0;
2147483647 >> 0; // => 2147483647
2147483648 >> 0; // => -2147483648
2147483649 >> 0; // => -2147483647
const age = 0;
// bad
const hasAge = new Boolean(age);
// good
const hasAge = Boolean(age);
// best
const hasAge = !!age;
// bad
function q() {
// ...
}
// bad
const OBJEcttsssss = {};
const this_is_my_object = {};
function c() {}
// good
const thisIsMyObject = {};
function thisIsMyFunction() {}
// bad
function user(options) {
this.name = options.name;
}
const bad = new user({
name: 'nope',
});
// good
class User {
constructor(options) {
this.name = options.name;
}
}
const good = new User({
name: 'yup',
});
// bad
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');
// bad
function foo() {
const self = this;
return function () {
console.log(self);
};
}
// good
function foo() {
return () => {
console.log(this);
};
}
// 파일 1 내용
class CheckBox {
// ...
}
export default CheckBox;
// 파일 2 내용
export default function fortyTwo() { return 42; }
// 파일 3 내용
export default function insideDirectory() {}
// 다른 파일
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
function makeStyleGuide() {
// ...
}
export default makeStyleGuide;
const AirbnbStyleGuide = {
es6: {
},
};
export default AirbnbStyleGuide;
// good
import SMSContainer from './containers/SMSContainer';
// good
const HTTPRequests = [
// ...
];
// also good
const httpRequests = [
// ...
];
// best
import TextMessageContainer from './containers/TextMessageContainer';
// best
const requests = [
// ...
];
해당 상수가
(1) 내보내기 될 때
(2) const 타입일 때 (값이 재할당되지 못할 때)
(3) 그 상수와 상수가 중첩된 속성이 절대 변하지 않는다는 것을 신뢰할 수 있을 때
이것은 변수가 영원히 변하지 않는다는 것을 확신할 수 없을 때 도움을 주기 위한 추가적인 도구입니다. 대문자 변수는 변수와 변수의 속성이 변하지 않는다는 것을 프로그래머에게 알려줍니다.
모든 const
변수 이름을 대문자로 짓는 것은 필수사항이 아니며, 파일 내 상수 이름을 꼭 대문자로 지을 필요는 없습니다. 하지만 내보내기되는 상수 이름은 대문자로 지어야 합니다.
내보내기 되는 객체 이름은 최상위 수준의 내보내기를 할 때 대문자로 이름짓고 (예시: EXPORTED_OBJECT.key
) 모든 중첩된 속성이 변경되지 않도록 유지합니다.
// bad
const PRIVATE_VARIABLE = 'should not be unnecessarily uppercased within a file';
// bad
export const THING_TO_BE_CHANGED = 'should obviously not be uppercased';
// bad
export let REASSIGNABLE_VARIABLE = 'do not use let with uppercase variables';
// ---
// allowed but does not supply semantic value
export const apiKey = 'SOMEKEY';
// better in most cases
export const API_KEY = 'SOMEKEY';
// ---
// bad - unnecessarily uppercases key while adding no semantic value
export const MAPPING = {
KEY: 'value'
};
// good
export const MAPPING = {
key: 'value'
};
getVal()
과 setVal('hello')
를 사용하세요.// bad
class Dragon {
get age() {
// ...
}
set age(value) {
// ...
}
}
// good
class Dragon {
getAge() {
// ...
}
setAge(value) {
// ...
}
}
// bad
if (!dragon.age()) {
return false;
}
// good
if (!dragon.hasAge()) {
return false;
}
class Jedi {
constructor(options = {}) {
const lightsaber = options.lightsaber || 'blue';
this.set('lightsaber', lightsaber);
}
set(key, val) {
this[key] = val;
}
get(key) {
return this[key];
}
}
// 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
});
// bad
const sidebar = $('.sidebar');
// good
const $sidebar = $('.sidebar');
// good
const $sidebarBtn = $('.sidebar-btn');
// bad
function setSidebar() {
$('.sidebar').hide();
// ...
$('.sidebar').css({
'background-color': 'pink',
});
}
// good
function setSidebar() {
const $sidebar = $('.sidebar');
$sidebar.hide();
// ...
$sidebar.css({
'background-color': 'pink',
});
}
// bad
$('ul', '.sidebar').hide();
// bad
$('.sidebar').find('ul').hide();
// good
$('.sidebar ul').hide();
// good
$('.sidebar > ul').hide();
// good
$sidebar.find('ul').hide();
전체적으로 중요하게 다루는 점 2가지를 뽑자면 다음과 같습니다.
- 이슈 예방
- 가독성
기본적으로 코드의 규칙성을 만들어 혹시 모를 이슈에 대한 예외처리를 확실하게 하고 프로그래머를 위한 가독성을 높이는 것이 목적이라고 느꼈습니다.
본문은 앞서도 말했지만 '코드 작성'에 집중하여 내용을 함축하여 작성한 것입니다.
위 내용 말고도 도움되는 내용이 많으니 코드 작성 말고도 궁금한 점이 있으시다면 본문을 참고하시면 좋을 것 같습니다.
잘못된 점에 대한 지적과 조언은 언제든 환영입니다.