Featured image of post Airbnb JavaScript 스타일 가이드

Airbnb JavaScript 스타일 가이드

Airbnb JavaScript 스타일 가이드

Photo by Pankaj Patel on Unsplash

https://github.com/jigsawye/javascript

Airbnb JavaScript 스타일 가이드()

JavaScript에 대한 가장 합리적인 접근 방식

Downloads Gitter

다른 스타일 가이드

Airbnb JavaScript Style Guide에서 번역되었습니다.

타입 (Types)

  • 1.1 원시형 (Primitives): 원시형에는 직접 접근합니다.
  • string
  • number
  • boolean
  • null
  • undefined
const foo = 1;
let bar = foo;

bar = 9;

console.log(foo, bar); // => 1, 9
  • 1.2 참조형 (Complex): 참조형은 참조를 통해 접근합니다.
  • object
  • array
  • function
const foo = [1, 2];
const bar = foo;

bar[0] = 9;

console.log(foo[0], bar[0]); // => 9, 9

참조 (References)

왜요? 이렇게 하면 참조를 재할당할 수 없도록 하여 버그를 줄이고 코드를 이해하기 쉽게 만듭니다.

// bad
var a = 1;
var b = 2;

// good
const a = 1;
const b = 2;
  • 2.2 참조를 재할당해야 하는 경우 var 대신 let을 사용하십시오. eslint: no-var jscs: disallowVar

왜요? letvar와 같은 함수 스코프가 아닌 블록 스코프이기 때문입니다.

// bad
var count = 1;
if (true) {
    count += 1;
}

// good, use the let.
let count = 1;
if (true) {
    count += 1;
}
  • 2.3 letconst는 모두 블록 스코프라는 점에 유의하십시오.
// const와 let은 그들이 정의된 블록 내에서만 존재합니다.
{
    let a = 1;
    const b = 1;
}
console.log(a); // ReferenceError
console.log(b); // ReferenceError

객체 (Objects)

  • 3.1 객체를 생성할 때는 리터럴 구문을 사용하십시오. eslint rules: no-new-object.
// bad
const item = new Object();

// good
const item = {};
// bad
const superman = {
    default: { clark: 'kent' },
    private: true,
};

// good
const superman = {
    defaults: { clark: 'kent' },
    hidden: true,
};
// bad
const superman = {
    class: 'alien',
};

// bad
const superman = {
    klass: 'alien',
};

// good
const superman = {
    type: 'alien',
};

  • 3.4 동적 속성 이름으로 객체를 생성할 때는 계산된 속성 이름(computed property names)을 사용하십시오.

왜요? 객체의 모든 속성을 한 곳에서 정의할 수 있기 때문입니다.


function getKey(k) {
    return `a key named ${k}`;
}

// bad
const obj = {
    id: 5,
    name: 'San Francisco',
};
obj[getKey('enabled')] = true;

// good
const obj = {
    id: 5,
    name: 'San Francisco',
    [getKey('enabled')]: true,
};

// bad
const atom = {
    value: 1,

    addValue: function (value) {
    return atom.value + value;
    },
};

// good
const atom = {
    value: 1,

    addValue(value) {
    return atom.value + value;
    },
};

왜요? 더 짧게 작성할 수 있고 명확하기 때문입니다.

const lukeSkywalker = 'Luke Skywalker';

// bad
const obj = {
    lukeSkywalker: lukeSkywalker,
};

// good
const obj = {
    lukeSkywalker,
};
  • 3.7 단축 표기 속성은 객체 선언의 시작 부분에 그룹화하십시오.

왜요? 어떤 속성이 단축 표기를 사용하는지 쉽게 알 수 있기 때문입니다.

const anakinSkywalker = 'Anakin Skywalker';
const lukeSkywalker = 'Luke Skywalker';

// bad
const obj = {
    episodeOne: 1,
    twoJediWalkIntoACantina: 2,
    lukeSkywalker,
    episodeThree: 3,
    mayTheFourth: 4,
    anakinSkywalker,
};

// good
const obj = {
    lukeSkywalker,
    anakinSkywalker,
    episodeOne: 1,
    twoJediWalkIntoACantina: 2,
    episodeThree: 3,
    mayTheFourth: 4,
};

왜요? 일반적으로 주관적으로 읽기 쉽다고 간주되기 때문입니다. 또한 구문 강조 표시가 향상되고 많은 JS 엔진에서 최적화되기 쉽습니다.

// bad
const bad = {
    'foo': 3,
    'bar': 4,
    'data-blah': 5,
};

// good
const good = {
    foo: 3,
    bar: 4,
    'data-blah': 5,
};

배열 (Arrays)

// bad
const items = new Array();

// good
const items = [];
  • 4.2 배열에 항목을 추가할 때는 직접 할당하는 대신 Array#push를 사용하십시오.
const someStack = [];

// bad
someStack[someStack.length] = 'abracadabra';

// good
someStack.push('abracadabra');

  • 4.3 배열을 복사하려면 전개 연산자(spread operator) ...를 사용하십시오.
