반응형
0️⃣ 소스파일
- 소스파일의 이름은 알파벳 소문자, 하이픈( - ), 밑줄( _ ) 으로만 작성한다.
- 소스파일의 인코딩은 UTF-8 으로 작성한다.
1️⃣ 형식
- 중괄호
- 중괄호는 하나의 구문만을 포함하고 있더라도 모든 제어문(i.e. if, else, for, do, while..)에 사용되어야 한다.
- if문의 경우, 하나의 구문만 포함할 경우, 같은 라인에 작성하고 중괄호를 생략할 수 있다.
// bad if (someVeryLongCondition()) doSomething(); // No for (let i = 0; i < foo.length; i++) bar(foo[i]); // Yes if (shortCondition()) foo();
- 중괄호의 사용은 Kernighan and Ritchie Style에 따른다.
- 여는 중괄호 전에는 줄을 바꾸지 않는다.
- 여는 중괄호 이후에 줄을 바꾼다.
- 닫는 중괄호 전에 줄을 바꾼다.
- 닫는 중괄호 이후에 줄을 바꾼다. 단, 닫는 중괄호 다음에 else, catch, while, 콤마, 세미콜론, 닫는 소괄호가 따라올 경우에는 줄을 바꾸지 않는다.
class InnerClass { constructor() {} /** @param {number} foo */ method(foo) { if (condition(foo)) { try { something(); } catch (err) { recover(); } } } }
- 빈 블록의 경우, 여는 중괄호( { ) 바로 다음에 닫는 중괄호를 사용한다.
단, if-else 구조 혹은 try-catch-finally 구조에서의 빈 블록에서는 줄을 바꾼다.
// Yes
function doNothing() {}
// No
if (condition) {
// …
} else if (otherCondition) {} else {
// …
}
// No
try {
// …
} catch (e) {}
- 들여쓰기
- 새로운 블록이 나타날 때 마다 두 칸의 공백(스페이스바)를 통해 들여쓴다.
- 배열, 객체는 블록과 동일하게 들여쓴다.
- 클래스
- 클래스 리터럴은 블록과 동일하게 들여쓴다.
- 클래스의 각 메소드와 닫는 중괄호 다음에는 세미콜론을 사용하지 않는다.
- 클래스의 상속 키워드로 @extends가 아닌 extends를 사용한다.
- 함수 표현식을 작성할 대에는 함수를 이전 레벨보다 2칸 들여써 작성한다.
prefix.something.LongFunctionName('whatever', (a1, a2) => {
// Indent the function body +2 relative to indentation depth
// of the 'prefix' statement one line above.
if (a1.equals(a2)) {
someOtherLongFunctionName(a1);
} else {
andNowForSomethingCompletelyDifferent(a2.parrot);
}
});
- 스위치문은 블록과 동일하게 들여쓰며, break와 다음 case 사이의 공백은 선택
switch (animal) {
case Animal.BANDERSNATCH:
handleBandersnatch();
break;
case Animal.JABBERWOCK:
handleJabberwock();
break;
default:
throw new Error('Unknown animal');
}
- 긴 메소드 체인을 작성할 때는 각 메소드마다 들여쓴다.
// 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
let name = 'Cada'
doSomething() saySomething()
// good
let name = 'Cada';
doSomething();
saySomething();
- 공백
- 한 줄이 80자를 넘기지 않도록 한다.
- 줄 공백 (Vertical Whitespace)
- 줄 공백은 아래의 사항에서 나타날 수 있다.
- 각 구문 사이
- 클래스나 객체 리터럴 내의 연속적으로 작성된 메소드 사이
- 오브젝트 리터럴 내의 연속적으로 작성된 속성 사이에서는 선택적으로 빈 줄을 사용할 수 있다. 이는, 논리적 그룹을 만드는데 사용된다.
- 클래스나 객체 리터럴 내의 첫 메소드 전이나 마지막 메소드 다음에 빈 줄을 선택적으로 사용할 수 있다.
- 줄 공백으로 2줄 이상 연속으로 사용하지 않는다.
// 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;
- 줄 공백은 아래의 사항에서 나타날 수 있다.
- 칸 공백 (Horizontal Whitespace)
칸 공백은 리딩(줄의 시작), 트레일링(줄의 끝), 인터널로 나눌 수 있다.- 리딩 공백은 들여쓰기 규칙에 따라 언제든지 사용될 수 있다.
- 트레일링 공백은 사용하지 않는다.
- 칸 공백은 아래의 사항에서 나타날 수 있다.
- if, for, catch와 같은 키워드와 소괄호 사이
(단, function과 super는 예외)
- if, for, catch와 같은 키워드와 소괄호 사이
// bad
if(myCondition) {
// do something
}
// good
if (myCondition) {
// do something
}
- else, catch와 같은 키워드와 닫는 중괄호 사이
// bad
if(myCondition) {
// do something
}else { }
// good
if (myCondition) {
// do something
} else { }
- 여는 중괄호 ( { ) 전
(단, 오브젝트 리터럴의 첫 인자나 배열의 첫 인자, 템플릿 리터럴은 예외) - 이항 연산자와 삼항 연산자의 양 쪽
// bad
const name = someCondotion?20:30;
// good
const name = someCondotion ? 20 : 30;
- 콤마( , ), 세미콜론( ; ) 다음 (단, 콤마와 세미콜론 전에는 사용하지 않음)
// bad
const arr = [1,2,3,4];
// good
const arr = [1, 2, 3, 4];
- 오브젝트 리터럴 내에서 콜론( : ) 다음
// bad
const myobject = {
"name":"Chris",
"age":34,
};
// good
const myobject = {
"name": "Chris",
"age": 34,
};
- // 양 쪽, /* 다음, */ 전
- 소괄호 사이, 대괄호 사이에는 칸 공백을 사용하지 않는다.
// 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' };
2️⃣ 변수
- 한 줄에 하나의 변수를 선언한다.
// bad
let a = 1, b = 3;
// good
let a = 1;
let b = 2;
- 지역 변수는 그 변수를 포함하는 블록 시작에서 선언하지 않고, 사용 범위를 최소화하기 위해 사용되는 지점과 가장 가까운 곳에서 선언한다.
// good
function() {
test();
console.log('doing stuff..');
const name = getName();
if (name === 'test') {
return false;
}
return name;
}
// bad - 함수 호출의 부적절한 위치
function(hasName) {
const name = getName();
if (!hasName) {
return false;
}
this.setFirstName(name);
return true;
}
// good
function(hasName) {
if (!hasName) {
return false;
}
const name = getName();
this.setFirstName(name);
return true;
}
- JSDoc을 위한 주석은 변수 선언 이전 혹은 변수 이름 이전에 작성한다.
(단, 두 가지 위치에 동시에 모두 작성하지 않는다)
// bad
/** Some description. */
const /** !Array<number> */ data = [];
const /** !Array<number> */ data = [];
// good
/**
* Some description.
* @type {!Array<number>}
*/
const data = [];
- 변수를 선언할 때는 const를 사용한다.
단, 변수의 값이 바뀌는 경우 let을 사용한다. - 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;
3️⃣ 배열
- 배열을 선언할 때는 Array 생성자가 아닌 리터럴 구문을 사용한다.
// bad
const items = new Array();
// good
const items = [];
- 배열에 값을 넣을 때는 Array.push를 사용한다.
const someStack = [];
// bad
someStack[someStack.length] = 'abracadabra';
// good
someStack.push('abracadabra');
- 배열을 복사할 때는 배열의 확장연산자 ( ... )를 사용한다.
// bad
const len = items.length;
const itemsCopy = [];
let i;
for (i = 0; i < len; i++) {
itemsCopy[i] = items[i];
}
// good
const itemsCopy = [...items];
- 배열과 유사한 오브젝트를 배열로 변환할 때는 Array.from을 사용한다.
const foo = document.querySelectorAll('.foo');
const nodes = Array.from(foo);
- 배열의 선언 시, 마지막 원소를 포함하여 각 원소 끝에는 콤마( , )를 포함한다.
const values = [
'first value',
'second value',
'third value',
];
- 배열에 숫자가 아닌 속성을 사용하지 않는다.
사용을 원할경우 맵 혹은 오브젝트를 사용한다.
// bad
const arr = [];
arr["str"] = 32;
// good
const obj = {};
arr.str = 32;
const map = new Map();
map.set("str", 32);
- 한 배열로부터 복수개의 값을 할당받을 때는 구조 분해 할당을 사용한다.
이는 함수의 파라미터에서도 사용할 수 있다.
const [a, b, c, ...rest] = generateResults();
let [, b,, d] = someArray;
function optionalDestructuring([a = 4, b = 2] = []) { … };
4️⃣ 객체
- 오브젝트를 선언할 때는 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
var my_object = {
key: "value",
};
var my_object = {
key: "value",
"key2": "value2",
};
// good
var my_object = {
"key": "value",
"key2": "value2",
};
- 동적 속성명을 사용할 때는, 계산된 속성명(Computed Porperty Name)을 사용한다.
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,
};
// 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,
};
5️⃣ 클래스
- 생성자는 선택적으로 작성한다. 하지만 하위 클래스는 필드를 설정하기 전에 반드시 super()를 호출해야한다. 그렇지 않으면 this에 접근할 것이다.
인터페이스의 경우 메소드가 아닌 속성을 생성자에서 반드시 선언해야 한다. - 정적 메소드의 사용 보다는 모듈 함수를 더 지향한다.
- 정적 메소드는 클래스 내에서만 호출되어야 한다.
- 동적 인스턴스를 포함하는 변수나 하위 클래스의 생성자 내부에서 호출되어서는 안된다.
- 하위 클래스 내에 선언되지 않은 정적 메소드는 호출되어서는 안된다.
class Base { /** @nocollapse */ static foo() {} } class Sub extends Base {} function callFoo(cls) { cls.foo(); } // Bad: 동적 인스턴스를 통해 호출되어서는 안된다. Sub.foo(); // Bad: 하위 클래스 내에 선언되지 않은 정적 메소드는 호출되어서는 안된다.
- prototype을 직접 조작하지 않고 class를 사용한다.
// bad
function Queue(contents = []) {
this._queue = [...contents];
}
Queue.prototype.pop = function() {
const value = this._queue[0];
this._queue.splice(0, 1);
return value;
}
// good
class Queue {
constructor(contents = []) {
this._queue = [...contents];
}
pop() {
const value = this._queue[0];
this._queue.splice(0, 1);
return value;
}
}
- 상속은 extends를 사용한다.
// bad
const inherits = require('inherits');
function PeekableQueue(contents) {
Queue.apply(this, contents);
}
inherits(PeekableQueue, Queue);
PeekableQueue.prototype.peek = function() {
return this._queue[0];
}
// good
class PeekableQueue extends Queue {
peek() {
return this._queue[0];
}
}
- 메소드의 반환값으로 this를 사용하는 것으로 메소드 채이닝을 할 수 있다.
// bad
Jedi.prototype.jump = function() {
this.jumping = true;
return true;
};
Jedi.prototype.setHeight = function(height) {
this.height = height;
};
const luke = new Jedi();
luke.jump(); // => true
luke.setHeight(20); // => undefined
// good
class Jedi {
jump() {
this.jumping = true;
return this;
}
setHeight(height) {
this.height = height;
return this;
}
}
const luke = new Jedi();
luke.jump()
.setHeight(20);
- toString() 메소드를 오버라이딩할 수 있지만 사이드 이펙트가 나타나지 않도록 해야한다.
class Jedi {
constructor(options = {}) {
this.name = options.name || 'no name'; // null이 반환되는 것을 방지
}
getName() {
return this.name;
}
toString() {
return `Jedi - ${this.getName()}`;
}
}
6️⃣ 함수
- 함수 내에 또다른 함수를 선언할 수 있다. 함수에 이름이 필요한 경우 const를 사용한다.
- 함수 표현식 보다는 함수 선언식을 사용한다.
// bad
const foo = function () { // foo만 hoist됨
};
// good
function foo() { // 함수 전체가 hoist됨
}
- 함수 이외의 블록(i.e. if, while) 안에서 함수를 선언하지 않는다.
- 함수의 파라미터에 arguments를 사용하지 않는다. 이는 오브젝트의 참조를 덮어쓰는 것을 야기한다.
- arguments를 사용하는 것 대신에 Rest 신택스( ... )를 사용한다.
// bad
function concatenateAll() {
const args = Array.prototype.slice.call(arguments);
return args.join('');
}
// good
function concatenateAll(...args) {
return args.join('');
}
- 함수의 파라미터를 조작하는 것보다는 파라미터 기본값을 사용한다.
// really bad
function handleThings(opts) {
// 이는 좋지 않습니다. 함수의 파라미터를 변이시키면 안됩니다.
// 만약 opts가 falsy 하다면 바라는데로 오브젝트가 설정됩니다.
// 하지만 미묘한 버그를 일으킬지도 모릅니다.
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
- 파라미터 기본값은 가장 뒤쪽에 둔다.
// bad
function handleThings(opts = {}, name) {
// ...
}
// good
function handleThings(name, opts = {}) {
// ...
}
- 함수식을 사용해야만한다면, 화살표 함수(Arrow Function)을 사용하라.
// 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;
});
7️⃣ 화살표 함수 (Arrow Function)
- 함수 바디가 하나의 식으로 구성된 경우, 중괄호와 return문을 생략할 수 있다.
중괄호를 생략하지 않을 경우, return문을 포함시켜야 한다.
// good
[1, 2, 3].map(number => `A string containing the ${number}.`);
// bad
[1, 2, 3].map(number => {
const nextNumber = number + 1;
`A string containing the ${nextNumber}.`;
});
// good
[1, 2, 3].map(number => {
const nextNumber = number + 1;
return `A string containing the ${nextNumber}.`;
});
- 식이 여러 줄에 걸쳐있을 경우에 가독성을 위해 소괄호로 감싸 사용한다.
// 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.'
));
- 함수의 인수가 하나인 경우 소괄호를 생략할 수 있다.
// good
[1, 2, 3].map(x => x * x);
// good
[1, 2, 3].reduce((y, x) => x + y);
8️⃣ 문자열
- 문자열을 선언할 때는 작은 따옴표( ' )를 사용한다.
문자열 내에 작은 따옴표가 포함될 경우 템플릿 리터럴( ` ` )을 사용한다.
// bad
const name = "Capt. Janeway";
// good
const name = 'Capt. Janeway';
// good
const name = `Mark J' Maclachlan`;
- 80글자 이상의 긴 문자열을 여러 줄에 걸쳐 쓰기 위해서는 템플릿 리터럴 혹은 문자열 연결( + )을 사용한다.
// 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.';
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();
}
// good
function sayHi(name) {
return `How are you, ${name}?`;
}
- eval() 함수를 사용하지 않는다. 취약점이 많은 함수이다.
9️⃣ 제어문
- 반복문
- 일반적인 for문 보다는 for-of를 사용한다.
하지만, 가능하다면 map(), reduce()와 같은 고급 함수를 사용하라.
- 일반적인 for문 보다는 for-of를 사용한다.
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;
- 스위치문
- 다음 case 구문이 실행되어야 한다면 주석으로 이를 남긴다.
- default문은 항상 마지막에 위치한다.class Base { /** @nocollapse */ static foo() {} } class Sub extends Base {} function callFoo(cls) { cls.foo(); } // Bad: 동적 인스턴스를 통해 호출되어서는 안된다. Sub.foo(); // Bad: 하위 클래스 내에 선언되지 않은 정적 메소드는 호출되어서는 안된다.
switch (input) {
case 1:
case 2:
prepareOneOrTwo();
// fall through
case 3:
handleOneTwoOrThree();
break;
default:
handleLargeNumber(input);
}
- this
- this키워드는 클래스 생성자와 메소드, 혹은 그 안에서 선언된 화살표 함수 내에서만 사용한다.
- 조건식, 등가식
- ==, != 보다는 ===, !==를 사용하라.
- 조건식은 ToBoolean 메소드에 의한 강제 형변환으로 평가되어 아래의 규칙을 따른다.
- 오브젝트는 true로 평가
- undefined는 false로 평가
- null 은 false 로 평가
- 값은 true로 평가. 단, +0, -0, or NaN 의 경우는 false로 평가
- 문자열은 true로 평가. 단, 빈문자( '' )의 경우는 false로 평가
if ([0]) {
// true
// An array is an object, objects evaluate to true
// 배열은 오브젝트이므로 true 로 평가됩니다.
}
# 참고사이트
▶ Google style Guide
▶ Airbnb style Guide
▶ Nexedi style Guide
▶ W3S School Guide
반응형
'Frontend > JavaScript' 카테고리의 다른 글
[JS] 자바스크립트 Ajax 통신 예제 (vanilla JS) (0) | 2022.02.03 |
---|---|
[JS] 자바스크립트 함수 호이스팅 (0) | 2022.01.25 |
[JS] 자바스크립트 네이밍 컨벤션 (0) | 2022.01.21 |
[JS] 자바스크립트 특정 문자로 시작하거나 끝나는지 확인하기 (0) | 2022.01.13 |
[JS] 자바스크립트 문자열에서 숫자만 추출하기 (0) | 2021.12.20 |
최근댓글