var
를 사용하지 말라고 많이들 들을 것이다. 우선은 var
를 사용하지 않는 것이 맞다. 나 역시도 이 말을 굉장히 많이 들었다. 왜 var
를 피해야하는지 3개월 전에는 와닿지 않았다. 하지만 이제는 얼마나 위험한 키워드인지 알게 됐고, 그 이유를 조금 구체적으로 정리하고자 한다. var
는 프로그래밍 관점에서 너무 위험한 현상을 수반한다. 어떠한 현상 때문에 위험한지, 더 좋은 코딩 방법은 무엇인지 알아보자. 장현석님의 클린코드 자바스크립트
를 토대로 정리하고, 추가적으로 살을 붙혔습니다.스코프 | 재할당 가능 여부 | 객체(배열) 조작 가능 여부 | 전역객체 조작 여부 | 호이스팅 | |
---|---|---|---|---|---|
var | 함수 스코프 | 가능 | 가능 | O | O |
let | 블록 스코프 | 가능 | 가능 | X | X |
const | 블록 스코프 | 불가능 | 가능 | X | X |
JavaScript에서는
var
는 지양하고,const
,let
을 사용해야 한다고 많이 듣는다. 그런데 왜?
var name = '휘바스키'
var name = '휘바스키2'
console.log(name) // 휘바스키3
console.log(name) // undefined
var name = '휘바스키'
var name = '휘바스키1'
var name = '휘바스키2'
name = '휘바스키3'
console.log(name) // '휘바스키3'
name변수
가 어떤 값을 가지고 있는지 알아내려면 꽤나 유심히 지켜봐야한다. 하지만 100줄 이상의 코드 내에서 위와 같은 상황이 벌어진다면 어떨까? name변수
가 무엇인지 찾으려면 꽤나 많은 시간을 보내야할 것이다.
var
를let
으로 바꿔보자.const
도 아래의 코드에서는let
과 같은 에러가 발생합니다.
console.log(name)
let name = '휘바스키'
// ReferenceError: Cannot access 'name' before initialization
let name = '휘바스키'
let name = '휘바스키1'
let name = '휘바스키2'
// SyntaxError: Identifier 'name' has already been declared
var
는 함수 스코프를 가지고 있다.var global = '전역'
//--------------------------------------------------
if (global === '전역') { ↑
var global = '지역'
⏐
console.log(global); // '지역'
} ↓
//--------------------------------------------------
// 위의 공간({}의 안쪽)내에서만 global변수를 '지역'으로 바꾸고, 그 외의 영역(전역)에서는
// '전역'이길 바랬는데, 아래의 결과처럼 '지역'으로 변수의 값이 오염되었다.
console.log(global); // 지역
let
과 const
는 블록 스코프를 가지고 있다.let global = '전역'
//--------------------------------------------------
if (global === '전역') { ↑
let global = '지역'
⏐
console.log(global); // '지역'
} ↓
//--------------------------------------------------
// 의도한 대로 동작한다. let은 block scope를 가지고 있다.
// 중괄호 {} 내부를 block 단위라고 한다.
console.log(global); // '전역'
let
은 재할당이 가능하지만 const
는 재할당이 불가능하다.const
는 값의 할당 없이는 선언이 불가하다let
let name
name = '휘바스키'
name = '휘바스키1'
name = '휘바스키2'
const
const name // 값을 할당없이 선언만하는 부분에서 바로 에러가 난다.
// SyntaxError: Missing initializer in const declaration
name = '휘바스키'
재할당은 안돼? 그럼 혹시 객체 조작도 안되는거 아니야?? Nope! 가능!
const person = {
name: 'Hwibaski',
age: 10
}
person.name = 'lee'
person.age = 11
// person = {name: 'lee', age: 11} -> 이 코드는 person 변수에 새로운 객체를 재할당하므로 불가
var
를 이용해서 전역 공간에 변수를 선언하면 전역 객체(window
or global
)에 등록된다.var test = 1
console.log(global.test) // 1
var setTimeout = 'hi'
console.log(setTimeout) // 'hi', 내장 함수인 setTimeout이 그냥 'hi'로 변경되버린다.
setTimeout(() => {
console.log('2 second');
}, 2000);
// TypeError: setTimeout is not a function -> ?? 끔찍하다.
const array = [1, 2, 3]
for(var i = 0; i < array.length; i++) {
console.log(array[i])
}
console.log(global)
RunJS
내에서 실행한 결과값입니다.
map
, filter
, reduce
)를 사용한다. → 강의 수강 후 추후에 블로깅하겠습니다.// 아래의 예제는 예를 들기위한 코드입니다. 참고만 해주세요.
function getElements() {
const result = {} // 이 부분이 임시 변수 혹은 임시 객체이다.
result.title = 'title';
result.text = 'text';
result.value = 'value'
// result.any = 'anything' -> 이와 같이 쉽게 값을 추가할 수 있을 것 같은 느낌이 든다.
return result;
}
// better code ver 01
function getElements() {
const result = {
title: 'title',
text: 'text',
value: 'value'
}
return result;
}
// better code ver 02 :
function getElements() {
return {
title: 'title',
text: 'text',
value: 'value'
}
}
//---------------------------------------------------------------------------
// bad code : 추후에 추가적인 사항이 요구된다면 수정이 힘들다.
// 아래의 함수를 100군데에서 이미 사용하고 있는 상황이라면, 이 함수를 변경함으로 인해
// 많은 문제점을 야기할 수 있다.
function getDateTime(targetData) {
let month = targetDate.getMonth();
let day = targetDate.getDate();
let hour = targetDate.Hours();
month = month >= 10 ? month : '0' + month;
day = day >= 10 ? day : '0' + day;
hour = hour >= 10 ? houre : '0' + hour;
return {
month, day, hour
}
}
// better code ver 01 :
function getDateTime(targetData) {
const month = targetDate.getMonth();
const day = targetDate.getDate();
const hour = targetDate.Hours();
return {
month: month >= 10 ? month : '0' + month,
day: day >= 10 ? day : '0' + day,
hour: hour >= 10 ? hour : '0' + hour:
}
}
// '~ 전'이라는 문자열을 추가해주는 기능을 구현해주세요~ 라고 요청이 왔다라고 가정해보자.
// 다른 함수를 정의하고 앞서 정의한 better code ver 01을 호출해서 쓰면 확장이 용이하다.
function 추가요구사항구현함수() {
const currentDataTime = getDateTime(new Date())
return {
month: currentDateTime.month + '달 전',
day: currentDateTime.day + '일 전',
hour: currentDateTime.hour + '시간 전',
}
}
결론은 하나의 함수에서 너무 많은 일을 하지 말고, 불필요한 변수를 선언하지 말자.
// 정상적으로 실행되는 코드가 아닙니다.
// 이런식으로 하나의 함수에서 너무 많은 일을 수행하지 말자는 것을 보여주는 예시입니다.
// 디버깅이 힘들고 함수의 기능을 파악하기 힘들어집니다.
function badFunction(params) {
let test = ''
const array = [1, 2, 3]
for (let i = 0; i < array.length; i++) {
test = array[i]
}
if (test = 1) {
test = 'test'
} else if (test = 2) {
test = 'hi'
}
return test
}
let
과 const
에서는 일어나지 않는다.var
의 호이스팅var global = 0;
function outer() {
console.log(global); // undefined
var global = 5;
function inner() {
var global = 10;
console.log(global); // 10
}
inner()
global = 1
console.log(global); // 1
}
outer()
var
가 함수 스코프를 가지고 있으니까 10이 된다는 것은 이해하기 쉽다.global
을 변경한 것인지, (2)에서 선언한 global
을 변경한 것인지는 모르겠지만 (4)에서 1로 할당하고 있으므로 이해하기 쉽다.undefined
를 출력하고 있는 global
은 무엇일까?global
이 호이스팅이 되서 undefined
를 가지게 되었다.자주색 hositng
부분은 실제로 있는 코드가 아니다. 하지만 (2)의 코드가 함수내부에서 선언부만 호이스팅되서 자주색 영역 내의 코드 같은 역할을 하고 있다.
실험을 해보자..
var global = 0;
var test = 1
function outer() {
console.log(test) // 1 -> 전역에 선언한 test 변수가 출력된다.
console.log(global); // 0 -> 바로 밑의 global을 주석 처리 시 전역 변수 global을 출력한다.
// var global = 5;
function inner() {
var global = 10;
console.log(global); // 10
}
inner()
global = 1
console.log(global); // 1
}
outer()
함수도 호이스팅이 된다.
정확히 말하면 함수 선언문
이 호이스팅 된다
ex1)
var sum;
sum = function() {
return 1 + 2;
}
// 여기까지는 별 문제가 없다
console.log(sum()); // 3
var sum;
function sum() {
return 1 + 2;
}
// 조금 이상하다. sum이라는 변수를 선언했는데, sum이라는 함수를 이상없이 만들고 실행이 가능하다...
console.log(sum()); // 3
var sum;
console.log(sum()); // 3
console.log(typeof sum) // function -> 왜 가장 상단에 선언한 sum을 찾지 못할까?
function sum() {
return 1 + 2;
}
var sum;
console.log(sum()); // 10 -> ?? 아래의 함수들이 전부 호이스팅되서 마지막 함수가 작동한다.
function sum() {
return 1 + 2;
}
function sum() {
return 1 + 2 + 3;
}
function sum() {
return 1 + 2 + 3 + 4;
}
이 미친 상황을 어떻게 방지할 수 있을까요? 함수 표현식!!
const
에 함수를 담아서 호이스팅을 방지하고, 재할당도 불가하게 합니다.console.log(sum); // Cannot access sum before init or Sum is not defined
const sum = function() {
return 1 + 2;
};
sum = 1 // TypeError: Assignment to constant variable.
var
쓰지 말자!!