// bad
const len = items.length;
const itemsCopy = [];
let i;

for (i = 0; i < len; i++) {
    itemsCopy[i] = items[i];
}

// good
const itemsCopy = [...items];
  • 4.4 배열과 유사한 객체를 배열로 변환하려면 Array#from을 사용하십시오.
const foo = document.querySelectorAll('.foo');
const nodes = Array.from(foo);
  • 4.5 배열 메서드 콜백에서는 return 문을 사용하십시오. 단, 함수 본문이 단일 문으로 구성된 경우(8.2 참조)는 return을 생략할 수 있습니다. eslint: array-callback-return
// good
[1, 2, 3].map((x) => {
    const y = x + 1;
    return x * y;
});

// good
[1, 2, 3].map(x => x + 1);

// bad
const flat = {};
[[0, 1], [2, 3], [4, 5]].reduce((memo, item, index) => {
    const flatten = memo.concat(item);
    flat[index] = memo.concat(item);
});

// good
const flat = {};
[[0, 1], [2, 3], [4, 5]].reduce((memo, item, index) => {
    const flatten = memo.concat(item);
    flat[index] = flatten;
    return flatten;
});

// bad
inbox.filter((msg) => {
    const { subject, author } = msg;
    if (subject === 'Mockingbird') {
    return author === 'Harper Lee';
    } else {
    return false;
    }
});

// good
inbox.filter((msg) => {
    const { subject, author } = msg;
    if (subject === 'Mockingbird') {
    return author === 'Harper Lee';
    }

    return false;
});

구조 분해 (Destructuring)

왜요? 속성에 대한 임시 참조를 만들지 않아도 되기 때문입니다.

// 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}`;
}
const arr = [1, 2, 3, 4];

// bad
const first = arr[0];
const second = arr[1];

// good
const [first, second] = arr;
  • 5.3 여러 값을 반환하는 경우 배열 구조 분해 대신 객체 구조 분해를 사용하십시오.

왜요? 호출하는 곳을 수정하지 않고도 새로운 속성을 추가하거나 순서를 변경할 수 있기 때문입니다.

// bad
function processInput(input) {
    // then a miracle occurs
    return [left, right, top, bottom];
}

// the caller needs to think about the order of return data
const [left, __, top] = processInput(input);

// good
function processInput(input) {
    // then a miracle occurs
    return { left, right, top, bottom };
}

// the caller selects only the data they need
const { left, right } = processInput(input);

문자열 (Strings)

// bad
const name = "Capt. Janeway";

// good
const name = 'Capt. Janeway';
  • 6.2 100자를 초과하는 긴 문자열은 문자열 연결을 사용하여 여러 줄에 걸쳐 작성하지 마십시오.
  • 6.3 주의: 긴 문자열 연결을 과도하게 사용하면 성능에 영향을 줄 수 있습니다. jsPerf & Discussion.
// 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.';

왜요? 템플릿 문자열은 적절한 줄 바꿈과 문자열 보간 기능을 갖춘 읽기 쉽고 간결한 구문을 제공하기 때문입니다.

// 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}?`;
}
  • 6.5 문자열에 eval()을 절대 사용하지 마십시오. 취약점이 너무 많습니다.

함수 (Functions)

왜요? 함수 선언은 이름이 있기 때문에 스택 추적에서 식별하기 쉽습니다. 또한 함수 선언은 호이스팅(hoisting)되지만 함수 표현식은 그렇지 않습니다. 이 규칙을 통해 화살표 함수가 함수 표현식을 완전히 대체할 수 있습니다.

// bad
const foo = function () {
};

// good
function foo() {
}

왜요? IIFE는 단일 단위입니다. 함수와 호출을 모두 괄호로 감싸면 이것이 명확해집니다. 모듈 세계에서는 일반적으로 IIFE가 더 이상 필요하지 않다는 점에 유의하십시오.

// immediately-invoked function expression (IIFE)
(function () {
    console.log('Welcome to the Internet. Please follow me.');
}());
  • 7.3 함수가 아닌 블록(if, while 등) 내에서 함수를 선언하지 마십시오. 대신 함수를 변수에 할당하십시오. 브라우저에서는 허용하지만 각각 다르게 해석합니다. eslint: no-loop-func

  • 7.4 주의: ECMA-262는 block을 문(statement)의 목록으로 정의합니다. 함수 선언은 문이 아닙니다. 이 문제에 대한 ECMA-262의 설명을 읽어보십시오.

// bad
if (currentUser) {
    function test() {
    console.log('Nope.');
    }
}

// good
let test;
if (currentUser) {
    test = () => {
    console.log('Yup.');
    };
}
  • 7.5 매개변수 이름을 arguments로 지정하지 마십시오. 이는 모든 함수 스코프에 제공되는 arguments 객체보다 우선합니다.
// bad
function nope(name, options, arguments) {
    // ...stuff...
}

// good
function yup(name, options, args) {
    // ...stuff...
}

왜요? ...는 어떤 인수를 가져올지 명시적입니다. 또한 rest 인수는 arguments와 같은 ‘배열 유사 객체’가 아니라 실제 배열입니다.

