๋ณธ๋ฌธ์œผ๋กœ ๊ฑด๋„ˆ๋›ฐ๊ธฐ

[Medium] ๐Ÿ“„ var, let, const

๊ฐœ์š”โ€‹

JavaScript์—๋Š” ๋ณ€์ˆ˜๋ฅผ ์„ ์–ธํ•˜๋Š” ์„ธ ๊ฐ€์ง€ ํ‚ค์›Œ๋“œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค: var, let, const. ๋ชจ๋‘ ๋ณ€์ˆ˜ ์„ ์–ธ์— ์‚ฌ์šฉ๋˜์ง€๋งŒ, ์Šค์ฝ”ํ”„, ์ดˆ๊ธฐํ™”, ์ค‘๋ณต ์„ ์–ธ, ์žฌํ• ๋‹น, ์ ‘๊ทผ ์‹œ์  ๋“ฑ์—์„œ ์ฐจ์ด๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

์ฃผ์š” ์ฐจ์ด์ โ€‹

๋™์ž‘varletconst
์Šค์ฝ”ํ”„ํ•จ์ˆ˜ ์Šค์ฝ”ํ”„ ๋˜๋Š” ์ „์—ญ๋ธ”๋ก ์Šค์ฝ”ํ”„๋ธ”๋ก ์Šค์ฝ”ํ”„
์ดˆ๊ธฐํ™”์„ ํƒ์„ ํƒํ•„์ˆ˜
์ค‘๋ณต ์„ ์–ธํ—ˆ์šฉ๋ถˆํ—ˆ๋ถˆํ—ˆ
์žฌํ• ๋‹นํ—ˆ์šฉํ—ˆ์šฉ๋ถˆํ—ˆ
์„ ์–ธ ์ „ ์ ‘๊ทผundefined ๋ฐ˜ํ™˜ReferenceError ๋ฐœ์ƒReferenceError ๋ฐœ์ƒ

์ƒ์„ธ ์„ค๋ช…โ€‹

์Šค์ฝ”ํ”„โ€‹

var์˜ ์Šค์ฝ”ํ”„๋Š” ํ•จ์ˆ˜ ์Šค์ฝ”ํ”„ ๋˜๋Š” ์ „์—ญ ์Šค์ฝ”ํ”„์ด๋ฉฐ, let๊ณผ const๋Š” ๋ธ”๋ก ์Šค์ฝ”ํ”„(ํ•จ์ˆ˜, if-else ๋ธ”๋ก, for ๋ฃจํ”„ ํฌํ•จ)์ž…๋‹ˆ๋‹ค.

function scopeExample() {
var varVariable = 'var';
let letVariable = 'let';
const constVariable = 'const';

console.log(varVariable); // 'var'
console.log(letVariable); // 'let'
console.log(constVariable); // 'const'
}

scopeExample();

console.log(varVariable); // ReferenceError: varVariable is not defined
console.log(letVariable); // ReferenceError: letVariable is not defined
console.log(constVariable); // ReferenceError: constVariable is not defined

if (true) {
var varInBlock = 'var in block';
let letInBlock = 'let in block';
const constInBlock = 'const in block';
}

console.log(varInBlock); // 'var in block'
console.log(letInBlock); // ReferenceError: letInBlock is not defined
console.log(constInBlock); // ReferenceError: constInBlock is not defined

์ดˆ๊ธฐํ™”โ€‹

var์™€ let์€ ์„ ์–ธ ์‹œ ์ดˆ๊ธฐํ™”ํ•˜์ง€ ์•Š์•„๋„ ๋˜์ง€๋งŒ, const๋Š” ๋ฐ˜๋“œ์‹œ ์„ ์–ธ ์‹œ ์ดˆ๊ธฐํ™”ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

var varVariable;  // ์œ ํšจ
let letVariable; // ์œ ํšจ
const constVariable; // SyntaxError: Missing initializer in const declaration

์ค‘๋ณต ์„ ์–ธโ€‹

๊ฐ™์€ ์Šค์ฝ”ํ”„ ๋‚ด์—์„œ var๋Š” ๋™์ผํ•œ ๋ณ€์ˆ˜์˜ ์ค‘๋ณต ์„ ์–ธ์„ ํ—ˆ์šฉํ•˜์ง€๋งŒ, let๊ณผ const๋Š” ํ—ˆ์šฉํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

