let a = 10;
{
let b = 20;
}
console.log(a, b); // b로 인한 에러
var a = 10;
{
var b = 20;
}
function fn() {
var c = 30;
}
console.log(a, b, c); // c로 인한 에러
실행 컨텍스트가 콜스택에 존재할 때 이는 메모리에 실행 컨텍스트가 적재되어 있다는 것을 의미한다.
🔎 가비지 컬렉터란?
메모리에서 더 이상 사용되지 않는 객체를 자동으로 감지하고, 해당 메모리를 회수하여 메모리에서 삭제해주는 시스템
🔎 가비지 컬렉션이란?
가비지 컬렉터에 의해 메모리가 회수되는 과정
🔎 클로저란?
함수가 자신이 생성된 스코프에 대한 참조를 기억하고 유지하는 능력
🔎 은닉화란?
외부에서 직접 접근할 수 없는 변수를 만들어, 해당 변수를 내부 함수에서만 접근 및 수정할 수 있도록 하는 것
function counter() {
let count = 0; // 자유 변수
return {
increment: function() {
count++; // 자유 변수 참조
return count;
},
decrement: function() {
count--;
return count;
},
};
}
let mycount = counter();
console.log(mycount.increment());
// 더이상 사용하지 않으므로 가비지 컬렉터 대상으로 만들기
mycount = null;
🔎 함수 팩토리란?
클로저를 활용하여 여러 개의 함수를 생성하는 패턴
function makeMultiple(multiplier) {
// 동적으로 함수 생성
return function (x) {
return x * multipler;
};
}
let double = makeMultiple(2);
let triple = makeMultiple(3);
console.log(double(5)); // 5 * 2
console.log(triple(5)); // 5 * 3
// 가비지 컬렉터 대상으로 만들기
double = null;
triple = null;
🔎 비동기 프로그래밍에서 클로저란?
콜백 함수가 실행될 때 사용할 변수의 값을 유지하거나 기억하는 데 유용함
function fetchData(url) {
let result; // 클로저 대상
return function (callback) {
setTimeout(() => {
result = "Fetched... Success";
callback(result);
}, 1000);
}
let fetchFromNaver = fetchData("https://www.naver.com");
fetchFromNaver((data) => console.log(data));
// 가비지 컬렉터 대상으로 만들기
fetchFromNaver = null;
function createTimer() {
for (let i = 1; i <= 3; i++) {
// setTimeout은 비동기 함수
setTimeout(function() {
console.log(`Timer ${i}초 후`);
}, i * 1000);
}
}
// createTimer가 종료되었음에도 i값을 참조하여 콜백함수 실행
createTimer();
실은 위에서 비동기 프로그래밍에서 클로저가 왜 필요한지 크게 와닿지 않았는데,
실행 컨텍스트와 엮어 생각해보니 이해가 잘 되었다.
1분의 시간이 소요되는 API 요청을 해야한다고 가정하자.
비동기 프로그래밍을 사용하지 않는다면, 우리는 API 요청 후 1분동안 결과가 올 때까지 기다려야 한다.
그리고 1분동안은 아무것도 할 수가 없다.
그런데 해야할 작업이 많은 와중에 1분동안 가만히 있을 수는 없다!! (비동기를 하고 싶다!)
그러기 위해서는 API 요청 내용이 담긴 실행 컨텍스트가 콜스택에서 제거되어야 한다.
그러나 콜스택에서 제거되면 메모리 역시 제거되므로 API 요청의 결과에 접근할 수 없다.
이럴 때 클로저가 필요하다.
클로저를 통해 비동기 작업이 완료될 때까지 필요한 변수(API 요청 결과를 받아낼 변수)를 참조하며 기억하는 것이다.
그리고 보통 그 참조를 기억하는 것이 콜백함수(이럴 땐 비동기 작업의 결과를 처리하는 로직일 듯)이다.
참조한 변수에 API 요청에 대한 결과를 받아오면 콜백함수의 실행 컨텍스트가 생성되며 실행된다.
오해하지 말아야 할 것은, 콜백함수가 참조한 변수에 대해 값의 변경을 인지하는 능력이 있는 것은 아니고,
자바스크립트 엔진의 이벤트 루프가 모든 동기 작업을 완료한 뒤 비동기 작업의 결과가 반환되었는지를 확인한 후
콜백 함수를 실행시켜주는 것이다.
그리고 비동기 함수에서 꼭 클로저가 발생하는 것은 아니다!
비동기 함수 내의 변수를 참조를 하냐 마냐에 달렸다.
또한, 꼭 콜백함수가 참조해서 클로저가 발생하지 않는다. 다른 내부 함수일 수도 있다.
🔎 메모이제이션 패턴이란?
함수의 결과를 저장하여 동일한 인수로 다시 호출할 때 계산을 반복하지 않고 저장된 결과를 반환하는 최적화 기법
function memoization(fn) {
const cache = {}; // 클로저 대상
return function (...args) {
const key = JSON.stringify(args);
if (cache[key]) return cache[key];
cache[key] = fn(...args);
return cache[key];
};
}
function slowFunction(num) {
for (let i = 0; i < 9999999999; i++);
return num * 2;
}
let memoizedFn = memoization(slowFunction);
console.log(memoizedFn(5)); // 첫 실행에서 출력까지 시간이 걸림
console.log(memoizedFn(5)); // 바로 출력됨
// 가비지 컬렉터 대상으로 만들기
memoizedFn = null;
자바스크립트 엔진 자체에서 실행되는 것이 아닌, 브라우저나 Node.js 환경에서 제공하는 타이머 API의 일부
동작 흐름
콜백 함수 등록
setTimeout 호출 -> 콜백 함수를 브라우저의 타이머 API나 Node.js 타이머 API에 등록
타이머 시작
타이머 API는 지정된 시간만큼 대기 -> 시간 지나면 콜백 함수를 태스크 큐에 추가
이벤트 루프와 콜백 함수 실행
이벤트 루프는 메인 스레드에서 실행 중인 모든 동기 작업이 종료될 때까지 대기 (자바스크립트는 싱글 스레드라서)
-> 이벤트 루프가 태스크 큐에서 준비된 콜백을 호출 스택에 올려 실행