// bad
function concatenateAll() {
    const args = Array.prototype.slice.call(arguments);
    return args.join('');
}

// good
function concatenateAll(...args) {
    return args.join('');
}

  • 7.7 함수 인수를 변경하는 대신 기본 매개변수 구문을 사용하십시오.
// really bad
function handleThings(opts) {
    // No! We shouldn't mutate function arguments.
    // Double bad: if opts is false it will be set to an object which may
    // be what you want but it can cause subtle bugs.
    opts = opts || {};
    // ...
}

// still bad
function handleThings(opts) {
    if (opts === void 0) {
    opts = {};
    }
    // ...
}

// good
function handleThings(opts = {}) {
    // ...
}
  • 7.8 기본 매개변수에서 부작용을 피하십시오.

왜요? 동작을 이해하기 혼란스럽기 때문입니다.

var b = 1;
// bad
function count(a = b++) {
    console.log(a);
}
count();  // 1
count();  // 2
count(3); // 3
count();  // 3
  • 7.9 기본 매개변수는 항상 마지막에 두십시오.
// bad
function handleThings(opts = {}, name) {
    // ...
}

// good
function handleThings(name, opts = {}) {
    // ...
}
  • 7.10 새 함수를 생성하기 위해 Function 생성자를 사용하지 마십시오.

왜요? 이 방식으로 함수를 생성하면 eval()과 유사하게 문자열을 평가하여 취약점을 유발할 수 있습니다.

// bad
var add = new Function('a', 'b', 'return a + b');

// still bad
var subtract = Function('a', 'b', 'return a - b');
  • 7.11 함수 시그니처의 공백.

왜요? 일관성은 좋은 것이며, 이름을 추가하거나 제거할 때 공백을 추가하거나 제거할 필요가 없어야 합니다.

// bad
const f = function(){};
const g = function (){};
const h = function() {};

// good
const x = function () {};
const y = function a() {};

왜요? 매개변수로 전달된 객체를 조작하면 호출자에서 원치 않는 변수 부작용이 발생할 수 있습니다.

// bad
function f1(obj) {
    obj.key = 1;
};

// good
function f2(obj) {
    const key = Object.prototype.hasOwnProperty.call(obj, 'key') ? obj.key : 1;
};

왜요? 매개변수 재할당은 특히 arguments 객체에 접근할 때 예상치 못한 동작을 유발할 수 있습니다. 또한 V8 엔진에서 최적화 문제를 일으킬 수도 있습니다.

```javascript
// 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) {
}
```

화살표 함수 (Arrow Functions)

왜요? 이것은 this 컨텍스트에서 실행되는 함수 버전을 생성하며(일반적으로 원하는 동작), 구문도 더 간결합니다.

왜 안되나요? 상당히 복잡한 함수의 경우 해당 로직을 자체 함수 선언으로 이동하는 것이 좋습니다.

// 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;
});

왜요? 문법적 설탕(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}.`);

// good
[1, 2, 3].map((number) => {
    const nextNumber = number + 1;
    return `A string containing the ${nextNumber}.`;
});
  • 8.3 식이 여러 줄에 걸쳐 있는 경우 가독성을 위해 괄호로 감싸십시오.

왜요? 함수의 시작과 끝을 명확하게 보여줍니다.

// bad
[1, 2, 3].map(number => 'As time went by, the string containing the ' +
    `${number} became much longer. So we needed to break it over multiple ` +
    'lines.'
);

// good
[1, 2, 3].map(number => (
    `As time went by, the string containing the ${number} became much ` +
    'longer. So we needed to break it over multiple lines.'
));

왜요? 시각적으로 더 깔끔합니다.

// bad
[1, 2, 3].map((x) => x * x);

// good
[1, 2, 3].map(x => x * x);

```javascript
// good
[1, 2, 3].map(number => (
    `A long string with the ${number}. Its so long that weve broken it ` +
    'over multiple lines!'
));

// 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;
});
  • 8.5 화살표 함수 구문(=>)을 비교 연산자(<=, >=)와 혼동하지 않도록 하십시오. eslint: no-confusing-arrow
// 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 => { return item.height > 256 ? item.largeSize : item.smallSize; }

클래스 (Classes & Constructors)

  • 9.1 항상 class를 사용하십시오. prototype을 직접 조작하는 것은 피하십시오.

왜요? class 구문이 더 간결하고 논리적으로 이해하기 쉽습니다.

// bad
function Queue(contents = []) {
    this._queue = [...contents];
}
Queue.prototype.pop = function () {
    const value = this._queue[0];
    this._queue.splice(0, 1);
    return value;
}


// good
class Queue {
    constructor(contents = []) {
    this._queue = [...contents];
    }
    pop() {
    const value = this._queue[0];
    this._queue.splice(0, 1);
    return value;
    }
}
  • 9.2 상속에는 extends를 사용하십시오.

왜요? 이것은 instanceof를 깨트리지 않고 프로토타입 기능을 상속하는 기본 제공 방법입니다.