var x = 1;
var x = 2; // ์œ ํšจ, x๋Š” ์ด์ œ 2

let y = 1;
let y = 2; // SyntaxError: Identifier 'y' has already been declared

const z = 1;
const z = 2; // SyntaxError: Identifier 'z' has already been declared

์žฌํ• ๋‹นโ€‹

var์™€ let์œผ๋กœ ์„ ์–ธ๋œ ๋ณ€์ˆ˜๋Š” ์žฌํ• ๋‹น์ด ๊ฐ€๋Šฅํ•˜์ง€๋งŒ, const๋กœ ์„ ์–ธ๋œ ๋ณ€์ˆ˜๋Š” ์žฌํ• ๋‹นํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

var x = 1;
x = 2; // ์œ ํšจ

let y = 1;
y = 2; // ์œ ํšจ

const z = 1;
z = 2; // TypeError: Assignment to a constant variable

์ฐธ๊ณ : const๋กœ ์„ ์–ธ๋œ ๋ณ€์ˆ˜๋Š” ์žฌํ• ๋‹นํ•  ์ˆ˜ ์—†์ง€๋งŒ, ๊ฐ์ฒด๋‚˜ ๋ฐฐ์—ด์ธ ๊ฒฝ์šฐ ๊ทธ ๋‚ด์šฉ์€ ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

const obj = { key: 'value' };
obj.key = 'new value'; // ์œ ํšจ
console.log(obj); // { key: 'new value' }

const arr = [1, 2, 3];
arr.push(4); // ์œ ํšจ
console.log(arr); // [1, 2, 3, 4]

์„ ์–ธ ์ „ ์ ‘๊ทผ (Temporal Dead Zone)โ€‹

var๋กœ ์„ ์–ธ๋œ ๋ณ€์ˆ˜๋Š” Hoisting๋˜์–ด ์ž๋™์œผ๋กœ undefined๋กœ ์ดˆ๊ธฐํ™”๋ฉ๋‹ˆ๋‹ค. ๋ฐ˜๋ฉด, let๊ณผ const๋กœ ์„ ์–ธ๋œ ๋ณ€์ˆ˜๋„ Hoisting๋˜์ง€๋งŒ ์ดˆ๊ธฐํ™”๋˜์ง€ ์•Š์œผ๋ฏ€๋กœ, ์„ ์–ธ ์ „์— ์ ‘๊ทผํ•˜๋ฉด ReferenceError๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

console.log(x); // undefined
var x = 5;

console.log(y); // ReferenceError: Cannot access 'y' before initialization
let y = 5;

console.log(z); // ReferenceError: Cannot access 'z' before initialization
const z = 5;

๋ฉด์ ‘ ๋ฌธ์ œโ€‹

๋ฌธ์ œ: setTimeout + var์˜ ๋Œ€ํ‘œ์  ํ•จ์ •โ€‹

๋‹ค์Œ ์ฝ”๋“œ์˜ ์ถœ๋ ฅ ๊ฒฐ๊ณผ๋ฅผ ํŒ๋‹จํ•˜์„ธ์š”:

for (var i = 1; i <= 5; i++) {
setTimeout(function () {
console.log(i);
}, 0);
}

์˜ค๋‹ต (ํ”ํ•œ ์˜คํ•ด)โ€‹

๋งŽ์€ ์‚ฌ๋žŒ๋“ค์ด ์ถœ๋ ฅ์ด 1 2 3 4 5๋ผ๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

์‹ค์ œ ์ถœ๋ ฅโ€‹

6
6
6
6
6

์™œ ๊ทธ๋Ÿด๊นŒ?โ€‹

์ด ๋ฌธ์ œ๋Š” ์„ธ ๊ฐ€์ง€ ํ•ต์‹ฌ ๊ฐœ๋…๊ณผ ๊ด€๋ จ๋ฉ๋‹ˆ๋‹ค:

1. var์˜ ํ•จ์ˆ˜ ์Šค์ฝ”ํ”„

