1.4.1 클로저의 정의
MDN 의 클로저 정의 : 함수와 함수가 선언된 어휘적 환경의 조합
ㄴ 변수가 코드 내부에서 어디에 선언됐는지
클로저는 어휘적 환경을 조합해 코딩하는 기법이다.
1.4.2 변수의 유효 범위, 스코프
스코프는 변수의 유효범위이다.
전역 스코프
- 전역 레벨에 선언하는 것이다.
- 이 스코프에서 변수를 선언하면 어디서든 호출할 수 있다.
var global = 'global scope'
function hello() {
console.log(global)
}
console.log(global) // global scope
hello() // global scope
console.log(global === window.global) // true
전역 스코프에 변수를 선언하면 코드 전체 어디에서든 그 변수를 쓸 수 있게 되고,
브라우저에서는 window 객체가 전역 변수들을 관리하는 역할을 한다.
함수 스코프
- 자바스크립트는 기본적으로 함수 레벨 스코프를 따른다.
- {} 블록이 스코프 범위를 결정하지 않는다.
if (true) {
var global = 'global scope'
}
console.log(global) //'global scope'
console.log(global === window.global) //true
var global은 분명 {} 내부에 선언돼 있는데, {} 밖에서도 접근 가능하다.
>> 자바스크립트는 함수 레벨 스코프를 가지고 있기 때문이다.
1.4.3 클로저의 활용
전역 스코프는 어디서든 원하는 값을 꺼내올 수 있다는 장점이 있지만,
반대로는 누구든 접근할 수 있고 수정할 수 있다는 뜻도 된다.
var counter = 0
function handleClick() {
counter++
}
위 코드는 전역 레벨에 선언되어 있어 누구나 수정할 수 있다.
자바스크립트를 조금만 아는 사람이라면 누구나 리액트 애플리케이션을 쉽게 망가뜨릴 수 있을 것이다.
그래서 리액트가 관리하는 내부 상태 값은 리액트가 별도로 관리하는 클로저 내부에서만 접근할 수 있다.
function Counter() {
var counter = 0
return {
increase: function () {
return ++counter
},
decrease: function () {
return --counter
},
counter: function () {
console.log('counter에 접근!')
return counter
},
}
}
var c = Counter()
console.log(c.increase()) // 1
console.log(c.increase()) // 2
console.log(c.increase()) // 3
console.log(c.decrease()) // 2
console.log(c.counter()) // 2
클로저를 활용하면 전역 스코프의 사용을 막고, 개발자가 원하는 정보만 개발자가 원하는 방향으로 노출시킬 수 있다.
리액트에서의 클로저
리액트 함수 컴포넌트 훅에서 클로저는 어떻게 사용될까?
대표적인 예로 useState를 들어보자.
function Component() {
const [state, setState] = useState()
function handleClick() {
//useState호출은 위에서 끝났지만
//setState는 계속 내부의 최신값(prev)을 알고 있다.
//이는 클로저를 활용했기 때문에 가능하다.
setState((prev) => prev + 1)
}
}
외부 함수(useState)가 반환한 내부 함수(setState)는 외부함수(useState)의 호출이 끝났음에도 자신이 선언된 외부 함수가 선언된 환경(state가 저장돼 있는 어딘가)을 기억하기 때문에 계속해서 state 값을 사용할 수 있다.
1.4.4 주의할 점
for (var i = 0; i < 5; i++) {
setTimeout(function () {
console.log(i)
}, i * 1000)
}
위의 코드의 의도는 0부터 시작해 1초 간격으로 0,1,2,3,4를 차례대로 출력하는 것이다.
그러나 실제로 실행하면 0,1,2,3,4 초 뒤에 5만 출력된다.
왜 ?
i가 전역 변수로 작동하기 때문이다.
for 문을 다 순회한 이후, 태스크 큐에 있는 setTimeout을 실행하려고 했을 때, 이미 전역 레벨에 있는 i는 5로 업데이트 되어 있다.
올바르게 수정하는 방법 1
함수 레벨 스코프가 아닌 블록 레벨 스코프를 갖는 let으로 수정하는 것.
for (let i = 0; i < 5; i++) {
setTimeout(function () {
console.log(i)
}, i * 1000)
}
let은 블록 레벨 스코프를 가지기 때문에 let i가 for 문을 순회하면서 각각의 스코프를 갖게 된다.
클로저의 기본 개념인 '함수와 함수가 선언된 어휘적 환경의 조합'을 주의 깊게 살펴봐야 클로저를 제대로 활용할 수 있다.
주의할 점
- 클로저를 사용하는 데는 비용이 든다.
-생성될 때 마다 선언적 환경을 기억해야 하므로 추가로 비용이 발생한다
외부 함수를 기억하고 이를 내부 함수에서 가져다 쓰는 메커니즘은 성능에 영향을 미친다.
'모던 리액트 Deep Dive 스터디' 카테고리의 다른 글
리액트에서 자주 사용하는 자바스크립트 문법 (0) | 2024.11.07 |
---|---|
이벤트 루프와 비동기 통신의 이해 (0) | 2024.11.06 |
리액트 개발을 위해 꼭 알아야 할 자바스크립트 - 클래스 (4) | 2024.11.05 |
리액트 개발을 위해 꼭 알아야 할 자바스크립트 - 함수 (3) | 2024.11.05 |
자바스크립트의 동등 비교 (1) | 2024.11.04 |