// bad
const inherits = require('inherits');
function PeekableQueue(contents) {
    Queue.apply(this, contents);
}
inherits(PeekableQueue, Queue);
PeekableQueue.prototype.peek = function () {
    return this._queue[0];
}

// good
class PeekableQueue extends Queue {
    peek() {
    return this._queue[0];
    }
}
  • 9.3 메서드 체이닝을 돕기 위해 메서드 내에서 this를 반환할 수 있습니다.
// bad
Jedi.prototype.jump = function () {
    this.jumping = true;
    return true;
};

Jedi.prototype.setHeight = function (height) {
    this.height = height;
};

const luke = new Jedi();
luke.jump(); // => true
luke.setHeight(20); // => undefined

// good
class Jedi {
    jump() {
    this.jumping = true;
    return this;
    }

    setHeight(height) {
    this.height = height;
    return this;
    }
}

const luke = new Jedi();

luke.jump()
    .setHeight(20);
  • 9.4 사용자 정의 toString() 메서드를 작성하는 것은 괜찮지만 올바르게 작동하고 부작용이 없는지 확인하십시오.
class Jedi {
    constructor(options = {}) {
    this.name = options.name || 'no name';
    }

    getName() {
    return this.name;
    }

    toString() {
    return `Jedi - ${this.getName()}`;
    }
}
  • 9.5 지정되지 않은 경우 클래스에는 기본 생성자가 있습니다. 빈 생성자나 단순히 부모 클래스에 위임하는 생성자는 필요하지 않습니다. no-useless-constructor
// bad
class Jedi {
    constructor() {}

    getName() {
    return this.name;
    }
}

// bad
class Rey extends Jedi {
    constructor(...args) {
    super(...args);
    }
}

// good
class Rey extends Jedi {
    constructor(...args) {
    super(...args);
    this.name = 'Rey';
    }
}

모듈 (Modules)

  • 10.1 비표준 모듈 시스템보다 항상 모듈(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;
  • 10.2 와일드카드 가져오기는 사용하지 마십시오.

왜요? 이렇게 하면 단일 기본 내보내기를 갖도록 보장됩니다.

// bad
import * as AirbnbStyleGuide from './AirbnbStyleGuide';

// good
import AirbnbStyleGuide from './AirbnbStyleGuide';
  • 10.3 또한 import에서 직접 export하지 마십시오.

왜요? 한 줄로 작성하는 것이 간결하긴 하지만, import와 export를 명확하게 분리하여 일관성을 유지하십시오.

// bad
// filename es6.js
export { es6 as default } from './airbnbStyleGuide';

// good
// filename es6.js
import { es6 } from './AirbnbStyleGuide';
export default es6;

이터레이터와 제너레이터 (Iterators and Generators)

  • 11.1 이터레이터를 사용하지 마십시오. for-of 루프 대신 map()reduce()와 같은 JavaScript의 고차 함수를 사용하십시오. eslint: no-iterator

왜요? 이것은 불변성 규칙을 강제합니다. 부작용을 처리하는 것보다 값을 반환하는 순수 함수를 처리하는 것이 추론하기 쉽습니다.

eslint rules: no-iterator.

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;
  • 11.2 제너레이터는 현재로서는 사용하지 마십시오.

왜요? ES5 로의 트랜스파일이 아직 완벽하지 않기 때문입니다.

속성 (Properties)

const luke = {
    jedi: true,
    age: 28,
};

// bad
const isJedi = luke['jedi'];

// good
const isJedi = luke.jedi;
  • 12.2 변수를 사용하여 속성에 접근할 때는 대괄호 표기법 []을 사용하십시오.
const luke = {
    jedi: true,
    age: 28,
};

function getProp(prop) {
    return luke[prop];
}

const isJedi = getProp('jedi');

변수 (Variables)

  • 13.1 변수를 선언할 때는 항상 const 또는 let을 사용하십시오. 그렇지 않으면 전역 변수가 됩니다. 전역 네임스페이스를 오염시키는 것은 피하고 싶을 것입니다. Captain Planet도 그렇게 경고했습니다.
// bad
superPower = new SuperPower();

// good
const superPower = new SuperPower();

왜요? 이렇게 하면 새로운 변수 선언을 추가하기 쉽고, ;,로 바꾸거나 구두점만의 차이(diff)를 걱정할 필요가 없습니다.

// bad
const items = getItems(),
    goSportsTeam = true,
    dragonball = 'z';

// bad
// (compare to above, notice the error)
const items = getItems(),
    goSportsTeam = true;
    dragonball = 'z';

// good
const items = getItems();
const goSportsTeam = true;
const dragonball = 'z';
  • 13.3 모든 const를 그룹화한 다음 모든 let을 그룹화하십시오.

왜요? 나중에 이전에 할당된 변수를 기반으로 변수를 할당해야 할 때 유용합니다.

// 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;
  • 13.4 변수는 필요한 곳에 할당하되 합리적인 위치에 두십시오.

왜요? letconst는 함수 스코프가 아니라 블록 스코프이기 때문입니다.

// 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;
}

