# PC-COMMUNICATION JavaScript Style Guide PC-COMMUNICATION 자바스크립트 스타일 가이드는 [Airbnb 자바스크립트 스타일 가이드](https://github.com/airbnb/javascript) 및 [Naver 자바스크립트 스타일 가이드](https://github.com/naver/eslint-config-naver/blob/master/STYLE_GUIDE.md) 를 기준으로 작성되었습니다. ## Table of Contents 1. [Types](#types) 1. [References](#references) 1. [Objects](#objects) 1. [Arrays](#arrays) 1. [Destructuring](#destructuring) 1. [Strings](#strings) 1. [Functions](#functions) 1. [Arrow Functions](#arrow-functions) 1. [Classes & Constructors](#classes--constructors) 1. [Modules](#modules) 1. [Iterators and Generators](#iterators-and-generators) 1. [Properties](#properties) 1. [Variables](#variables) 1. [Hoisting](#hoisting) 1. [Comparison Operators & Equality](#comparison-operators--equality) 1. [Blocks](#blocks) 1. [Comments](#comments) 1. [Whitespace](#whitespace) 1. [Commas](#commas) 1. [Semicolons](#semicolons) 1. [Type Casting & Coercion](#type-casting--coercion) 1. [Naming Conventions](#naming-conventions) 1. [Accessors](#accessors) 1. [Events](#events) 1. [jQuery](#jquery) 1. [Resources](#resources) ## Types > [Airbnb](https://github.com/airbnb/javascript/blob/master/README.md#types) 와 동일합니다. - [1.1](#1.1) **Primitives**: primitive type은 그 값을 직접 조작한다. + `string` + `number` + `boolean` + `null` + `undefined` ```javascript const foo = 1; let bar = foo; bar = 9; console.log(foo, bar); // => 1, 9 ``` - [1.2](#1.2) **Complex**: 참조형(Complex)은 참조를 통해 값을 조작한다. + `object` + `array` + `function` ```javascript const foo = [1, 2]; const bar = foo; bar[0] = 9; console.log(foo[0], bar[0]); // => 9, 9 ``` **[⬆ back to top](#table-of-contents)** ## References > [Airbnb](https://github.com/airbnb/javascript/blob/master/README.md#references) 와 동일합니다. - [2.1](#2.1) 변수 선언은 가급적 `const` 를 사용하고, `var` 를 사용하지 않는다. eslint: [`prefer-const`](http://eslint.org/docs/rules/prefer-const.html), [`no-const-assign`](http://eslint.org/docs/rules/no-const-assign.html) ```javascript // bad var a = 1; var b = 2; // good const a = 1; const b = 2; ``` - [2.2](#2.2) 참조를 재할당 해야한다면 `var` 대신 `let` 을 사용한다. eslint: [`no-var`](http://eslint.org/docs/rules/no-var.html) jscs: [`disallowVar`](http://jscs.info/rule/disallowVar) ```javascript // bad var count = 1; if (true) { count += 1; } // good, use the let. let count = 1; if (true) { count += 1; } ``` - [2.3](#2.3) `let` 과 `const` 는 선언된 블록 안에서만 존재하는 블록 스코프이다. ```javascript // const 와 let 은 선언된 블록의 안에서만 존재한다. { let a = 1; const b = 1; } console.log(a); // ReferenceError console.log(b); // ReferenceError ``` **[⬆ back to top](#table-of-contents)** ## Objects > `3.6` 항목을 제외하고는 [Airbnb](https://github.com/airbnb/javascript/blob/master/README.md#objects) 와 동일합니다. > (`3.6` 항목은 [Naver](https://github.com/naver/eslint-config-naver/blob/master/STYLE_GUIDE.md#objects) 와 동일합니다.) - [3.1](#3.1) 오브젝트를 작성할 때는, 리터럴 구문을 사용한다. eslint: [`no-new-object`](http://eslint.org/docs/rules/no-new-object.html) ```javascript // bad const item = new Object(); // good const item = {}; ``` - [3.2](#3.2) 동적 프로퍼티명을 갖는 오브젝트를 작성할 때, 계산된 프로퍼티명(computed property names)을 이용한다. ```javascript function getKey(k) { return `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 }; ``` - [3.3](#3.3) 메서드의 단축 구문을 이용한다. eslint: [`object-shorthand`](http://eslint.org/docs/rules/object-shorthand.html) ```javascript // bad const atom = { value: 1, addValue: function (value) { return atom.value + value; }, }; // good const atom = { value: 1, addValue(value) { return atom.value + value; }, }; ``` - [3.4](#3.4) 속성의 단축구문을 이용한다. eslint: [`object-shorthand`](http://eslint.org/docs/rules/object-shorthand.html) ```javascript const lukeSkywalker = "Luke Skywalker"; // bad const obj = { lukeSkywalker: lukeSkywalker, }; // good const obj = { lukeSkywalker, }; ``` - [3.5](#3.5) 속성의 단축 구문은 오브젝트 선언의 시작 부분에 그룹화한다. ```javascript 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, }; ``` - [3.6](#3.6) 속성명이 키워드(default, if, ...)일 경우에는 꼭 따옴표를 붙인다. 또한, `-`문자와 같이 속성명으로 지정할수 없는 경우에는 꼭 따옴표를 붙인다. eslint: [`quote-props`](http://eslint.org/docs/rules/quote-props.html) ```js // bad const bad = { foo: 3, default: 20, data-blah: 5 }; // good const good = { foo: 3, "default": 20, "data-blah": 5, 10: "naver" }; ``` - [3.7](#3.7) `hasOwnProperty`, `propertyIsEnumerable`, `isPrototypeOf` 와 같은 `Object.prototype` 메서드를 직접적으로 사용하지 않는다. eslint: [`no-prototype-builtins`](http://eslint.org/docs/rules/no-prototype-builtins.html) ```javascript // bad console.log(object.hasOwnProperty(key)); // good console.log(Object.prototype.hasOwnProperty.call(object, key)); // best const has = Object.prototype.hasOwnProperty; // 모듈스코프에서 한 번 캐시하세요. /* or */ import has from "has"; // ... console.log(has.call(object, key)); ``` - [3.8](#3.8) 얕은 복사(shallow-copy)를 하기 위해서 [`Object.assign`](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Object/assign) 보다는 spread 연산자(...)를 선호한다. 오브젝트에서 몇몇 속성을 제거한 새로운 오브젝트를 얻고자 할때는 rest 파라미터(...)를 사용한다. ```javascript // very bad const original = { a: 1, b: 2 }; const copy = Object.assign(original, { c: 3 }); // this mutates `original` delete copy.a; // so does this // bad const original = { a: 1, b: 2 }; const copy = Object.assign({}, original, { c: 3 }); // copy => { a: 1, b: 2, c: 3 } // good const original = { a: 1, b: 2 }; const copy = { ...original, c: 3 }; // copy => { a: 1, b: 2, c: 3 } const { a, ...noA } = copy; // noA => { b: 2, c: 3 } ``` **[⬆ back to top](#table-of-contents)** ## Arrays > `4.7` 항목을 제외하고는 [Airbnb](https://github.com/airbnb/javascript/blob/master/README.md#array) 와 동일합니다. - [4.1](#4.1) 배열을 작성 할 때는 리터럴 구문을 사용한다. eslint: [`no-array-constructor`](http://eslint.org/docs/rules/no-array-constructor.html) ```javascript // bad const items = new Array(); // good const items = []; ``` - [4.2](#4.2) 아이템을 배열에 추가하는 경우, 직접 배열에 항목을 대입하지 말고 [Array#push](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/push) 를 이용한다. ```javascript const someStack = []; // bad someStack[someStack.length] = "abracadabra"; // good someStack.push("abracadabra"); ``` - [4.3](#4.3) 배열을 복사할 때는 배열의 spread 연산자(`...`) 를 이용한다. ```javascript // 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](#4.4) 순회 가능한 객체(iterable object)를 배열로 변환할 때는 Array.from 대신 spread 연산자(`...`) 를 사용하세요. [Naver](https://github.com/naver/eslint-config-naver/blob/master/STYLE_GUIDE.md#4.4) 에서는 `array-like 오브젝트를 배열로 변환하는 경우`에 대해서 별도로 가이드 하지 않는다. ```javascript const foo = document.querySelectorAll('.foo'); // good const nodes = Array.from(foo); // best const nodes = [...foo]; ``` - [4.5](#4.5) array-like 객체를 배열로 변환할 때는 Array.from을 사용하세요. [Naver](https://github.com/naver/eslint-config-naver/blob/master/STYLE_GUIDE.md#Arrays) 에서는 `Array.from`에 대해 별도로 가이드 하지 않는다. ```javascript const arrLike = { 0: 'foo', 1: 'bar', 2: 'baz', length: 3 }; // bad const arr = Array.prototype.slice.call(arrLike); // good const arr = Array.from(arrLike); ``` - [4.6](#4.6) 매핑할 때는 전개 구문 `...` 대신 `Array.from`을 사용하세요. 중간 배열 생성을 방지하기 때문입니다. [Naver](https://github.com/naver/eslint-config-naver/blob/master/STYLE_GUIDE.md#Arrays) 에서는 `Array.from`에 대해 별도로 가이드 하지 않는다. ```javascript // bad const baz = [...foo].map(bar); // good const baz = Array.from(foo, bar); ``` - [4.7](#4.7) [Airbnb](https://github.com/airbnb/javascript/blob/master/README.md#arrays--callback-return) 와 다르게 `배열의 메서드 콜백`에 대해서는 별도로 가이드 하지 않는다. eslint: [`array-callback-return`](http://eslint.org/docs/rules/array-callback-return.html) - [4.8](#4.8) 배열이 멀티 라인인 경우, 배열의 시작(before)과 끝(after)은 개행한다. ```javascript // 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, ]; ``` **[⬆ back to top](#table-of-contents)** ## Destructuring > [Airbnb](https://github.com/airbnb/javascript/blob/master/README.md#Destructuring) 와 동일합니다. > [Naver](https://github.com/naver/eslint-config-naver/blob/master/STYLE_GUIDE.md#destructuring) 에선 비구조화에 대해 별도의 가이드를 제공하지 않습니다. - [5.1](#destructuring--object) 하나의 객체에서 여러 속성에 접근할 때는 객체 비구조화를 사용하세요. eslint: [`prefer-destructuring`](https://eslint.org/docs/rules/prefer-destructuring) > 왜? 비구조화는 속성들을 위한 임시 참조를 만들지 않도록 해주고, 객체의 반복적인 접근을 방지합니다. 반복적인 객체 접근은 중복 코드와 실수를 만들어내고, 더 많은 코드를 읽게 합니다. 또한 객체 비구조화는 블록에서 사용되는 객체의 구조를 정의하는 단일한 위치를 제공함으로써 어떤 것이 사용되는지 알아내기 위해 모든 블록을 읽지 않아도 되도록 해줍니다. ```javascript // 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}`; } ``` - [5.2](#destructuring--array) 배열 비구조화를 사용하세요. eslint: [`prefer-destructuring`](https://eslint.org/docs/rules/prefer-destructuring) ```javascript const arr = [1, 2, 3, 4]; // bad const first = arr[0]; const second = arr[1]; // good const [first, second] = arr; ``` - [5.3](#destructuring--object-over-array) 여러 값을 반환하는 경우 배열 비구조화가 아닌 객체 비구조화를 사용하세요. > 왜? 이렇게 하면 이후 호출처에 영향을 주지 않고 새로운 속성을 추가하거나 순서를 변경할 수 있습니다. ```javascript // bad function processInput(input) { // 기적이 일어납니다 return [left, right, top, bottom]; } // 반환되는 데이터의 순서를 생각해야합다 const [left, __, top] = processInput(input); // good function processInput(input) { // 기적이 일어납니다 return { left, right, top, bottom }; } // 필요한 데이터만 선택하면 됩니다 const { left, top } = processInput(input); ``` **[⬆ back to top](#table-of-contents)** ## Strings > [Airbnb](https://github.com/airbnb/javascript/blob/master/README.md#strings) 와 동일합니다. > `6.1` 항목은 [Naver](https://github.com/naver/eslint-config-naver/blob/master/STYLE_GUIDE.md#quotes) 에선 쌍따옴표(`""`)를 권장합니다. > `6.2` 항목은 [Naver](https://github.com/airbnb/javascript/blob/master/README.md#6.2) 에선 `+` 연산자를 권장합니다. - [6.1](#6.1) 문자열에는 작은 따옴표 `''`를 사용하세요. eslint: [`quotes`](https://eslint.org/docs/rules/quotes.html) ```javascript // bad const name = "Capt. Janeway"; // bad - template literals should contain interpolation or newlines const name = `Capt. Janeway`; // good const name = 'Capt. Janeway'; ``` - [6.2](#6.2) 100자가 넘는 문자열을 문자열 연결을 이용해 여러 줄에 걸쳐 쓰지 마세요. > 왜? 문자열이 끊어지면 작업하기 어렵고, 코드를 찾기 어렵게 됩니다. ```javascript // 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.'; ``` - [6.3](#6.3) 프로그램에서 문자열을 생성하는 경우는 문자열 연결이 아닌 template strings를 이용한다. eslint: [`prefer-template`](http://eslint.org/docs/rules/prefer-template.html) [`template-curly-spacing`](http://eslint.org/docs/rules/template-curly-spacing) ```javascript // bad function sayHi(name) { return "How are you, " + name + "?"; } // bad function sayHi(name) { return ["How are you, ", name, "?"].join(); } // good function sayHi(name) { return `How are you, ${name}?`; } ``` - [6.4](#6.4) 절대로 `eval()` 을 사용하지 않는다. - [6.5](#6.5) 문자열에 불필요한 escape 문자를 사용하지 않는다. eslint: [`no-useless-escape`](http://eslint.org/docs/rules/no-useless-escape) ```javascript // bad const foo = "\'this\' \i\s \'quoted\'"; // good const foo = "\"this\" is 'quoted'"; const foo = `my name is '${name}'`; ``` **[⬆ back to top](#table-of-contents)** ## Functions > `7.1 ~ 2`, `7.11 ~ 14` 항목을 제외하고는 [Airbnb](https://github.com/airbnb/javascript/blob/master/README.md#functions) 와 동일합니다. - [7.1](#7.1) 함수 표현식 대신 함수 선언문을 사용한다. eslint: [`func-style`](http://eslint.org/docs/rules/func-style) [Airbnb](https://github.com/airbnb/javascript/blob/master/README.md#func-style) 에선 함수 표현식 권장. [Naver](https://github.com/naver/eslint-config-naver/blob/master/STYLE_GUIDE.md#func-style) 에선 별도의 스타일 가이드 제공하지 않음. > 왜? 함수 스타일에 대해서는 별도의 스타일 가이드를 제공하지 않는다. > 1. 함수선언문은 코드를 구현한 위치와 관계없이 자바스크립트의 특징인 호이스팅에 따라 브라우저가 자바스크립트를 해석할 때 맨 위로 끌어 올려진다. 함수표현식은 함수선언문과 달리 선언과 호출 순서에 따라서 정상적으로 함수가 실행되지 않을 수 있다. > 1. 함수 표현식에 Typescript (React.FC) 사용 시, Default Props 정상동작하지 않음 **(React 공식문서도 선언문 이용)** ```javascript // bad const foo = function() { // ... }; // bad // foo 변수만 호이스팅 됨. const foo = function bar() { // ... }; // good function foo() { // ... } ``` - [7.2](#7.2) 즉시 실행함수는 함수를 괄호로 감싼다. eslint: [`wrap-iife`](http://eslint.org/docs/rules/wrap-iife.html) ```javascript // bad !function()) { console.log("Welcome to the Internet. Please follow me."); }(); // bad (function() { console.log("Welcome to the Internet. Please follow me."); }()); // good (function() { console.log("Welcome to the Internet. Please follow me."); })(); ``` - [7.3](#7.3) 함수 이외의 블록 (if나 while같은) 안에서 함수를 선언하지 않는다. eslint: [`no-loop-func`](http://eslint.org/docs/rules/no-loop-func.html) ```js // bad let i; for (i = 10; i; i--) { (function() { return i; })(); } // bad while(i) { let a = function() { return i; }; a(); } // good const a = function() {}; let i; for (i = 10; i; i--) { a(); } ``` - [7.4](#7.4) [Airbnb](https://github.com/airbnb/javascript/blob/master/README.md#functions--note-on-blocks) 와 다르게 `block 내의 함수선언`에 대해서는 별도로 가이드 하지 않는다. - [7.5](#7.5) 절대로 파라미터에 `arguments` 를 지정하지 않는다. 이것은 함수 스코프에 전해지는 `arguments` 오브젝트의 참조를 덮어 버린다. ```javascript // bad function nope(name, options, arguments) { // ...stuff... } // good function yup(name, options, args) { // ...stuff... } ``` - [7.6](#7.6) 절대 `arguments` 를 이용하지 않는다. 대신에 rest 파라미터(`...`) 를 이용한다. eslint: [`prefer-rest-params`](http://eslint.org/docs/rules/prefer-rest-params.html) ```javascript // bad function concatenateAll() { const args = Array.prototype.slice.call(arguments); return args.join(""); } // good function concatenateAll(...args) { return args.join(""); } ``` - [7.7](#7.7) 함수의 파라미터를 재정의하지 않는다. 대신 default 파라미터를 이용한다. ```javascript // really bad function handleThings(opts) { opts = opts || {}; // ... } // still bad function handleThings(opts) { if (opts === void 0) { opts = {}; } // ... } // good function handleThings(opts = {}) { // ... } ``` - [7.8](#7.8) side effect 를 유발하는 default 파라미터의 이용을 피한다. ```javascript var b = 1; // bad function count(a = b++) { console.log(a); } count(); // 1 count(); // 2 count(3); // 3 count(); // 3 ``` - [7.9](#7.9) 항상 default 파라미터는 뒤쪽에 둔다. ```javascript // bad function handleThings(opts = {}, name) { // ... } // good function handleThings(name, opts = {}) { // ... } ``` - [7.10](#7.10) 절대 새 함수를 작성하기 위해 Function constructor를 이용하지 않는다. eslint: [`no-new-func`](http://eslint.org/docs/rules/no-new-func) ```javascript // bad var add = new Function("a", "b", "return a + b"); // still bad var subtract = Function("a", "b", "return a - b"); ``` - [7.11](#7.11) 익명함수는 function과 괄호 사이에 공백이 없다. 기명 함수(named function)는 함수 이름과 괄호 사이에 공백이 없다. async arrow function인 경우 async와 arrow function 사이에 공백이 있다. eslint: [`space-before-function-paren`](http://eslint.org/docs/rules/space-before-function-paren) [`space-before-blocks`](http://eslint.org/docs/rules/space-before-blocks) ```javascript // bad const f = function () {}; const g = function a (){}; const h = async(v,i) => {}; // good const x = function() {}; const y = function a() {}; const z = async (v,i) => {}; ``` - [7.12](#7.12) 가급적 mutate parameter는 사용하지 않는다. 하지만, 필요에 의해서는 주의하여 사용한다. eslint: [`no-param-reassign`](http://eslint.org/docs/rules/no-param-reassign.html) ```javascript // 권장하지 않음. function f1(obj) { obj.key = 1; } ``` - [7.13](#7.13) 파라미터를 재할당하지 않는다. 단, 파라미터의 속성에 대해서는 재할당이 가능하다. eslint: [`no-param-reassign`](http://eslint.org/docs/rules/no-param-reassign.html) ```javascript // bad function f1(a) { a = 1; // ... } // bad function f2(a) { if (!a) { a = 1; } // ... } // good function f3(a) { const b = a || 1; // ... } // good function f4(a) { if (!a) { a.b = 1; } // ... } ``` - [7.14](#7.14) 가변함수를 호출할 때는 spread 연산자 (`...`)를 사용한다. eslint: [`prefer-spread`](http://eslint.org/docs/rules/prefer-spread) ```javascript // 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]); ``` - [7.15](#7.15) 함수의 정의가 멀티라인 인 경우, 오브젝트와 같은 스타일 가이드를 따른다. [19.1](#19.1)을 참조한다. ```javascript // bad function foo(bar, baz, quux) { // ... } // good function foo( bar, baz, quux, ) { // ... } // bad console.log(foo, bar, baz); // good console.log( foo, bar, baz, ); ``` **[⬆ back to top](#table-of-contents)** ## Arrow Functions > `8.2`, `8.4` 항목을 제외하고는 [Airbnb](https://github.com/airbnb/javascript/blob/master/README.md#arrow-functions) 와 동일합니다. - [8.1](#8.1) 익명함수를 전달하는 경우, arrow function 표기를 이용한다. eslint: [`prefer-arrow-callback`](http://eslint.org/docs/rules/prefer-arrow-callback.html), [`arrow-spacing`](http://eslint.org/docs/rules/arrow-spacing.html) ```javascript // 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; }); ``` - [8.2](#8.2) 함수의 몸체(body)가 단일 표현식이라면 중괄호(`{}`)를 생략하고, 묵시적으로 그 값은 반환값이 된다. 그렇지 않으면, 중괄호(`{}`)는 생략할 수 없고, 반환값이 필요한 경우는 return 을 명시한다. eslint: [`arrow-parens`](http://eslint.org/docs/rules/arrow-parens.html), [`arrow-body-style`](http://eslint.org/docs/rules/arrow-body-style.html) ```javascript // bad [1, 2, 3].map(number => { const nextNumber = number + 1; `A string containing the ${nextNumber}.`; }); // good [1, 2, 3].map(number => `A string containing the ${number}.`); // good [1, 2, 3].map(number => { const nextNumber = number + 1; return `A string containing the ${nextNumber}.`; }); // good [1, 2, 3].map((number, index) => ({ [index]: number, })); // good [1, 2, 3].forEach(number => { console.log(nextNumber); }); ``` - [8.3](#8.3) 식이 복수행에 걸쳐 있을 경우는 가독성을 더욱 좋게하기 위해 소괄호(`()`)로 감싼다. ```javascript // bad ['get', 'post', 'put'].map(httpMethod => Object.prototype.hasOwnProperty.call( httpMagicObjectWithAVeryLongName, httpMethod, ) ); // good ['get', 'post', 'put'].map(httpMethod => ( Object.prototype.hasOwnProperty.call( httpMagicObjectWithAVeryLongName, httpMethod, ) )); ``` - [8.4](#8.4) 함수가 단일 파라미터인 경우, 소괄호(`()`)는 생략한다. eslint: [`arrow-parens`](http://eslint.org/docs/rules/arrow-parens.html) ```javascript // bad [1, 2, 3].map((x) => x * x); // good [1, 2, 3].map(x => x * x); // good [1, 2, 3].map(number => ( `A long string with the ${number}. It’s so long that we don’t want it to take up space on the .map line!` )); // bad [1, 2, 3].map((x) => { const y = x + 1; return x * y; }); // good [1, 2, 3].map(x => { const y = x + 1; return x * y; }); ``` - [8.5](#8.5) arrow function 문법(`=>`)과 비교 연산자 (`<=`, `>=`)를 함께 사용할 경우, 소괄호(`()`)를 이용하여 혼란스럽지 않도록 표현한다. eslint: [`no-confusing-arrow`](http://eslint.org/docs/rules/no-confusing-arrow) ```javascript // 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; }; ``` **[⬆ back to top](#table-of-contents)** ## Classes & Constructors > [Airbnb](https://github.com/airbnb/javascript/blob/master/README.md#classes--constructors) 와 동일합니다. - [9.1](#9.1) `prototype`을 직접 조작하는 것을 피하고 항상 `class`를 이용한다. ```javascript // 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](#9.2) 상속은 `extends` 를 이용한다. ```javascript // 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](#9.3) 필요하다면, 메서드의 반환값으로 `this` 를 반환하는 것으로 메서드체이닝을 할 수 있다. ```javascript // 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](#9.4) toString()을 작성하는 것을 허용하지만 올바르게 동작하는 지와 side effect 가 없는지를 꼭 확인한다. ```javascript class Jedi { constructor(options = {}) { this.name = options.name || "no name"; } getName() { return this.name; } toString() { return `Jedi - ${this.getName()}`; } } ``` - [9.5](#9.5) 불필요한 빈 default constructor는 사용하지 않는다. eslint: [`no-useless-constructor`](http://eslint.org/docs/rules/no-useless-constructor) ```javascript // 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'; } } ``` - [9.6](#9.6) 클래스에는 동일 멤버가 있어서는 안된다. eslint: [`no-dupe-class-members`](http://eslint.org/docs/rules/no-dupe-class-members) ```javascript // bad class Foo { bar() { return 1; } bar() { return 2; } } // good class Foo { bar() { return 1; } } // good class Foo { bar() { return 2; } } ``` **[⬆ back to top](#table-of-contents)** ## Modules > `10.5`, `10.9` 항목을 제외하고는 [Airbnb](https://github.com/airbnb/javascript/blob/master/README.md#modules) 와 동일합니다. - [10.1](#10.1) 비표준 모듈시스템이 아닌 (`import`/`export`) 를 항상 사용한다. ```javascript // 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](#10.2) wildcard import 는 이용하지 않는다. ```javascript // bad import * as AirbnbStyleGuide from "./AirbnbStyleGuide"; // good import AirbnbStyleGuide from "./AirbnbStyleGuide"; ``` - [10.3](#10.3) import 문으로부터 직접 export 하지 않는다. > 왜? 한줄이 간결하기는 하지만, 명확한 import와 명확한 export를 통해 일관성을 가질 수 있기 때문입니다. ```javascript // bad // filename es6.js export {es6 as default} from "./airbnbStyleGuide"; // good // filename es6.js import {es6} from "./AirbnbStyleGuide"; export default es6; ``` - [10.4](#10.4) import는 중복되지 않게 한 곳에서 import 한다. eslint: [`no-duplicate-imports`](http://eslint.org/docs/rules/no-duplicate-imports) ```javascript // 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"; ``` - [10.5](#10.5) mutable 객체를 export 하는 것에 대해 강제하지 않는다. eslint: [`import/no-mutable-exports`](https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-mutable-exports.md) ```javascript // good // let은 변경 가능한 객체 let foo = { a: 10 } export {foo}; // good // const는 변경 가능하지 않는 객체 const foo = 3; export {foo}; ``` - [10.6](#10.6) export가 하나일 경우, default export를 사용한다. eslint: [`import/prefer-default-export`](https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/prefer-default-export.md) ```javascript // bad export function foo() {} // good export default function foo() {} ``` - [10.7](#10.7) 모든 `import`문은 상위에 위치한다. eslint: [`import/first`](https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/first.md) ```javascript // bad import foo from "foo"; foo.init(); import bar from "bar"; // good import foo from "foo"; import bar from "bar"; foo.init(); ``` - [10.8](#10.8) 멀티 라인 imports 문은 배열이나 오브젝트의 literal과 같이 표현한다. [19.1](#19.1)을 참조한다. ```javascript // good import { longNameA, longNameB, longNameC, longNameD, longNameE, } from "path"; ``` - [10.9](#10.9) 웹팩 로더 문법 사용에 대해 강제하지 않는다. eslint: [`import/no-webpack-loader-syntax`](https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-webpack-loader-syntax.md) ```javascript // good 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'; ``` **[⬆ back to top](#table-of-contents)** ## Iterators and Generators > [Airbnb](https://github.com/airbnb/javascript/blob/master/README.md#iterators-and-generators) 와 다르게 별도의 가이드를 제공하지 않습니다. ## Properties > [Airbnb](https://github.com/airbnb/javascript/blob/master/README.md#properties) 와 동일합니다. - [12.1](#12.1) 프로퍼티에 억세스하는 경우는 점 `.` 을 사용한다. eslint: [`dot-notation`](http://eslint.org/docs/rules/dot-notation.html) ```javascript const luke = { jedi: true, age: 28, }; // bad const isJedi = luke["jedi"]; // good const isJedi = luke.jedi; ``` - [12.2](#12.2) 변수를 사용해 프로퍼티에 억세스하는 경우는 대괄호(`[]`)를 사용한다. ```javascript const luke = { jedi: true, age: 28, }; function getProp(prop) { return luke[prop]; } const isJedi = getProp("jedi"); ``` **[⬆ back to top](#table-of-contents)** ## Variables > `13.6` 항목을 제외하고는 [Airbnb](https://github.com/airbnb/javascript/blob/master/README.md#variables) 와 동일합니다. - [13.1](#13.1) 변수를 선언 할 때는 가급적 `const`나 `let`을 사용한다. 그렇게 하지 않으면 글로벌 변수로 선언된다. eslint: [`no-undef`](http://eslint.org/docs/rules/no-undef) [`prefer-const`](http://eslint.org/docs/rules/prefer-const) ```javascript // bad superPower = new SuperPower(); // good const superPower = new SuperPower(); ``` - [13.2](#13.2) 변수 선언은 변수당 하나씩 사용한다. eslint: [`one-var`](http://eslint.org/docs/rules/one-var.html) ```javascript // 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"; ``` - [13.3](#13.3) 우선 `const` 를 그룹화하고 다음에 `let` 을 그룹화 한다. ```javascript // 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](#13.4) `let`과 `const`을 사용할 때는 블럭 스코프 이기 때문에, 변수가 사용될 적당한 위치에 변수를 선언한다. [Naver](https://github.com/naver/eslint-config-naver/blob/master/STYLE_GUIDE.md#13.4) 에선 `let`과 `const`를 사용할 수 없다면, `var`로 상단에 변수를 선언한다. ```javascript // 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; } ``` - [13.5](#13.5) 여러 개의 변수를 한 줄에 동일한 값으로 설정하지 않는다. eslint [`no-multi-assign`](http://eslint.org/docs/rules/no-multi-assign) ```javascript // bad (function example() { let a = b = c = 1; }()); // good (function example() { let a = 1; let b = a; let c = a; }()); ``` - [13.6](#13.6) ++, -- 연산자 사용이 가능하다. 연산자와 피연산자 사이에 공백을 두지 않는다. eslint [`no-plusplus`](http://eslint.org/docs/rules/no-plusplus) ```javascript // bad ++ i; i ++; // good ++i; i++; ``` **[⬆ back to top](#table-of-contents)** ## Hoisting > [Airbnb](https://github.com/airbnb/javascript/blob/master/README.md#hoisting) 와 동일합니다. - [14.1](#14.1) `var` 선언은 할당 없이 스코프의 선두에 hoist 된다. `const` 와 `let` 선언은[Temporal Dead Zones (TDZ)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let#Temporal_dead_zone_and_errors_with_let) 라고 불리는 새로운 컨셉의 혜택을 받고 있다. ```javascript // notDefined 가 글로벌변수에 존재하지 않는다고 판정한 경우, 잘 동작하지 않는다. function example() { console.log(notDefined); // => throws a ReferenceError } // 그 변수를 참조하는 코드의 뒤에서 그 변수를 선언한 경우 // 변수가 hoist 된 상태에서 동작한다. // 주의:`true` 라는 값 자체는 hoist 되지 않는다. function example() { console.log(declaredButNotAssigned); // => undefined var declaredButNotAssigned = true; } // 인터프리터는 변수선언을 스코프의 선두에 hoist 한다. // 위의 예는 다음과 같이 다시 쓸수 있다. 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; } ``` - [14.2](#14.2) 익명 함수의 경우 함수가 할당되기 전의 변수가 hoist 된다. ```javascript function example() { console.log(anonymous); // => undefined anonymous(); // => TypeError anonymous is not a function var anonymous = function() { console.log('anonymous function expression'); }; } ``` - [14.3](#14.3) 기명 함수(named function)의 경우도 똑같이 변수가 hoist 된다. 함수명이나 함수본체는 hoist 되지 않는다. ```javascript 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"); } } ``` - [14.4](#14.4) 함수선언은 함수명과 함수본체가 hoist 된다. ```javascript function example() { superPower(); // => Flying function superPower() { console.log("Flying"); } } ``` **[⬆ back to top](#table-of-contents)** ## Comparison Operators & Equality > [Airbnb](https://github.com/airbnb/javascript/blob/master/README.md#comparison-operators--equality) 와 동일합니다. - [15.1](#15.1) `==` 이나 `!=` 보다 `===` 와 `!==` 을 사용한다. - [15.2](#15.2) `if` 문과 같은 조건식은 `ToBoolean` 메서드에 의한 강제형변환으로 평가되어 항상 다음과 같은 심플한 룰을 따른다. + **Object** 는 **true** 로 평가된다. + **undefined** 는 **false** 로 평가된다. + **null** 은 **false** 로 평가된다. + **Boolan** 은 **boolean형의 값** 으로 평가된다. + **Number** 는 **true** 로 평가된다. 하지만 **+0, -0, or NaN** 의 경우는 **false** 이다. + **String** 은 **true** 로 평가된다. 하지만 빈문자 `""` 의 경우는 **false** 이다. ```javascript if ([0]) { // true // 배열은 오브젝트이므로 true 로 평가된다. } ``` - [15.3](#15.3) Boolen에 대해서는 단축형을 사용한다. ```javascript // bad if (name !== "") { // ...stuff... } // good if (name) { // ...stuff... } // bad if (collection.length > 0) { // ...stuff... } // good if (collection.length) { // ...stuff... } ``` - [15.4](#15.4) 비교에 대한 자세한 정보는 [Truth Equality and JavaScript](https://javascriptweblog.wordpress.com/2011/02/07/truth-equality-and-javascript/#more-2108) by Angus Croll. 을 참조한다. - [15.5](#15.5) `case`, `default` 구문에서 `let`, `const`, `function`, `class`가 사용 되는 경우에는 중괄호(`{}`)를 사용한다. eslint rules: [`no-case-declarations`](http://eslint.org/docs/rules/no-case-declarations.html). ```javascript // 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.6](#15.6) 중첩 3항 연산자는 사용하지 않는다. eslint rules: [`no-nested-ternary`](http://eslint.org/docs/rules/no-nested-ternary.html). ```javascript // 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.7](#15.7) 불필요한 3항 연산자는 사용하지 않는다. eslint rules: [`no-unneeded-ternary`](http://eslint.org/docs/rules/no-unneeded-ternary.html). ```javascript // 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; ``` **[⬆ back to top](#table-of-contents)** ## Blocks > [Naver](https://github.com/naver/eslint-config-naver/blob/master/STYLE_GUIDE.md#blocks) 와 동일합니다. - [16.1](#16.1) 중괄호(`{}`)는 클래스, 메서드, 제어문의 블럭을 구분한다. 중괄호는 클래스 선언, 메서드 선언, 조건/반복문/제어문,줄의 마지막에서 시작한다. ```javascript // bad const Empty = function() { } // good const Empty = function() { } switch (type) { case 0: break; case 1: { break; } default: common(); } if (true) { return; } else if (false) { return; } else { } ``` - [16.2](#16.2) 조건/반복문/제어문에 중괄호 사용한다. 조건/반복문/제어문이 한줄로 끝이라도 중괄호를 활용한다. eslint: [`brace-style`](http://eslint.org/docs/rules/brace-style.html) ```javascript // bad if (exp == null) return false; for (var i in obj) if ( i === "key" ) return obj[i]; // good if (exp == null) { return false; } for (var i in obj) { if ( i === "stop" ) { return obj[i]; } } ``` **[⬆ back to top](#table-of-contents)** ## Comments > `17.1`, `17.2` 항목을 제외하고 [Airbnb](https://github.com/airbnb/javascript/blob/master/README.md#comments) 와 동일합니다. - [17.1](#17.1) 복수행의 코멘트는 `/** ... */` 을 사용한다. 함수일 경우 설명과 모든 파라미터, 반환값에 대해 형이나 값을 필수로 기술한다. ```javascript // 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; } ``` - [17.2](#17.2) 단일행 코멘트에는 `//` 을 사용한다. 코멘트를 추가하고 싶은 코드의 상부에 배치한다. 또한, 코멘트의 앞에 공백을 넣고 구문의 앞에 빈행을 남겨둔다. ```javascript // 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; } ``` - [17.3](#17.3) 문제를 지적하고 재고를 촉구하는 경우나 문제의 해결책을 제안하는 경우 등, 코멘트의 앞에 `FIXME` 나 `TODO` 를 붙이는 것으로 다른 개발자가 빠르게 이해할 수 있도록 한다. 이런 것들은 어떤 액션을 따른다는 의미이다. 액션이라는 것은 `FIXME -- 해결이 필요` 또는 `TODO -- 구현이 필요` 를 뜻한다. - [17.4](#17.4) 문제의 해결이 필요하다는 주석으로써 `// FIXME:` 를 사용한다. ```javascript class Calculator extends Abacus { constructor() { super(); // FIXME: total은 글로벌 변수를 사용해야한다. total = 0; } } ``` - [17.5](#17.5) 구현이 필요하다는 주석으로 `// TODO:` 를 사용한다. ```javascript class Calculator extends Abacus { constructor() { super(); // TODO: total 은 옵션 파라미터로 설정해야한다. this.total = 0; } } ``` **[⬆ back to top](#table-of-contents)** ## Whitespace `18. 11 ~ 13` 항목을 제외하고는 [Airbnb](https://github.com/airbnb/javascript/blob/master/README.md#whitespace) 와 동일합니다. - [18.1](#18.1) 탭은 공백문자 2개로 설정한다. eslint: [`indent`](http://eslint.org/docs/rules/indent.html) [Naver](https://github.com/naver/eslint-config-naver/blob/master/STYLE_GUIDE.md#18.1) 에선 다른 방식으로 사용한다. ```javascript // bad function foo() { ∙∙∙∙let name; } // bad function bar() { ∙let name; } // good function baz() { ∙∙let name; } ``` - [18.2](#18.2) 주요 중괄호 ({}) 앞에는 공백을 1개 넣는다. eslint: [`space-before-blocks`](http://eslint.org/docs/rules/space-before-blocks.html) ```javascript // 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", }); ``` - [18.3](#18.3) 제어구문 (`if` 문이나 `while` 문 등) 의 소괄호(`()`) 앞에는 공백을 1개 넣는다. 함수선언이나 함수호출시 인수리스트의 앞에는 공백을 넣지 않는다. eslint: [`keyword-spacing`](http://eslint.org/docs/rules/keyword-spacing.html) ```javascript // bad if(isJedi) { fight (); } // good if (isJedi) { fight(); } // bad function fight () { console.log ("Swooosh!"); } // good function fight() { console.log("Swooosh!"); } ``` - [18.4](#18.4) 연산자 사이에는 공백을 넣는다. eslint: [`space-infix-ops`](http://eslint.org/docs/rules/space-infix-ops.html) ```javascript // bad const x=y+5; // good const x = y + 5; ``` - [18.5](#18.5) 파일 끝에는 개행문자를 1개 넣는다. eslint: [`eol-last`](https://github.com/eslint/eslint/blob/master/docs/rules/eol-last.md) ```javascript // bad (function(global) { // ...stuff... })(this); ``` ```javascript // bad (function(global) { // ...stuff... })(this);↵ ↵ ``` ```javascript // good (function(global) { // ...stuff... })(this);↵ ``` - [18.6](#18.6) 메서드 체인이 2개를 초과한 경우, 적절히 줄 바꿈을 하여 사용한다. 또한 해당 줄이 새로운 구문이 아니라 메소드 호출임을 강조하는 마침표를 맨 앞에 둔다. 줄 바꿈 후에는 가독성을 위하여 자동 들여쓰기를 한다. eslint: [`newline-per-chained-call`](http://eslint.org/docs/rules/newline-per-chained-call) [`no-whitespace-before-property`](http://eslint.org/docs/rules/no-whitespace-before-property) ```javascript // 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); ``` - [18.7](#18.7) 구문의 앞과 블록의 뒤에는 빈행을 남겨둔다. ```javascript // 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; ``` - [18.8](#18.8) 블록에 빈행을 끼워 넣지 않는다. eslint: [`padded-blocks`](http://eslint.org/docs/rules/padded-blocks.html) ```javascript // 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); } ``` - [18.9](#18.9) 소괄호(`()`)의 안쪽에 공백을 추가하지 않는다. eslint: [`space-in-parens`](http://eslint.org/docs/rules/space-in-parens.html) ```javascript // bad function bar( foo ) { return foo; } // good function bar(foo) { return foo; } // bad if ( foo ) { console.log(foo); } // good if (foo) { console.log(foo); } ``` - [18.10](#18.10) 대괄호(`[]`)의 안쪽에 공백을 추가하지 않는다. eslint: [`array-bracket-spacing`](http://eslint.org/docs/rules/array-bracket-spacing.html) ```javascript // bad const foo = [ 1, 2, 3 ]; console.log(foo[ 0 ]); // good const foo = [1, 2, 3]; console.log(foo[0]); ``` - [18.11](#18.11) 괄호 안에 공백을 삽입하지 않는다. eslint: [`object-curly-spacing`](http://eslint.org/docs/rules/object-curly-spacing.html) ```javascript // bad var obj = { }; var obj = { "a": 2 }; var arr = [ ]; var arr = [ 1, 2 ]; function foo( a, b ) { } // good var obj = {}; var obj = {"a": 2}; var arr = []; var arr = [1, 2]; function foo(a, b) { } ``` - [18.12](#18.12) 최대 줄 너비는 `100` 이다. 고해상도 모니터(해상도 1280*1024)사용이 보편화 됨에 따라, 최대 줄 사용 너비는 100자까지 가능하다. eslint: [`max-len`](http://eslint.org/docs/rules/max-len.html) ```javascript // 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://www.naver.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; ``` - [18.13](#18.13) 연산식의 경우에는 연산자 후에 줄 바꿈을 한다. 상위 레벨의 깊이에 맞게 들여쓰기를 한다 ```javascript // bad const sum = 100 + 200 + 300 + 400 + 500 + 600 + 700 + 800; // good const sum = 100 + 200 + 300 + 400 + 500 + 600 + 700 + 800; ``` **[⬆ back to top](#table-of-contents)** ## Commas > [Airbnb](https://github.com/airbnb/javascript/blob/master/README.md#commas) 와 동일합니다. - [19.1](#19.1) 콤마는 뒤에 표기한다. eslint: [`comma-style`](http://eslint.org/docs/rules/comma-style.html) ```javascript // 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" }; ``` - [19.2](#19.2) 끝에 콤마를 넣는다. eslint: [`comma-dangle`](http://eslint.org/docs/rules/comma-dangle.html) ```javascript // 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", ]; ``` **[⬆ back to top](#table-of-contents)** ## Semicolons > [Airbnb](https://github.com/airbnb/javascript/blob/master/README.md#semicolons) 와 동일합니다. - [20.1](#20.1) `;`은 문장의 끝에 표기한다. eslint: [`semi`](http://eslint.org/docs/rules/semi.html) ```javascript // bad (function() { const name = "Skywalker" return name })() // good (() => { const name = "Skywalker"; return name; })(); // good ;(() => { const name = "Skywalker"; return name; })(); ``` **[⬆ back to top](#table-of-contents)** ## Type Casting & Coercion > [Airbnb](https://github.com/airbnb/javascript/blob/master/README.md#type-casting--coercion) 와 동일합니다. - [21.1](#21.1) 문의 선두에서 형의 강제를 수행한다. - [21.2](#21.2) 문자열의 경우: ```javascript // => this.reviewScore = 9; // bad const totalScore = this.reviewScore + ""; // good const totalScore = String(this.reviewScore); ``` - [21.3](#21.3) 수치의 경우: `Number` 로 형변환하는 경우는 `parseInt` 를 이용하고, 항상 형변환을 위한 기수를 인수로 넘겨 준다. eslint: [`radix`](http://eslint.org/docs/rules/radix) ```javascript 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); ``` - [21.4](#21.4) `parseInt`를 사용하는 경우 [성능적인 이유](http://jsperf.com/coercion-vs-casting/3)로 문제가 되면, Bitshift를 사용한다. 이때에는 꼭! Bitshift를 사용한 이유를 주석으로 남긴다. ```javascript // good /** * parseInt로 인해 느려졌음 * Bitshift를 통한 수치로의 문자열 강제 형변환으로 성능을 개선시킴. */ const val = inputValue >> 0; ``` - [21.5](#21.5) **주의:** Bitshift를 사용하는 경우의 주의사항. 수치는 [64비트 값](http://es5.github.io/#x4.3.19)으로 표현되어 있으나 bitshift 연산한 경우는 항상 32비트 integer 로 넘겨진다. ```javascript 2147483647 >> 0 //=> 2147483647 2147483648 >> 0 //=> -2147483648 2147483649 >> 0 //=> -2147483647 ``` - [21.6](#21.6) Boolean의 경우: ```javascript const age = 0; // bad const hasAge = new Boolean(age); // good const hasAge = Boolean(age); // good const hasAge = !!age; ``` **[⬆ back to top](#table-of-contents)** ## Naming Conventions > `22.2`, `22.4 ~ 5`, `22.10 ~ 15` 항목을 제외하고는 [Airbnb](https://github.com/airbnb/javascript/blob/master/README.md#naming-conventions) 와 동일합니다. - [22.1](#22.1) 1문자의 이름은 사용하지 않는다. eslint: [`id-length`](http://eslint.org/docs/rules/id-length) ```javascript // bad function q() { // ...stuff... } // good function query() { // ..stuff.. } ``` - [22.2](#22.2) 네임스페이스, 오브젝트, 함수 그리고 인스턴스에는 camelCase를 사용한다. eslint: [`camelcase`](http://eslint.org/docs/rules/camelcase.html) ```javascript // bad naver.FOO.bar = function() {}; const OBJEcttsssss = {}; const this_is_my_object = {}; function c() {} // good naver.foo.bar = function() {}; const thisIsMyObject = {}; function thisIsMyFunction() {} ``` - [22.3](#22.3) 클래스나 constructor에는 PascalCase 를 사용한다. eslint: [`new-cap`](http://eslint.org/docs/rules/new-cap.html) ```javascript // 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", }); ``` - [22.4](#22.4) 함수명이나 속성명의 앞에 `_`가 있는 경우는 private을 의미한다. 외부에서 접근할 수 있는 경우, 사용하면 안되는 메서드명 또는 속성명은 `_`을 사용한다. 외부에서 접근할 수 없다면, 메서드명 이나 속성명에 `_`을 사용하지 않는다. eslint: [`no-underscore-dangle`](http://eslint.org/docs/rules/no-underscore-dangle.html) ```javascript // bad - 외부에서 접근할 수 있는 경우, 사용하면 안되는 메서드명 또는 속성명은 `_`을 사용한다. class User({ constructor() { privateState: true } privateMethod() { } _publicMethod() { } } // good - 외부에서 접근할 수 있는 경우, 사용하면 안되는 메서드명 또는 속성명은 `_`을 사용한다. class User({ constructor() { _privateState: true } _privateMethod() { } publicMethod() { } } // bad - 외부에서 접근할 수 없다면, 메서드명 이나 속성명에 `_`을 사용하지 않는다. (function(){ var _privateState = true; function _privateMethod() { } function publicMethod() { _privateMethod(); } })(); // good - 외부에서 접근할 수 없다면, 메서드명 이나 속성명에 `_`을 사용하지 않는다. (function() { var privateState = true; function privateMethod() { } function publicMethod() { privateMethod(); } })(); ``` - [22.5](#22.5) 가능한 this를 캐싱하지 않는다. arrow functions 또는 [Function#bind](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind) 를 사용한다. jQuery를 사용하는 경우라면, [$.proxy](http://api.jquery.com/jQuery.proxy) 를 사용한다. 만약, this의 참조를 저장하는 경우라면 self 를 사용한다. ```javascript // 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); }; } ``` - [22.6](#22.6) 파일을 1개의 `클래스`로 export 하는 경우, 파일명은 클래스명과 완전히 일치시킨다. ```javascript // 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"; ``` - [22.7](#22.7) Default export가 `함수`일 경우, camelCase를 이용한다. 파일명은 함수명과 동일해야 한다. ```javascript function makeStyleGuide() { } export default makeStyleGuide; ``` - [22.8](#22.8) `Singleton / function library / 빈 오브젝트`를 export 하는 경우에는 PascalCase를 사용한다. ```javascript const AirbnbStyleGuide = { es6: { } }; export default AirbnbStyleGuide; ``` - [22.9](#22.9) 약어 및 이니셜은 항상 모두 대문자이거나 모두 소문자이어야 한다. ```javascript // bad import SmsContainer from "./containers/SmsContainer"; // bad const HttpRequests = [ // ... ]; // good import SMSContainer from "./containers/SMSContainer"; // good const HTTPRequests = [ // ... ]; // best import TextMessageContainer from "./containers/TextMessageContainer"; // best const Requests = [ // ... ]; ``` - [22.10](#22.10) 소스의 변수명, 클래스명 등에는 영문 이외의 언어를 사용하지 않는다. - [22.11](#22.11) 한글 발음을 그대로 사용하지 않는다. ```javascript // ''무형자산''이라는 의미의 변수를 선언한 예. // bad const moohyungJasan; // good const intangibleAssets; ``` - [22.12](#22.12) 클래스, 메서드 등의 이름에는 특수 문자를 사용하지 않는다. jQuery 변수의 경우 `$`을 사용하는 것은 예외사항으로 한다. ```js // bad funtion $some() { } ``` - [22.13](#22.13) 클래스명과 변수명은 `명사 사용`을 준수한다. - [22.14](#22.14) 메서드명은 `동사 사용`을 준수한다. - [22.15](#22.15) 상수명은 대문자를 사용하고, 단어와 단어사이는 _로 연결한다. ```js // bad const firefox = 1; const is_left = true; // good const FIREFOX = 1; const IS_LEFT = true; ``` **[⬆ back to top](#table-of-contents)** ## Accessors > [Airbnb](https://github.com/airbnb/javascript/blob/master/README.md#accessors) 와 동일합니다. - [23.1](#23.1) 프로퍼티를 위한 접근자 (Accessor) 함수는 필수가 아니다. - [23.2](#23.2) 접근자 함수가 필요한 경우, `getVal()` 이나 `setVal('hello')` 로 한다. ```javascript // bad dragon.age(); // good dragon.getAge(); // bad dragon.age(25); // good dragon.setAge(25); ``` - [23.3](#23.3) 프로퍼티가 `boolean` 인 경우, `isVal()` 이나 `hasVal()` 로 한다. ```javascript // bad if (!dragon.age()) { return false; } // good if (!dragon.hasAge()) { return false; } ``` - [23.4](#23.4) 일관된 경우, `get()` 과 `set()` 으로 함수를 작성해도 좋다. ```javascript class Jedi { constructor(options = {}) { const lightsaber = options.lightsaber || "blue"; this.set('lightsaber', lightsaber); } set(key, val) { this[key] = val; } get(key) { return this[key]; } } ``` **[⬆ back to top](#table-of-contents)** ## Events > [Airbnb](https://github.com/airbnb/javascript/blob/master/README.md#events) 와 다르게 별도의 가이드를 제공하지 않습니다. ## jQuery > `25.3 ~ 4` 항목을 제외하고는 [Airbnb](https://github.com/airbnb/javascript/blob/master/README.md#jquery) 와 동일합니다. - [25.1](#25.1) jQuery오브젝트의 변수는 선두에 `$` 를 부여한다. ```javascript // bad const sidebar = $(".sidebar"); // good const $sidebar = $(".sidebar"); // good const $sidebarBtn = $(".sidebar-btn"); ``` - [25.2](#25.2) jQuery의 검색결과를 캐시하여 사용한다. ```javascript // 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" }); } ``` - [25.3](#25.3) [Airbnb](https://github.com/airbnb/javascript/blob/master/README.md#jquery--queries) 와 다르게 `DOM 검색에 대해서`는 별도로 가이드 하지 않는다. - [25.4](#25.4) [Airbnb](https://github.com/airbnb/javascript/blob/master/README.md#jquery--find) 와 다르게 `DOM 검색에 대해서`는 별도로 가이드 하지 않는다. **[⬆ back to top](#table-of-contents)** ## Resources - [Airbnb 자바스크립트 스타일 가이드 - 영문](https://github.com/airbnb/javascript) - [Airbnb 자바스크립트 스타일 가이드 - 한글](https://github.com/tipjs/javascript-style-guide) - [NAVER 자바스크립트 스타일 가이드](https://github.com/parksb/javascript-style-guide/blob/master/README.md#%EB%B9%84%EA%B5%AC%EC%A1%B0%ED%99%94-destructuring) **[⬆ back to top](#table-of-contents)**