우아한테크코스 프리코스 과제를 하면서 '자바스크립트 컨벤션'을 지키면서 과제를 수행하라고 명시되어 있었다.
'자바스크립트 컨벤션'에 대해서 공부해 볼 필요가 있었다.
=> 원시형 같은 경우 메모리 상에서 스택에 저장된다. 반면에 참조형 같은 경우 메모리 상에서 힙에 저장된다.
const arr = [1,2,3];
const copy_arr = arr;
copy_arr[0] = 5 // arr[0] = 5
이렇게 참조형을 복사할 때에는 깊은 복사를 해야 하는 것이 생각난다.
// bad
var a = 1;
var b = 2;
// good
const a = 1;
const b = 2;
// bad
var count = 1;
if (true) {
count += 1;
}
// good, use the let.
let count = 1;
if (true) {
count += 1;
}
// const와 let은 선언된 블록 안에서만 존재합니다.
{
let a = 1;
const b = 1;
}
console.log(a); // ReferenceError
console.log(b); // ReferenceError
한 때 호이스팅에 대해서 공부한 적이 있었다.
호이스팅이란 코드가 실행되기 전에 선언된 변수와 함수를 기억해 둔다.
코드가 실행되면서 기억해 둔 변수와 함수를 꺼내서 쓰는 구조이다.
즉 안에 있는 변수들을 범위의 최 상단으로 끌어올리는 것이다.
console.log(a) // undefined
var a = 1;
console.log(a) // 1
다른 언어라면 에러가 발생되지만 자바스크립트에서는 호이스팅때문에 에러가 발생하지 않고 undefined가 뜨게 된다.
즉 변수를 멋대로 선언하고 할당도 undefined로 멋대로 한다.
var는 지역변수와 전역변수도 제대로 구분되어 있지 않다.
for(var i = 1; i< 5; i++){
console.log(i) => 1,2,3,4
}
console.log(i) => 5
var는 함수만 지역변수로 호이스팅이 되고 나머지로 다 전역변수로 올려버린다.
let도 호이스팅이 되지만 할당이 되기 전까지는 TDZ이기 때문에 접근할 수 없다는 에러가 뜬다.
=> 그냥 var를 사용하지 말자
// bad
const item = new Object();
// good
const item = {};
// bad
const superman = {
default: { clark: 'kent' },
private: true,
};
// good
const superman = {
defaults: { clark: 'kent' },
hidden: true,
};
예약어는 abstract, break, catch, class, default, private 등이 있다.
자세한 것은 https://redk.tistory.com/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,
};
객체에 값을 추가할 때 'key' : 'value'형태로만 가능할 줄 알았는데
const a = '1'
const b= '2'
const obj = {
a,
b,
test1 : 1,
test2 : 2
}
이런식으로도 추가 가능하다. 이럴 때 a,b는 앞에 써주자
// bad
const items = new Array();
// good
const items = [];
리터럴 구문을 사용하면 가독성, 편의성, 또한 배열의 크기와 요소를 직접 정의할 수 있어 성능측면에서도 향상된다고 하니 객체나 배열을 생성할 땐 리터럴 구문을 사용하자
// bad
const len = items.length;
const itemsCopy = [];
let i;
for (i = 0; i < len; i++) {
itemsCopy[i] = items[i];
}
// good
const itemsCopy = [...items];
... 연산자를 사용하면 원본 배열을 변경하지 않고 새로운 배열을 만드는 간단한 방법이다.
// bad
function getFullName(user) {
const firstName = user.firstName;
const lastName = user.lastName;
return `${firstName} ${lastName}`;
}
// good
function getFullName(obj) {
const { firstName, lastName } = obj;
return `${firstName} ${lastName}`;
}
// best
function getFullName({ firstName, lastName }) {
return `${firstName} ${lastName}`;
}
리액트를 공부할 때 API에서 객체를 전달받을 때 구조화대입을 사용한 것이 기억난다. 그냥 사용했지만 이제는 알고 사용해야 겠다.
router.post("/bbsListCount", (req, res) => {
getConnection((conn) => {
const {userID, curtab, option, input,limit, page, orderTarget, orderValue } = req.body;
이런식으로 말이다.
const arr = [1, 2, 3, 4];
// bad
const first = arr[0];
const second = arr[1];
// good
const [first, second] = arr;
배열도 구조화대입이 되는지 몰랐다.
만약 first, third를 얻고 싶다면
const [first, , third] = arr;
하면 된다.
하지만 순서에 영향을 받기 때문에 오브젝트의 구조화대입을 이용하자.
// 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);
// bad
const name = "Capt. Janeway";
// good
const name = 'Capt. Janeway';
// 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}?`;
}
// bad
const foo = function () {
};
// good
function foo() {
}
console.log(foo())
호이스팅과 관련되어 있다. 함수 선언식은 호이스팅시 전체가 올라가는데 함수식, 화살표 함수는 참조만 올라간다. 그래서 함수선언식 같은 경우 선언전에도 사용가능하지만 함수식, 화살표 함수는 함수호출 전에 변수를 선언해야 한다.
안전성 측면에서는 한수식이 더 괜찮아 보이지만 가독성, 편리성 측면에서는 함수선언이 좋아 보인다.
// 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;
}
}
// 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];
}
}
// 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;
주로 COMMONJS방식으로 import, export하였는데, 가이드에서는 ES6문법으로 import, export를 권장하고 있었다.
// bad
import * as AirbnbStyleGuide from './AirbnbStyleGuide';
// good
import AirbnbStyleGuide from './AirbnbStyleGuide';
const luke = {
jedi: true,
age: 28,
};
// bad
const isJedi = luke['jedi'];
// good
const isJedi = luke.jedi;
const luke = {
jedi: true,
age: 28,
};
function getProp(prop) {
return luke[prop];
}
const isJedi = getProp('jedi');
따로 구분없이 사용하였는데 직접 접근할 때에는 .을
변수를 통해 접근할 때에는 []를 사용하는 것이 더 좋아 보인다.
// 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;
function example() {
console.log(notDefined); // => throws a ReferenceError
}
notDefined가 선언된 것이 없어 에러가 발생한다.
function example() {
console.log(declaredButNotAssigned); // => undefined
var declaredButNotAssigned = true;
}
호이스팅 된 상태에서 동작한다. 그래서 undefined를 출력한다.
function example() {
let declaredButNotAssigned;
console.log(declaredButNotAssigned); // => undefined
declaredButNotAssigned = true;
}
호이스팅이 되지만 변수가 선언된 후 호출했으므로 undefined
function example() {
console.log(declaredButNotAssigned); // => throws a ReferenceError
console.log(typeof declaredButNotAssigned); // => throws a ReferenceError
const declaredButNotAssigned = true;
}
호이스팅이 되지만 아직 변수가 선언되기 전에 호출했으므로 오류가 발생
function example() {
console.log(anonymous); // => undefined
anonymous(); // => TypeError anonymous is not a function
var anonymous = function() {
console.log('anonymous function expression');
};
}
함수식도 마찬가지로 변수가 호이스팅되어 undefined가 발생한다. 하지만 함수식은 함수선언식과 반대로 함수가 할당되기전에 호출되면 에러가 발생한다.
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');
};
}
마찬가지로 named가 호이스팅 되어 undefined가 출력된다. 함수식이므로 에러가 발생한다.
함수명이나 함수본체는 호이스팅이 되지 않아 에러가 발생한다.
function example() {
console.log(superPower); // => func
superPower(); // => Flying
function superPower() {
console.log('Flying');
};
}
함수선언식 같은 경우 에러가 발생하지 않는다. 함수식과 함수선언식 차이를 알아야 할 필요가 있다.
// bad
if (name !== '') {
// ...stuff...
}
// good
if (name) {
// ...stuff...
}
// bad
if (collection.length > 0) {
// ...stuff...
}
// good
if (collection.length) {
// ...stuff...
}
// 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( 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 story = [
once
, upon
, aTime
];
// good
const story = [
once,
upon,
aTime,
];
// 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)
// good (즉시함수가 연결된 2개의 파일일때 인수가 되는 부분을 보호합니다.
;(() => {
const name = 'Skywalker';
return name;
})();
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);