호이스팅 (Hoisting)

// we know this wouldn't work (assuming there
// is no notDefined global variable)
function example() {
    console.log(notDefined); // => throws a ReferenceError
}

// creating a variable declaration after you
// reference the variable will work due to
// variable hoisting. Note: the assignment
// value of `true` is not hoisted.
function example() {
    console.log(declaredButNotAssigned); // => undefined
    var declaredButNotAssigned = true;
}

// The interpreter is hoisting the variable
// declaration to the top of the scope,
// which means our example could be rewritten as:
function example() {
    let declaredButNotAssigned;
    console.log(declaredButNotAssigned); // => undefined
    declaredButNotAssigned = true;
}

// using const and let
function example() {
    console.log(declaredButNotAssigned); // => throws a ReferenceError
    console.log(typeof declaredButNotAssigned); // => throws a ReferenceError
    const declaredButNotAssigned = true;
}
  • 14.2 익명 함수 표현식은 변수 이름을 끌어올리지만 함수 할당은 끌어올리지 않습니다.
function example() {
    console.log(anonymous); // => undefined

    anonymous(); // => TypeError anonymous is not a function

    var anonymous = function () {
    console.log('anonymous function expression');
    };
}
  • 14.3 명명된 함수 표현식은 변수 이름을 끌어올리지만 함수 이름이나 함수 본문은 끌어올리지 않습니다.
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');
    };
}

// the same is true when the function name
// is the same as the variable name.
function example() {
    console.log(named); // => undefined

    named(); // => TypeError named is not a function

    var named = function named() {
    console.log('named');
    }
}
  • 14.4 함수 선언은 이름과 함수 본문을 끌어올립니다(hoisting).
function example() {
    superPower(); // => Flying

    function superPower() {
    console.log('Flying');
    }
}

비교 연산자와 등가성 (Comparison Operators & Equality)

  • 15.1 ==!=보다 ===!==을 사용하십시오. eslint: eqeqeq

  • 15.2 if 문과 같은 조건문은 ToBoolean 추상 메서드를 사용하여 강제로 평가하며 항상 다음의 간단한 규칙을 따릅니다:

  • 객체true로 평가됩니다
  • Undefinedfalse로 평가됩니다
  • Nullfalse로 평가됩니다
  • 부울부울 값으로 평가됩니다
  • 숫자+0, -0, NaN이면 false, 그 외에는 true로 평가됩니다
  • 문자열은 빈 문자열 ''이면 false, 그 외에는 true로 평가됩니다
if ([0] && []) {
    // true
    // an array (even an empty one) is an object, objects will evaluate to true
}
  • 15.3 단축 평가를 사용하십시오.
// bad
if (name !== '') {
    // ...stuff...
}

// good
if (name) {
    // ...stuff...
}

// bad
if (collection.length > 0) {
    // ...stuff...
}

// good
if (collection.length) {
    // ...stuff...
}
  • 15.4 자세한 내용은 Angus Croll의 Truth Equality and JavaScript를 참조하십시오.
  • 15.5 어휘적 선언(let, const, function, class 등)이 포함된 casedefault 절에서는 블록을 생성하기 위해 중괄호를 사용하십시오.

왜요? 어휘적 선언은 전체 switch 블록에서 볼 수 있지만, 할당될 때만 초기화되며, 이는 해당 case에 도달할 때만 발생합니다. 여러 case 절이 동일한 것을 정의하려고 할 때 문제가 발생합니다.

eslint rules: no-case-declarations.

// bad
switch (foo) {
    case 1:
    let x = 1;
    break;
    case 2:
    const y = 2;
    break;
    case 3:
    function f() {}
    break;
    default:
    class C {}
}

// 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 {}
    }
}
  • 15.7 삼항 연산자는 중첩되어서는 안 되며 일반적으로 한 줄로 작성해야 합니다.

eslint rules: no-nested-ternary.

// bad
const foo = maybe1 > maybe2
    ? "bar"
    : value1 > value2 ? "baz" : null;

// better
const maybeNull = value1 > value2 ? 'baz' : null;

const foo = maybe1 > maybe2
    ? 'bar'
    : maybeNull;

// best
const maybeNull = value1 > value2 ? 'baz' : null;

const foo = maybe1 > maybe2 ? 'bar' : maybeNull;
  • 15.8 불필요한 삼항 연산자는 피하십시오.

eslint rules: no-unneeded-ternary.

// 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;

블록 (Blocks)

  • 16.1 모든 여러 줄 블록에는 중괄호를 사용하십시오.
// 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;
}
// bad
if (test) {
    thing1();
    thing2();
}
else {
    thing3();
}

// good
if (test) {
    thing1();
    thing2();
} else {
    thing3();
}

제어문 (Control Statements)

  • 17.1 제어문(if, while 등)이 너무 길어지거나 최대 줄 길이를 초과하는 경우, 각(그룹화된) 조건을 새 줄에 넣을 수 있습니다. 논리 연산자는 줄의 시작 부분에 있어야 합니다.