// var๋Š” ๋ฃจํ”„ ๋‚ด์—์„œ ๋ธ”๋ก ์Šค์ฝ”ํ”„๋ฅผ ์ƒ์„ฑํ•˜์ง€ ์•Š์Œ
for (var i = 1; i <= 5; i++) {
// i๋Š” ์™ธ๋ถ€ ์Šค์ฝ”ํ”„์— ์žˆ์œผ๋ฉฐ, ๋ชจ๋“  ์ดํ„ฐ๋ ˆ์ด์…˜์ด ๊ฐ™์€ i๋ฅผ ๊ณต์œ 
}
console.log(i); // 6 (๋ฃจํ”„ ์ข…๋ฃŒ ํ›„ i์˜ ๊ฐ’)

// var์˜ ๊ฒฝ์šฐ
{
var i;
i = 1;
i = 2;
i = 3;
i = 4; // ๋ฃจํ”„ ์ข…๋ฃŒ
}

2. setTimeout์˜ ๋น„๋™๊ธฐ ์‹คํ–‰

// setTimeout์€ ๋น„๋™๊ธฐ๋กœ, ํ˜„์žฌ ๋™๊ธฐ ์ฝ”๋“œ ์‹คํ–‰์ด ์™„๋ฃŒ๋œ ํ›„์— ์‹คํ–‰๋จ
for (var i = 1; i <= 5; i++) {
setTimeout(function () {
// ์ด ์ฝ”๋“œ๋Š” Event Loop์˜ ํƒœ์Šคํฌ ํ์— ๋“ค์–ด๊ฐ
console.log(i);
}, 0);
}
// ๋ฃจํ”„๊ฐ€ ๋จผ์ € ์™„๋ฃŒ๋˜๊ณ (i๊ฐ€ 6์ด ๋จ), ๊ทธ ๋‹ค์Œ์— setTimeout ์ฝœ๋ฐฑ์ด ์‹คํ–‰๋จ

3. Closure ์ฐธ์กฐ

// ๋ชจ๋“  setTimeout ์ฝœ๋ฐฑ ํ•จ์ˆ˜๊ฐ€ ๊ฐ™์€ i๋ฅผ ์ฐธ์กฐํ•จ
// ์ฝœ๋ฐฑ์ด ์‹คํ–‰๋  ๋•Œ, i๋Š” ์ด๋ฏธ 6์ด ๋˜์–ด ์žˆ์Œ

ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•โ€‹

๋ฐฉ๋ฒ• 1: let ์‚ฌ์šฉ (๊ถŒ์žฅ) โ˜…

for (let i = 1; i <= 5; i++) {
setTimeout(function () {
console.log(i);
}, 0);
}
// ์ถœ๋ ฅ: 1 2 3 4 5

// let์˜ ๊ฒฝ์šฐ
{
let i = 1; // ์ฒซ ๋ฒˆ์งธ ์ดํ„ฐ๋ ˆ์ด์…˜์˜ i
}
{
let i = 2; // ๋‘ ๋ฒˆ์งธ ์ดํ„ฐ๋ ˆ์ด์…˜์˜ i
}
{
let i = 3; // ์„ธ ๋ฒˆ์งธ ์ดํ„ฐ๋ ˆ์ด์…˜์˜ i
}

์›๋ฆฌ: let์€ ๋งค ์ดํ„ฐ๋ ˆ์ด์…˜๋งˆ๋‹ค ์ƒˆ๋กœ์šด ๋ธ”๋ก ์Šค์ฝ”ํ”„๋ฅผ ์ƒ์„ฑํ•˜์—ฌ, ๊ฐ setTimeout ์ฝœ๋ฐฑ์ด ํ˜„์žฌ ์ดํ„ฐ๋ ˆ์ด์…˜์˜ i ๊ฐ’์„ ์บก์ฒ˜ํ•ฉ๋‹ˆ๋‹ค.

// ๋“ฑ๊ฐ€
{
let i = 1;
setTimeout(function () {
console.log(i);
}, 0);
}
{
let i = 2;
setTimeout(function () {
console.log(i);
}, 0);
}
// ... ์ดํ•˜ ๋™์ผ

๋ฐฉ๋ฒ• 2: IIFE (์ฆ‰์‹œ ์‹คํ–‰ ํ•จ์ˆ˜) ์‚ฌ์šฉ

for (var i = 1; i <= 5; i++) {
(function (j) {
setTimeout(function () {
console.log(j);
}, 0);
})(i);
}
// ์ถœ๋ ฅ: 1 2 3 4 5

