일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- 자바스크립트코딩테스트
- JS
- react
- 알고리즘문제풀이
- 프로그래머스
- 리액트쿼리
- 자바스크립트
- Baekjoon
- 자바스크립트 문제풀이
- 자바스크립트 알고리즘
- 리액트
- 프론트엔드
- lodash
- NPM
- leetcode
- 자바스크립트 문제 풀이
- JavaScript
- 자바스크립트 연결리스트
- next13
- stack문제
- Next
- HTML
- 제로베이스
- til
- 자바스크립트 문제
- 자바스크립트 알고리즘 문제
- Next.js13
- 타입스크립트
- CSS
- leetcode문제풀이
- Today
- Total
코드노트
자바스크립트 클로저(Closure) 정리 본문
클로저(Closure)
- 함수와 함수가 선언된 어휘적 환경(렉시컬 환경)의 조합
- 외부 함수보다 충첩함수가 더 오래 유지되는 경우 중첩 함수는 이미 생명주기가 종료한 외부 함수의 변수를 참조
- 중첩 함수 == 클로저
-> 내부함수가 외부함수 변수에 접근 가능
function init() {
// name은 init에 의해 생성된 지역 변수
var name = "Mozilla";
// displayName() 은 내부 함수이며, 클로저
function displayName() {
// 부모 함수에서 선언된 변수를 사용
alert(name);
}
displayName();
}
init();
쉽게 이야기하자면 함수안에 함수를 만들었을때 그 외부에 있는 변수를 사용할 수 있다.
- 부모 함수에서 선언된 변수를 사용하는 것!
function makeFunc() {
var name = "Mozilla";
function displayName() {
alert(name);
}
return displayName;
}
var myFunc = makeFunc();
//myFunc변수에 displayName을 리턴함
//유효범위의 어휘적 환경을 유지
myFunc();
//리턴된 displayName 함수를 실행(name 변수에 접근)
- 외부함수의 환경을 기억한다.
- 클로저는 반환된 내부함수가 자신이 선언되었을 때 환경의 스코프를 기억하여
자신이 선언되었을 때 환경밖에서 호출되어도 그 환경을 접근할 수 있는 함수를 의미한다.
* 이 같은 현상이 발생하는 이유는 자바스크립트가 렉시컬 스코프를 따르는 언어이기 때문
- 함수를 어디서 호출했는지가 아니라 함수를 어디에 정의했는지에 따라 상위 스코프를 결정한다. 이를 렉시컬 스코프라 한다.
const x = 1;
function a() {
const x = 10;
b();
}
function b() {
console.log(x);
}
a(); // 1
b(); // 1
- 두 함수 모두 전역에서 정의된 전역 함수이다.
- 함수의 상위 스코프는 함수를 어디서 정의했느냐에 따라 결정되기 때문에 a, b함수는 상위 스코프는 전역이다.
- 함수를 어디서 호출하는지는 함수의 상위 스코프 결정에 어떠한 영향도 주지 못한다.
- 함수의 상위 스코프는 함수를 정의한 위치에 의해 정적으로 결정되고 변하지 않는다.
* 상위 스코프는 함수 정의 환경(위치)에 따라서 결정
* 함수 호출 위치와 상위 스코프는 아무런 관계가 없다.
함수 객체의 내부 슬롯 [[ Environment ]]
- 함수는 자신의 내부슬롯 [[ Environment ]] 에 상위 스코프의 참조를 저장
* 현재 실행중인 실행 컨텍스트의 렉시컬 환경을 가리킨다.
- 클로저는 중첩 함수가 상위 스코프의 식별자를 참조하고 있고 중첨 함수가 외부 함수보다 더 오래 유지되는 경우 한정하는것이 일반적.
- 클로저가 참조하고 있지 않은 식별자는 기억하지 않는다.
자유변수
- 클로저에 의해 참조되는 상위 스코프의 변수
클로저 활용 예
- 클로저는 상태를 안전하게 변경하고 유지하기 위해 사용
- 상태를 안전하게 은닉하고 특정 함수에게만 상태 변경을 허용하기 위해 사용
// (1)
let num = 0; // 카운트 상태 변수
const increase = function () { // 카운트 상태 변경 함수
return ++num; // 카운트 1 증가
};
console.log(increase()); // 1
console.log(increase()); // 2
console.log(increase()); // 3
// (2)
let increase = (function () { // 카운트 상태 변경 함수
let num = 0; // 카운트 상태 변수
return function () { // 클로저
return ++num; // 카운트 1 증가
};
})();
console.log(increase()); // 1
console.log(increase()); // 2
console.log(increase()); // 3
- 같은 값을 호출하는 코드이지만 클로저를 통해서 변수 값을 오류가 나지 않게 할 수 있다.
(1) 첫번째 코드
- 전역변수로 지정되어 있기 때문에 접근도 쉽고 오류 가능성이 높다.
(2) 두번째 코드
- 즉시 실행 함수를 통해서 변수에 할당된 함수는 자신이 정의된 위치에 의해 결정된 상위 스코프를 기억하는 클로저 이다.
- 즉시 실행 함수는 호출된 이후 소멸되지만 즉시 실행 함수가 반환한 클로저는 변수에 할당되어 호출된다.
- 클로저는 자신이 정의된 위치에 의해 결정된 상위 스코프(함수의 렉시컬 환경)를 기억하고 있다.
- 카운트 상태를 유지하기 위한 자유 변수 num을 언제 어디서 호출하던지 참조 가능
const counter = (function () {
let num = 0;
// 클로저인 메서드를 갖는 객체를 반환
// 객체 리터럴은 스코프를 만들지 않는다.
// 아래 메서드들의 상위 스코프는 즉시 실행 함수의 렉시컬 환경
return {
increase() {
return ++num;
},
decrease() {
return num > 0 ? --num : 0;
},
};
})();
console.log(counter.increase()); // 1
console.log(counter.increase()); // 2
console.log(counter.decrease()); // 1
console.log(counter.decrease()); // 0
- increase, decrease 메서드의 상위 스코프는 메서드가 평가되는 시점에 실행중인 즉시실행 함수 실행컨텍스트의 렉시컬 환경
- 언제 어디서 호출되든 상관없이 즉시 실행 함수의 스코프의 식별자를 참조 가능
- 위 예제를 생성자 함수로 표현하면?
const Counter = (function () {
let num = 0;
function Counter() {}
Counter.prototype.increase = function () {
return ++num;
};
Counter.prototype.decrease = function () {
return num > 0 ? --num : 0;
};
return Counter;
})();
const counter = new Counter();
console.log(counter.increase()); // 1
console.log(counter.increase()); // 2
console.log(counter.decrease()); // 1
console.log(counter.decrease()); // 0
- 즉시실행함수로 내에 선언된 변수는 인스턴스를 통해 접근할 수 없다.
- 프로토타입을 통해 메서드를 상속받는 인스턴스를 생성
- num변수의 값은 메서드만이 변경할 수 있다.
* 변수 값은 누구나 변경이 가능하면 오류 발생의 원이이 된다.
* 프로그램의 안정성을 높이기 위해 클로저는 적극적으로 사용
- 고차함수 예시
// 함수를 인수로 전달 받고 반환하는 고차 함수
// 이 함수는 카운트 상태를 유지하기 위한 자유 변수 counter를 기억하는 클로저를 반환
function makeCounter(a) {
// 카운터 상태를 유지하기 위한 자유 변수
let counter = 0;
// 클로저 반환
return function () {
counter = a(counter);
return counter;
};
}
function increase(n) {
return ++n;
}
function decrease(n) {
return --n;
}
// 두 함수는 자신만의 독립된 렉시컬 환경을 갖기 때문에 카운트 유지가 안된다.
const increaser = makeCounter(increase);
console.log(increaser()); // 1
console.log(increaser()); // 2
const decreaser = makeCounter(decrease);
console.log(decreaser()); // -1
console.log(decreaser()); // -2
- 어떻게 보면 당연하다 카운트 상태를 유지하려면 렉시컬 환경을 공유하는 클로저를 만들어야 한다.
- makeCounter()함수를 두번 호출하면 안된다.
- 그럼 어떻게?
- 렉시컬 환경을 공유하는 클로저를 만들자!
const counter = (function () {
let counter = 0;
return function (a) {
counter = a(counter);
return counter;
};
})();
function increase(n) {
return ++n;
}
function decrease(n) {
return --n;
}
console.log(counter(increase)); // 1
console.log(counter(increase)); // 2
console.log(counter(decrease)); // 1
console.log(counter(decrease)); // 0
'Code note > 자바스크립트' 카테고리의 다른 글
자바스크립트 이벤트 전파, 중단 정리 (0) | 2022.11.14 |
---|---|
자바스크립트 이벤트 event, 이벤트 핸들러 정리 (0) | 2022.11.14 |
자바스크립트 자료구조 우선순위 큐 메서드 정리 (0) | 2022.09.28 |
자바스크립트 자료구조 큐(Queue) (0) | 2022.09.28 |
자바스크립트 Dom, 프로토타입 정리 (0) | 2022.09.25 |