왜요? 연산자를 줄의 시작 부분에 두면 연산자가 정렬되고 메서드 체이닝과 유사한 패턴을 따릅니다. 또한 복잡한 논리를 시각적으로 따라가기 쉬워져 가독성이 향상됩니다.

// 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();
}
  • 17.2 제어문 대신 선택 연산자를 사용하지 마십시오.
// bad
!isRunning && startRunning();

// good
if (!isRunning) {
    startRunning();
}

주석 (Comments)

  • 18.1 여러 줄 주석에는 /** ... */를 사용하십시오. 설명을 포함하고 모든 매개변수와 반환 값의 유형과 값을 지정하십시오.
// bad
// make() returns a new element
// based on the passed in tag name
//
// @param {String} tag
// @return {Element} element
function make(tag) {

    // ...stuff...

    return element;
}

// good
/**
 * make() returns a new element
 * based on the passed in tag name
 *
 * @param {String} tag
 * @return {Element} element
 */
function make(tag) {

    // ...stuff...

    return element;
}
  • 18.2 한 줄 주석에는 //를 사용하십시오. 한 줄 주석은 주석 처리 대상인 행 위에 새 줄에 배치하십시오. 블록의 첫 번째 줄이 아닌 한 주석 앞에 빈 줄을 넣으십시오.
// 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;
}
  • 18.3 주석의 접두사로 FIXMETODO를 사용하면 다른 개발자가 다시 방문해야 할 문제인지, 아니면 구현해야 할 해결책인지 빠르게 이해하는 데 도움이 됩니다. 이들은 일반 주석과 달리 조치를 취할 수 있습니다(actionable). 조치는 FIXME: -- 해결해야 함 또는 TODO: -- 구현해야 함입니다.

  • 18.4 문제를 주석 처리하려면 // FIXME:를 사용하십시오.

class Calculator extends Abacus {
    constructor() {
    super();

    // FIXME: shouldn't use a global here
    total = 0;
    }
}
  • 18.5 문제 해결책을 주석 처리하려면 // TODO:를 사용하십시오.
class Calculator extends Abacus {
    constructor() {
    super();

    // TODO: total should be configurable by an options param
    this.total = 0;
    }
}

공백 (Whitespace)

// bad
function foo() {
∙∙∙∙const name;
}

// bad
function bar() {
const name;
}

// good
function baz() {
∙∙const name;
}
// bad
function test(){
    console.log('test');
}

// good
function test() {
    console.log('test');
}

// bad
dog.set('attr',{
    age: '1 year',
    breed: 'Bernese Mountain Dog',
});

// good
dog.set('attr', {
    age: '1 year',
    breed: 'Bernese Mountain Dog',
});
// bad
if(isJedi) {
    fight ();
}

// good
if (isJedi) {
    fight();
}

// bad
function fight () {
    console.log ('Swooosh!');
}

// good
function fight() {
    console.log('Swooosh!');
}
// bad
const x=y+5;

// good
const x = y + 5;
  • 19.5 파일 끝은 단일 개행 문자로 끝나야 합니다.
// bad
(function (global) {
    // ...stuff...
})(this);
// bad
(function (global) {
    // ...stuff...
})(this);

// good
(function (global) {
    // ...stuff...
})(this);
  • 19.6 긴 메서드 체이닝(2개 이상의 메서드 체이닝)을 작성할 때는 들여쓰기를 사용하십시오. 선행 점을 사용하여 해당 줄이 새로운 문이 아니라 메서드 호출임을 강조하십시오. eslint: newline-per-chained-call no-whitespace-before-property
// 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);
// bad
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;
// bad
function bar() {

    console.log(foo);

}

// also bad
if (baz) {

    console.log(qux);
} else {
    console.log(foo);

}

// good
function bar() {
    console.log(foo);
}

// good
if (baz) {
    console.log(qux);
} else {
    console.log(foo);
}
// 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
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 = 'Whatever national crop flips the window. The cartoon reverts within the screw. Whatever wizard constrains a helpful ally. The counterpart ascends!';

// 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 = 'Whatever national crop flips the window. The cartoon reverts within the screw. ' +
    'Whatever wizard constrains a helpful ally. The counterpart ascends!';

// good
$.ajax({
    method: 'POST',
    url: 'https://airbnb.com/',
    data: { name: 'John' },
})
    .done(() => console.log('Congratulations!'))
    .fail(() => console.log('You have failed this city.'));

콤마 (Commas)

// 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',
};

왜요? 이렇게 하면 Git diff가 더 깔끔해집니다. 또한 Babel과 같은 트랜스파일러는 트랜스파일된 코드에서 여분의 후행 콤마를 제거하므로 레거시 브라우저의 후행 콤마 문제에 대해 걱정할 필요가 없습니다.

// bad - git diff without trailing comma
const hero = {
        firstName: 'Florence',
    lastName: 'Nightingale'
+    lastName: 'Nightingale',
+    inventorOf: ['coxcomb graph', 'modern nursing']
};

