UI 공부

JS의 또 다른 특징들

Rocomi 2024. 8. 17. 18:00

JavaScript는 유연하고 강력한 프로그래밍 언어로, 객체, 배열, 함수, 이벤트, 정규표현식 외에도 다양한 중요한 개념들이 존재합니다.

여기에서는 호이스팅(hoisting), 스코프(scope), 클로저(closure), this 바인딩 등 JavaScript의 중요한 특징들을 설명하겠습니다.

1. 호이스팅 (Hoisting)

**호이스팅(hoisting)**은 JavaScript에서 변수 선언과 함수 선언이 코드 실행 전에 상단으로 끌어올려지는 동작을 말합니다. 하지만 변수 초기화는 호이스팅되지 않고, 선언만 호이스팅됩니다.

예제 코드:

1
2
3
4
5
6
7
8
9
10
11
console.log(x); // undefined (변수 선언이 호이스팅되었으나 초기화는 되지 않음)
var x = 5;
console.log(x); // 5
 
// 함수 호이스팅
hoistedFunction(); // "이 함수는 호이스팅되었습니다!"
 
function hoistedFunction() {
    console.log("이 함수는 호이스팅되었습니다!");
}
 
cs

위의 예제에서 var로 선언된 변수 x는 선언 부분만 호이스팅되어 undefined로 평가됩니다. 반면 함수 선언은 전체가 호이스팅되어 함수 호출이 가능합니다.

주의: let과 const로 선언된 변수는 호이스팅되지만, Temporal Dead Zone (TDZ) 때문에 선언 전에 참조할 수 없습니다.

예제 코드 (TDZ):

1
2
3
console.log(y); // ReferenceError: y is not defined
let y = 10;
 
cs

2. 스코프 (Scope)

**스코프(scope)**는 변수가 접근 가능한 영역을 의미합니다. JavaScript에는 전역 스코프(Global Scope)와 지역 스코프(Local Scope)가 있으며, ES6 이후에는 블록 스코프(Block Scope)가 추가되었습니다.

1) 전역 스코프 (Global Scope)

전역 스코프에 선언된 변수는 어디서든 접근 가능합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
var globalVar = "전역 변수";
 
function testFunction() {
    console.log(globalVar); // "전역 변수"
}
testFunction();var globalVar = "전역 변수";
 
function testFunction() {
    console.log(globalVar); // "전역 변수"
}
testFunction();
 
 
cs

2) 지역 스코프 (Local Scope)

함수 내에서 선언된 변수는 함수 내에서만 접근 가능합니다.

1
2
3
4
5
6
7
8
function testFunction() {
    var localVar = "지역 변수";
    console.log(localVar); // "지역 변수"
}
testFunction();
 
console.log(localVar); // ReferenceError: localVar is not defined
 
cs

3) 블록 스코프 (Block Scope)

let과 const로 선언된 변수는 블록 스코프를 가지며, 중괄호 {}로 감싸인 블록 내에서만 접근 가능합니다.

1
2
3
4
5
6
7
if (true) {
    let blockVar = "블록 변수";
    console.log(blockVar); // "블록 변수"
}
 
console.log(blockVar); // ReferenceError: blockVar is not defined
 
cs

3. 클로저 (Closure)

**클로저(closure)**는 함수가 선언될 때의 스코프를 기억하는 함수입니다. 클로저는 함수가 생성될 때 주변 스코프를 "닫아둔" 상태로, 그 함수가 생성될 당시의 변수들을 기억합니다.

예제 코드:

1
2
3
4
5
6
7
8
9
10
11
12
13
function outerFunction() {
    let outerVar = "외부 변수";
 
    function innerFunction() {
        console.log(outerVar); // "외부 변수"
    }
 
    return innerFunction;
}
 
const closure = outerFunction();
closure(); // "외부 변수"
 
cs

innerFunction은 outerFunction의 스코프에서 선언되었기 때문에, outerFunction이 종료된 이후에도 outerVar에 접근할 수 있습니다. 이처럼 클로저는 상태를 유지할 수 있는 강력한 방법입니다.

4. this 바인딩

this는 함수가 호출될 때 동적으로 결정되는 객체를 가리킵니다. this가 가리키는 대상은 함수 호출 방식에 따라 달라집니다.