์›๋ฆฌ: IIFE๊ฐ€ ์ƒˆ๋กœ์šด ํ•จ์ˆ˜ ์Šค์ฝ”ํ”„๋ฅผ ์ƒ์„ฑํ•˜๋ฉฐ, ๋งค ์ดํ„ฐ๋ ˆ์ด์…˜๋งˆ๋‹ค ํ˜„์žฌ์˜ i ๊ฐ’์ด ๋งค๊ฐœ๋ณ€์ˆ˜ j๋กœ ์ „๋‹ฌ๋˜์–ด Closure๋ฅผ ํ˜•์„ฑํ•ฉ๋‹ˆ๋‹ค.

๋ฐฉ๋ฒ• 3: setTimeout์˜ ์„ธ ๋ฒˆ์งธ ์ธ์ˆ˜ ์‚ฌ์šฉ

for (var i = 1; i <= 5; i++) {
setTimeout(
function (j) {
console.log(j);
},
0,
i
); // ์„ธ ๋ฒˆ์งธ ์ธ์ˆ˜๊ฐ€ ์ฝœ๋ฐฑ ํ•จ์ˆ˜์— ์ „๋‹ฌ๋จ
}
// ์ถœ๋ ฅ: 1 2 3 4 5

์›๋ฆฌ: setTimeout์˜ ์„ธ ๋ฒˆ์งธ ์ธ์ˆ˜ ์ดํ›„๋Š” ์ฝœ๋ฐฑ ํ•จ์ˆ˜์˜ ์ธ์ˆ˜๋กœ ์ „๋‹ฌ๋ฉ๋‹ˆ๋‹ค.

๋ฐฉ๋ฒ• 4: bind ์‚ฌ์šฉ

for (var i = 1; i <= 5; i++) {
setTimeout(
function (j) {
console.log(j);
}.bind(null, i),
0
);
}
// ์ถœ๋ ฅ: 1 2 3 4 5

์›๋ฆฌ: bind๋Š” ์ƒˆ๋กœ์šด ํ•จ์ˆ˜๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ํ˜„์žฌ์˜ i ๊ฐ’์„ ์ธ์ˆ˜๋กœ ๋ฐ”์ธ๋”ฉํ•ฉ๋‹ˆ๋‹ค.

๋ฐฉ๋ฒ• ๋น„๊ตโ€‹

๋ฐฉ๋ฒ•์žฅ์ ๋‹จ์ ๊ถŒ์žฅ๋„
let๊ฐ„๊ฒฐ, ํ˜„๋Œ€์ , ์ดํ•ดํ•˜๊ธฐ ์‰ฌ์›€ES6+5/5 ๊ฐ•๋ ฅ ๊ถŒ์žฅ
IIFEํ˜ธํ™˜์„ฑ ์ข‹์Œ๊ตฌ๋ฌธ์ด ๋ณต์žก3/5 ๊ณ ๋ ค ๊ฐ€๋Šฅ
setTimeout ์ธ์ˆ˜๊ฐ„๋‹จํ•˜๊ณ  ์ง์ ‘์ ์ž˜ ์•Œ๋ ค์ง€์ง€ ์•Š์Œ4/5 ๊ถŒ์žฅ
bindํ•จ์ˆ˜ํ˜• ์Šคํƒ€์ผ๊ฐ€๋…์„ฑ์ด ๋‹ค์†Œ ๋–จ์–ด์ง3/5 ๊ณ ๋ ค ๊ฐ€๋Šฅ

์‹ฌํ™” ๋ฌธ์ œโ€‹

Q1: ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋ณ€๊ฒฝํ•˜๋ฉด?

for (var i = 1; i <= 5; i++) {
setTimeout(function () {
console.log(i);
}, i * 1000);
}

๋‹ต: ๋งค์ดˆ 6์ด ์ถœ๋ ฅ๋˜๋ฉฐ, ์ด 5๋ฒˆ ์ถœ๋ ฅ๋ฉ๋‹ˆ๋‹ค(๊ฐ๊ฐ 1์ดˆ, 2์ดˆ, 3์ดˆ, 4์ดˆ, 5์ดˆ์— ์ถœ๋ ฅ).

Q2: ๋งค์ดˆ ์ˆœ์„œ๋Œ€๋กœ 1, 2, 3, 4, 5๋ฅผ ์ถœ๋ ฅํ•˜๋ ค๋ฉด?