// good - git diff with trailing comma
const hero = {
        firstName: 'Florence',
        lastName: 'Nightingale',
+    inventorOf: ['coxcomb chart', 'modern nursing'],
};

// bad
const hero = {
    firstName: 'Dana',
    lastName: 'Scully'
};

const heroes = [
    'Batman',
    'Superman'
];

// good
const hero = {
    firstName: 'Dana',
    lastName: 'Scully',
};

const heroes = [
    'Batman',
    'Superman',
];

세미콜론 (Semicolons)

// bad
(function () {
    const name = 'Skywalker'
    return name
})()

// good
(() => {
    const name = 'Skywalker';
    return name;
}());

// good (guards against the function becoming an argument when two files with IIFEs are concatenated)
;(() => {
    const name = 'Skywalker';
    return name;
}());

더보기.

타입 변환과 강제 (Type Casting & Coercion)

  • 22.1 문장의 시작 부분에서 타입 강제를 수행하십시오.
  • 22.2 문자열:
// => this.reviewScore = 9;

// bad
const totalScore = this.reviewScore + '';

// good
const totalScore = String(this.reviewScore);
  • 22.3 숫자: 타입변환에는 Number를 사용하고 문자열 구문 분석에는 항상 기수(radix)와 함께 parseInt를 사용하십시오. eslint: radix
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);
  • 22.4 어떤 이유로 미친 짓을 하고 있고 parseInt가 병목 현상을 일으키며 성능상의 이유로 비트 시프트를 사용해야 하는 경우, 그 이유와 수행 중인 작업을 설명하는 주석을 남기십시오.
// good
/**
 * parseInt was the reason my code was slow.
 * Bitshifting the String to coerce it to a
 * Number made it a lot faster.
 */
const val = inputValue >> 0;
  • 22.5 주의: 비트 시프트 연산을 사용할 때는 주의하십시오. 숫자는 64비트 값으로 표시되지만 비트 시프트 연산은 항상 32비트 정수를 반환합니다 (source). 비트 시프트는 32비트보다 큰 정수 값에 대해 예상치 못한 동작을 유발할 수 있습니다. Discussion. 부호 있는 32비트 정수의 최대값은 2,147,483,647입니다:
2147483647 >> 0 //=> 2147483647
2147483648 >> 0 //=> -2147483648
2147483649 >> 0 //=> -2147483647
const age = 0;

// bad
const hasAge = new Boolean(age);

// good
const hasAge = Boolean(age);

// good
const hasAge = !!age;

명명 규칙 (Naming Conventions)

  • 23.1 한 글자 이름은 피하십시오. 설명적인 이름을 사용하십시오.
// bad
function q() {
    // ...stuff...
}

// good
function query() {
    // ..stuff..
}
// 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';
  • 23.5 this에 대한 참조를 저장하지 마십시오. 화살표 함수 또는 Function#bind를 사용하십시오. jscs: disallowNodeTypes
// 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);
    };
}
  • 23.6 파일이 단일 클래스를 내보내는 경우 파일 이름은 클래스 이름과 정확히 일치해야 합니다.
// file contents
class CheckBox {
    // ...
}
export default CheckBox;

// in some other file
// bad
import CheckBox from './checkBox';

// bad
import CheckBox from './check_box';

// good
import CheckBox from './CheckBox';
  • 23.7 기본 함수를 내보낼 때는 카멜 케이스를 사용하십시오. 파일 이름은 함수 이름과 동일해야 합니다.
function makeStyleGuide() {
}

export default makeStyleGuide;
  • 23.8 싱글톤 / 함수 라이브러리 / 빈 객체를 내보낼 때는 파스칼 케이스를 사용하십시오.
const AirbnbStyleGuide = {
    es6: {
    }
};

export default AirbnbStyleGuide;

접근자 (Accessors)

  • 24.1 속성의 접근자 함수는 필수 사항이 아닙니다.
  • 24.2 JavaScript getter/setter를 사용하지 마십시오. 예상치 못한 부작용을 일으키고 테스트, 유지 관리 및 추론하기 어렵게 만들 수 있기 때문입니다. 대신 접근자 함수를 만드는 경우 getVal() 및 setVal(‘hello’)를 사용하십시오.
// bad
dragon.age();

// good
dragon.getAge();

// bad
dragon.age(25);

// good
dragon.setAge(25);
  • 24.3 속성이 부울이면 isVal() 또는 hasVal()을 사용하십시오.
// bad
if (!dragon.age()) {
    return false;
}

// good
if (!dragon.hasAge()) {
    return false;
}
  • 24.4 get() 및 set() 함수를 생성해도 괜찮지만 일관성을 유지하십시오.
class Jedi {
    constructor(options = {}) {
    const lightsaber = options.lightsaber || 'blue';
    this.set('lightsaber', lightsaber);
    }

    set(key, val) {
    this[key] = val;
    }

    get(key) {
    return this[key];
    }
}

