1. 클로저를 들어가기 전에 알아야 할 개념
1-1) 스코프란?
변수 이름, 함수 이름, 클래스 이름과 같은 식별자가
본인이 선언된 위치에 따라 다른 코드에서 자신이 참조될 수 있을지 없을지 결정되는 것
function add(x,y) {
console.log(x,y);
return x + y;
}
add(3,6);
console.log(x,y);
실행결과
3,6
ReferenceError: x in not defined 어쩌고..
함수의 중첩 스코프
var x = "나는 전역 X";
function outer() {
var y = "나는 outer함수의 지역 Y";
console.log(x);
console.log(y);
function inner() {
var x = "나는 inner함수의 지역 X";
console.log(x);
console.log(y);
}
inner();
}
outer();
console.log(x);
console.log(y);
실행결과
var x = "나는 전역 X";
function outer() {
var y = "나는 outer함수의 지역 Y";
console.log(x); // 나는 전역 X
console.log(y); // 나는 outer함수의 지역 Y
function inner() {
var x = "나는 inner함수의 지역 X";
console.log(x); // 나는 inner함수의 지역 X
console.log(y); // 나는 outer함수의 지역 Y
}
inner();
}
outer();
console.log(x); // 나는 전역 X
console.log(y); // ReferenceError: x in not defined 어쩌고..
▲스코프 체인
1-2) 스코프 체인
- 계층적으로 연결 (무조건 위로만 올라감)
- 물리적으로 존재
- 자바스크립트 엔진은 스코프 체인을 통해 변수를 참조
- 최상위 스코프는 전역 스코프
자바스크립트는 렉시컬 스코프를 따르기 때문에 함수는 정의되자마자 상위 스코프가 결정되고 이후에 해당 함수에 의해
함수 객체가 생성이 되면 해당 함수 객체는 내부 슬롯 에 상위 스코프에 대한 참조를 저장하여 본인의 상위 스코프를 알 수 있게 됨
*렉시컬(Lexical) : 포함하는 식별자, 식별자에 바인딩 된 값, 상위 렉시컬 환경에 대한 참조
2. 클로저
const x = 1;
function outer() {
const x = 10;
const inner = function () {
console.log(x);
};
return inner;
}
const jh = outer();
jh();
- outer 함수는 중첩 함수 inner를 jh에게 반환하면서 생명주기를 마감, 즉 outer 함수가 실행이 종료되면 실행 컨텍스트 스택에서 제거
- outer 함수의 지역변수 x도 생명주기가 마감하게 되어 지역 변수 x에 접근할 방법이 없지 않을까?
실행결과
10
---------------------------------
x의 부활......
중첩함수 inner가 이미 생명주기를 마감한 outer 함수의 지역 변수 x를 참조할 수 있다면
이때 inner를 클로저라고 함
- outer 함수는 종료되면서 jh 한테 inner 함수를 반환하기 때문에 inner 함수 객체를 참조
- outer 함수의 생명주기가 종료되어도 렉시컬 환경까지 소멸하지 않음
클로저는 중첩 함수가 상위 스코프의 식별자를 참조하고 있고, 외부 함수보다 더 살아있음
∴ 클로저는 함수와 함수가 선언된 어휘적 환경의 조합이다
사용처
- 하나의 state가 의도치 않게 변경되지 않도록 state를 안전하게 은닉
- 특정 함수에게만 state 변경을 허용하기 위해 사용
주의해야 할 점
- 클로저 함수를 볼 때 외부에서 어떤 함수를 가져다가 사용하는지 직관적으로 알 수 있게 해야 함
- 클로저는 명확한 것을 생략하여 가독성을 높여주는 역할을 할 수 있는데 이것을 잘못 활용하면 오히려 코드 가독성을 나쁘게 만들 수 있음
3. 추가 내용
3-1) 클로저 예제 (모듈 패턴 예제)
- 자바스크립트는 이런 방법을 제공하지 않지만 클로저를 이용하여 프라이빗 메소드를 흉내내는 것이 가능
- 프라이빗 메소드는 코드에 제한적인 접근만을 허용한다는 점 뿐만 아니라 전역 네임 스페이스를 편리하게 관리가능
var makeCounter = function() {
var privateCounter = 0;
function changeBy(val) {
privateCounter += val;
}
return {
increment: function() {
changeBy(1);
},
decrement: function() {
changeBy(-1);
},
value: function() {
return privateCounter;
}
}
};
var counter1 = makeCounter();
var counter2 = makeCounter();
console.log(counter1.value());
counter1.increment();
counter1.increment();
console.log(counter1.value());
counter1.decrement();
console.log(counter1.value());
console.log(counter2.value());
실행결과
0
2
1
0
- counter.increment, counter.decrement, counter.value 세 함수에 의해 공유되는 하나의 어휘적 환경 (렉시컬 환경)을 만듦
- 어휘적 환경은 실행되는 익명 함수 안에서 만들어짐
- 어휘적 환경에 privateCounter와 changeBy 함수 포함, 익명 함수 외부에서 접근 X
- 익명 래퍼에서 반환된 세 개의 퍼블릭 함수를 통해서만 접근
*렉시컬(Lexical) : 포함하는 식별자, 식별자에 바인딩 된 값, 상위 렉시컬 환경에 대한 참조
∴ 세 가지 퍼블릭 함수는 같은 환경을 공유하는 클로저다.
각 클로저는 그들 고유의 클로저를 통한 privateCounter 변수의 다른 버전을 참조하기 때문에 각 카운터가 호출될 때 하나의 클로저에서 변수값을 변경해도 다른 클로저의 값에는 영향을 주지 않음
3-2) 클로저 함수의 장점 (활용)
- 데이터 보존 (함수의 중첩 스코프 예제)
- 클로저 함수는 외부 함수의 실행이 끝나더라도 외부 함수 내의 변수 사용 가능
- 특정 데이터를 함수 스코프 안에 가둬두고 사용할 수 있는 폐쇄성을 갖음 - 정보의 접근 제한 (캡슐화 3-1 예제)
- '클로저 모듈 패턴'을 사용하여 객체에 여러 개의 함수를 담아 리턴하도록 만듦
3-3) 클로저 사용 이유
자바스크립트는 private,public 등의 별도 문법이 없는 언어로 private 기능이 없다는 것은 예상치 못한 누군가가 접근과 조작이 가능하다는 것
클로저는 제한적인 접근만을 허용하며, private한 변수 또는 메소드의 효과를 낼 수 있음
-> 객체지향 프로그래밍의 정보 은닉과 캡슐화의 이점을 얻을 수 있음
3-4) 자바(JAVA)와 자바스크립트(Javascript) 메모리 구조
- 자바스크립트(Javascript) 메모리 구조
- 콜스택(Call Stack) : 원시타입 데이터가 저장, 실행 컨텍스트를 통해 변수 식별자 저장, 스코프 체인 및 this 관리, 코드 실행 순서 관리
- 메모리힙(Memory Heap) : 참조타입 데이터가 저장
- 자바(JAVA) 메모리 구조
- 스택 영역(Java Stack) : 메소드가 호출되어 실행될 때 메소드의 매개변수, 메소드 내에 선언된 지역변수, 값 , 임시 변수 저장
- 힙 영역(Heap) : 데이터를 저장하기 위해 동적으로 할당하여 사용, 모든 프로그램에 의해 공유 , GC으로 관리
'Javascript' 카테고리의 다른 글
javascript 익명 함수 정리 (0) | 2022.05.17 |
---|---|
javascript xlsx ecxel 모든 cell에 style 적용 (0) | 2022.04.22 |
javascript sheetjs , xlsx cell value에 따라 cell style 적용하기 (0) | 2022.04.15 |
Ajax를 이용한 file upload & download (0) | 2021.12.10 |
바닐라 자바스크립트와 ES6 자바스크립트 차이점 (0) | 2021.11.03 |