어제는 OT 였고, 오늘이 첫 강의 시작이었다.
오늘 배운 내용은 변수, 상수, 자료형, 메모리, 표현식, 연산자, 흐름제어, 배열/객체, 스코프, 클로저 대부분 완벽히는 아니지만 어느정도 아는 내용이었다.
그 중, 잘 몰랐던 클로저에 대해 부족한 내용은 코어 자바스크립트 책과 구글링을 통해 학습하였다.
클로저란 함수가 선언된 환경의 스코프를 기억하여 함수가 스코프 밖에서 실행될 때에도 기억한 스코프에 접근할 수 있게 만드는 문법이다.
function a(name) {
const greet = 'hello, '; // 지역 스코프라 함수가 종료되면 메모리에서 사라짐.
return function () {
console.log(greet + name);
};
};
const world = a("world");
const ik = a("ik");
world(); // hello, world
ik(); // hello, ik
전역 Lexical 환경에는 현재 각각 a:(function)
과 world:(function)
, ik(function)
이 들어있다.
a의 Lexical 환경에는 greet: ‘hello, ‘
가 들어있고,
a의 블록안에 존재하는 익명함수 Lexical 환경에는 선언된 것이 없다.
이제 코드의 결과를 봐보자.
순서대로 hello, world와 hello, ik이 출력될 것이다.
분명 가바지 컬렉터에 의해 greet 변수는 world와 ik에 익명함수를 리턴 함과 동시에 사라져야하는데 말이다.
하지만 이 상황에선 클로저로 인해 함수가 선언된 환경의 스코프를 기억하여 world와 ik이 참조하는 greet는 각각 “world”와 “ik”으로 메모리에서는 사라졌지만 선언될 당시의 스코프를 기억하여 각각 hello, world와 hello, ik을 출력한 것이다.
위 코드의 Lexical 환경이 참조하는 순서는 아래와 같다.
💡 익명함수 Lexical 환경 →(참조) a Lexical 환경 →(참조) 전역 Lexical 환경
위 순서로 현재 스코프에 존재하지 않는 변수를 상위 스코프(Lexical 환경)에서 찾는다.
이어서 다음 코드의 결과에 대해서도 고민해보자.
function count() {
let i = 0;
for (i = 0; i < 5; i ++) {
setTimeout(function () {
console.log(i);
}, i * 100);
};
};
count();
처음에 setTimeout 안의 익명함수의 존재를 잊어버리고 당연하게 결과를 0, 1, 2, 3, 4가 나올 것이라 예상 하였지만, 애석 하게도 답은 5, 5, 5, 5, 5다.
클로저 중에서도 setTimeout을 사용한 부분이 가장 어려웠는데, 반복문의 상황에 대해 적어보겠다.
첫 번째 반복문 상황 → i의 값은 0이고, timer 함수가 작동하여 100ms 뒤, i의 값을 콘솔에 출력하도록 명령.
두 번째 반복문 상황 → i의 값은 1이고, timer 함수가 작동하여 100ms 뒤, i의 값을 콘솔에 출력하도록 명령.
세 번째 반복문 상황 → i의 값은 2이고, timer 함수가 작동하여 100ms 뒤, i의 값을 콘솔에 출력하도록 명령.
네 번째 반복문 상황 → i의 값은 3이고, timer 함수가 작동하여 100ms 뒤, i의 값을 콘솔에 출력하도록 명령.
마지막 반복문 상황 → i의 값은 4이고, timer 함수가 작동하여 100ms 뒤, i의 값을 콘솔에 출력하도록 명령.
💡 이 상황에서let i = 0;
for (i = 0; i < 5; i++) {
(function(){
var innerI = i;
setTimeout(function timer() {
console.log(innerI);
}, i * 100);
})();
}
// 0, 1, 2, 3, 4
for (let i = 0; i < 5; i++) {
setTimeout(function timer() {
console.log(i);
}, i * 100);
}
// 0, 1, 2, 3, 4
상태를 안전하게 변경하고 유지하기 위해 사용한다.
다시 말해, 상태가 의도치 않게 변경되지 않도록 상태를 안전하게 은닉하고 특정 함수에게만 상태 변경을 허용하기 위해 사용한다.
클로저에 대해서는 코어 자바스크립트 책을 보며 어느 정도 익혔다고 생각했는데 오산이었다. 개발 공부를 할 때 겉핥기식으로 공부하는 것이 아니라 깊게 파고들어야겠다고 생각했고, 오늘 JS에 대한 기본적인 내용의 강의를 들으며 더욱 기본에 충실해야겠다는 느낌을 받았다.