for (let i = 1; i <= 5; i++) {
setTimeout(function () {
console.log(i);
}, i * 1000);
}
// 1์ดˆ ํ›„ 1 ์ถœ๋ ฅ
// 2์ดˆ ํ›„ 2 ์ถœ๋ ฅ
// 3์ดˆ ํ›„ 3 ์ถœ๋ ฅ
// 4์ดˆ ํ›„ 4 ์ถœ๋ ฅ
// 5์ดˆ ํ›„ 5 ์ถœ๋ ฅ

๋ฉด์ ‘ ํฌ์ธํŠธโ€‹

์ด ๋ฌธ์ œ๊ฐ€ ํ…Œ์ŠคํŠธํ•˜๋Š” ๊ฒƒ:

  1. var์˜ ์Šค์ฝ”ํ”„: ํ•จ์ˆ˜ ์Šค์ฝ”ํ”„ vs ๋ธ”๋ก ์Šค์ฝ”ํ”„
  2. Event Loop: ๋™๊ธฐ vs ๋น„๋™๊ธฐ ์‹คํ–‰
  3. Closure: ํ•จ์ˆ˜๊ฐ€ ์™ธ๋ถ€ ๋ณ€์ˆ˜๋ฅผ ์–ด๋–ป๊ฒŒ ์บก์ฒ˜ํ•˜๋Š”์ง€
  4. ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•: ๋‹ค์–‘ํ•œ ํ•ด๋ฒ•๊ณผ ์žฅ๋‹จ์  ๋น„๊ต

๋‹ต๋ณ€ ์‹œ ๊ถŒ์žฅ์‚ฌํ•ญ:

  • ๋จผ์ € ์ •๋‹ต์„ ๋งํ•จ (6 6 6 6 6)
  • ์ด์œ ๋ฅผ ์„ค๋ช…ํ•จ (var ์Šค์ฝ”ํ”„ + setTimeout ๋น„๋™๊ธฐ)
  • ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•์„ ์ œ์‹œํ•จ (let ์šฐ์„ , ๋‹ค๋ฅธ ๋ฐฉ๋ฒ•๋„ ์„ค๋ช…)
  • JavaScript ๋‚ด๋ถ€ ๋ฉ”์ปค๋‹ˆ์ฆ˜์— ๋Œ€ํ•œ ์ดํ•ด๋ฅผ ๋ณด์—ฌ์คŒ

๋ชจ๋ฒ” ์‚ฌ๋ก€โ€‹

  1. ์šฐ์„ ์ ์œผ๋กœ const ์‚ฌ์šฉ: ์žฌํ• ๋‹น์ด ํ•„์š” ์—†๋Š” ๋ณ€์ˆ˜์—๋Š” const๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ฝ”๋“œ์˜ ๊ฐ€๋…์„ฑ๊ณผ ์œ ์ง€๋ณด์ˆ˜์„ฑ์„ ๋†’์ž…๋‹ˆ๋‹ค.
  2. ๋‹ค์Œ์œผ๋กœ let ์‚ฌ์šฉ: ์žฌํ• ๋‹น์ด ํ•„์š”ํ•œ ๊ฒฝ์šฐ let์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
  3. var ์‚ฌ์šฉ์„ ํ”ผํ•จ: var์˜ ์Šค์ฝ”ํ”„์™€ Hoisting ๋™์ž‘์ด ์˜ˆ์ƒ์น˜ ๋ชปํ•œ ๋ฌธ์ œ๋ฅผ ์ผ์œผํ‚ฌ ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ, ํ˜„๋Œ€ JavaScript ๊ฐœ๋ฐœ์—์„œ๋Š” ์‚ฌ์šฉ์„ ํ”ผํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค.
  4. ๋ธŒ๋ผ์šฐ์ € ํ˜ธํ™˜์„ฑ ์ฃผ์˜: ๊ตฌํ˜• ๋ธŒ๋ผ์šฐ์ €๋ฅผ ์ง€์›ํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ, Babel ๋“ฑ์˜ ๋„๊ตฌ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ let๊ณผ const๋ฅผ var๋กœ ํŠธ๋žœ์ŠคํŒŒ์ผํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ด€๋ จ ์ฃผ์ œโ€‹