1) 전역 컨텍스트에서의 this

전역 컨텍스트에서 this는 전역 객체(window 또는 global)를 가리킵니다.

1
2
console.log(this === window); // true (브라우저 환경)
 
cs

2) 객체 메서드에서의 this

객체의 메서드에서 this는 해당 메서드를 호출한 객체를 가리킵니다.

1
2
3
4
5
6
7
8
9
const person = {
    name"John",
    greet: function() {
        console.log(this.name); // "John"
    }
};
 
person.greet();
 
cs

3) 함수에서의 this

일반 함수에서의 this는 엄격 모드에서는 undefined이고, 비엄격 모드에서는 전역 객체를 가리킵니다.

1
2
3
4
5
function regularFunction() {
    console.log(this);
}
regularFunction(); // window (비엄격 모드) 또는 undefined (엄격 모드)
 
cs

4) 화살표 함수에서의 this

화살표 함수는 this 바인딩을 가지지 않고, 외부 스코프의 this를 상속받습니다.

1
2
3
4
5
6
7
8
9
const obj = {
    name"John",
    arrowFunction: () => {
        console.log(this.name); // undefined (전역 스코프의 this)
    }
};
 
obj.arrowFunction();
 
cs

5. 프로토타입 (Prototype)

**프로토타입(prototype)**은 JavaScript의 객체가 다른 객체로부터 속성과 메서드를 상속받을 수 있게 해주는 메커니즘입니다. 모든 객체는 프로토타입 객체를 가지고 있으며, 이 프로토타입을 통해 상속이 이루어집니다.

예제 코드:

 
1
2
3
4
5
6
7
8
9
10
11
function Person(name) {
    this.name = name;
}
 
Person.prototype.greet = function() {
    console.log("Hello, my name is " + this.name);
};
 
const john = new Person("John");
john.greet(); // "Hello, my name is John"
 
cs

6. 비동기 처리 (Asynchronous Programming)

JavaScript는 싱글 스레드 언어로, 비동기 작업을 처리하기 위해 이벤트 루프와 콜백 큐를 사용합니다. 비동기 처리의 주요 방식은 콜백(callback), 프로미스(Promise), 그리고 async/await입니다.

1) 콜백 (Callback)

콜백은 함수가 다른 함수의 인자로 전달되어, 특정 작업이 완료된 후 호출되는 함수입니다.

1
2
3
4
5
6
7
8
9
10
function fetchData(callback) {
    setTimeout(() => {
        callback("데이터를 불러왔습니다");
    }, 1000);
}
 
fetchData(data => {
    console.log(data); // "데이터를 불러왔습니다"
});
 
cs

2) 프로미스 (Promise)

프로미스는 비동기 작업의 성공 또는 실패를 나타내는 객체입니다.

1
2
3
4
5
6
7
8
9
10
11
12
let promise = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve("성공적으로 완료되었습니다!");
    }, 1000);
});
 
promise.then(result => {
    console.log(result); // "성공적으로 완료되었습니다!"
}).catch(error => {
    console.log(error);
});
 
cs

3) async/await

async/await은 프로미스를 더 쉽게 사용하기 위한 문법으로, 비동기 코드를 동기 코드처럼 작성할 수 있게 해줍니다.

1
2
3
4
5
6
7
8
9
async function fetchData() {
    let result = await new Promise((resolve) => {
        setTimeout(() => resolve("데이터를 불러왔습니다"), 1000);
    });
    console.log(result); // "데이터를 불러왔습니다"
}
 
fetchData();
 
cs

정리

  • 호이스팅: 변수와 함수 선언이 코드 상단으로 끌어올려지는 현상.
  • 스코프: 변수의 접근 범위를 정의하는 영역.
  • 클로저: 함수가 생성될 때의 스코프를 기억하여 함수가 종료된 후에도 해당 스코프에 접근할 수 있는 기능.
  • this 바인딩: 함수 호출 방식에 따라 동적으로 결정되는 객체 참조.
  • 프로토타입: 객체 지향 프로그래밍에서 상속을 구현하는 메커니즘.
  • 비동기 처리: JavaScript에서 비동기 작업을 관리하는 다양한 방법들.

이러한 개념들을 잘 이해하고 활용하면 JavaScript를 더욱 강력하게 사용할 수 있습니다.