이벤트 (Events)

  • 25.1 이벤트에 데이터 페이로드를 첨부할 때(DOM 이벤트이든 Backbone 이벤트와 같은 고유 이벤트이든) 원시 값 대신 해시(객체)를 전달하십시오. 이렇게 하면 나중에 기여자가 이벤트의 모든 핸들러를 찾아 업데이트할 필요 없이 이벤트 페이로드에 데이터를 추가할 수 있습니다. 예를 들어 다음과 같은 대신:
// 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
});

jQuery

// bad
const sidebar = $('.sidebar');

// good
const $sidebar = $('.sidebar');

// good
const $sidebarBtn = $('.sidebar-btn');
  • 26.2 jQuery 조회를 캐시하십시오.
// bad
function setSidebar() {
    $('.sidebar').hide();

    // ...stuff...

    $('.sidebar').css({
    'background-color': 'pink'
    });
}

// good
function setSidebar() {
    const $sidebar = $('.sidebar');
    $sidebar.hide();

    // ...stuff...

    $sidebar.css({
    'background-color': 'pink'
    });
}
  • 26.3 DOM 쿼리의 경우 계단식 $('.sidebar ul') 또는 부모 > 자식 $('.sidebar > ul')을 사용하십시오. jsPerf
  • 26.4 범위가 지정된 jQuery 객체 쿼리에 find를 사용하십시오.
// bad
$('ul', '.sidebar').hide();

// bad
$('.sidebar').find('ul').hide();

// good
$('.sidebar ul').hide();

// good
$('.sidebar > ul').hide();

// good
$sidebar.find('ul').hide();

ECMAscript 5 호환성 (Compatibility)

ECMAScript 6 스타일 (Styles)

  • 28.1 다양한 ES6 기능에 대한 링크입니다.
  1. 화살표 함수
  2. 클래스
  3. 객체 단축 표기
  4. 객체 간결 표기
  5. 계산된 객체 속성
  6. 템플릿 문자열
  7. 구조 분해
  8. 기본 매개변수
  9. Rest
  10. 배열 전개
  11. Let과 Const
  12. 이터레이터와 제너레이터
  13. 모듈

표준 라이브러리 (Standard Library)

표준 라이브러리에는 기능이 손상되었지만 레거시 이유로 남아 있는 일부 기능 유틸리티가 포함되어 있습니다.

  • 29.1 isNaN 대신 Number.isNaN을 사용하십시오.

왜요? isNaN은 비숫자를 숫자로 강제 변환하고 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
  • 29.2 isFinite 대신 Number.isFinite를 사용하십시오.

왜요? isFinite는 비숫자를 숫자로 강제 변환하고 유한수로 강제 변환되는 모든 것에 대해 true를 반환합니다. 이 동작이 필요한 경우 명시적으로 수행하십시오.

// bad
isFinite('2e3'); // true

// good
Number.isFinite('2e3'); // false
Number.isFinite(parseInt('2e3', 10)); // true

테스트 (Testing)

function foo() {
    return true;
}
  • 30.2 농담은 제쳐두고:
  • 어떤 테스트 프레임워크를 사용하든 테스트를 작성해야 합니다!
  • 작고 순수한 함수를 많이 작성하고 변경(mutation)이 발생하는 위치를 최소화하도록 노력하십시오.
  • 스텁과 모의 객체(mocks)에 주의하십시오 - 테스트를 취약하게 만들 수 있습니다.
  • Airbnb는 주로 mocha를 사용합니다. tape도 작고 고립된 모듈에 가끔 사용됩니다.
  • 100% 테스트 커버리지는 좋은 목표이지만 항상 현실적인 것은 아닙니다.
  • 버그를 수정할 때마다 회귀 테스트를 작성하십시오. 회귀 테스트 없이 수정된 버그는 나중에 다시 발생할 가능성이 큽니다.

성능 (Performance)

리소스 (Resources)

Learning ES6

Read This

Tools

  • Code Style Linters

Other Style Guides

Other Styles

Further Reading

Books

Blogs

Podcasts

사용 중인 곳 (In the Wild)

다음은 이 스타일 가이드를 사용하는 조직 목록입니다. 당신의 조직에서도 사용하는 경우, 풀 리퀘스트를 보내 추가해 주십시오.

번역 (Translation)

이 스타일 가이드는 다른 언어로도 제공됩니다:

JavaScript 스타일 가이드 가이드 (The JavaScript Style Guide Guide)

JavaScript에 대해 채팅하기 (Chat with us about JavaScript)

  • gitter에서 우리를 찾으십시오.

기여자 (Contributors)

License

(The MIT License)

Copyright (c) 2014-2016 Airbnb

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the ‘Software’), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED ‘AS IS’, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Amendments

이 가이드를 포크하고 팀의 스타일 가이드에 맞게 규칙을 변경하는 것을 권장합니다. 아래에 스타일 가이드에 대한 몇 가지 수정 사항을 나열할 수 있습니다. 이를 통해 병합 충돌을 처리할 필요 없이 스타일 가이드를 주기적으로 업데이트할 수 있습니다.

Reference

All rights reserved,未經允許不得隨意轉載
Hugo로 만듦
JimmyStack 테마 사용 중