- 클로저 목차
- 클로저는 자바스크립트의 고유 개념이 아니다.
- 함수를 일급 객체로 취급하는 함수형 프로그래밍언어에서 사용되는 중요한 특성이다.
- MDN에서는 "클러저는 함수와 그 함수가 선언된 렉시컬 환경과의 조합이다"라고 설명한다.
자바스크립트는 렉시컬 스코프를 따르는 프로그래밍 언어이다.
const x =1; function outerFunction(){ const x =10; function innerFunction(){ console.log(x) //10 } }
→ innerFunction에서 outerFunction에 있는 x에 접근이 가능하다.
중첩함수 inner의 상위스코프는 외부함수 outerFunction이기 때문이다.
다음은 inner에서 outer 변수에 접근이 불가능하다.
const x =1; function outerFunction(){ const x =10; } function innerFunction(){ console.log(x) // 1 }
→ 이 같은 현상이 발생하는 이유는 자바스크립트가 렉시컬 스코프를 따르는 프로그래밍 언어이기 때문이다.
자바스크립트 엔진은 함수를 어디서 호출하느냐가 아닌 어디서 정의하냐에 따라 상위 스코프를 결정한다.
이를 렉시컬 스코프(정적 스코프)라 한다.
스코프의 실체는 실행 컨텍스트의 렉시컬 환경
이다. 이 렉시컬 환경은 자신의 "외부 렉시컬 환경에 대한 참조(outer Lexical Environment Reference"를 통해 상위 렉시컬 환경과 연결된다.
→ 이것이 바로 스코프 체인
이다.
함수 객체의 내부 슬롯 [[Environment]]
- 함수는 자신의 내부 슬롯 에 자신의 정의된 환경, 즉 상위 스코프의 참조를 저장한다.
Why ?
→ 함수가 정의된 환경(위치)가 호출되는 환경(위치)은 다를 수 있으므로, 렉시컬 스코프가 가능하려면, 함수는 자신이 호출되는 환경과는 상관없이 자신의 정의된 환경, 즉 상위 스코프(함수 정의가 위치하는 스코프가 바로 상위 스코프다.)를 기억해야하므로이다.
즉, [[ Environment ]]에 저장된 현재 실행 중인 실행 컨텍스트의 렉시컬 환경의 참조가 바로 상위 스코프이다.
또한, 자신이 호출되었을 때 생성될 함수 렉시컬 환경의 "외부 렉시컬 환경에 대한 참조"에 저장될 참조 값이다.
클로저와 렉시컬 환경
const x= 1; //1 function outer(){ const x=10; const inner = function (){ console.log(x) // 2 } return inner; } //outer 함수를 호출하면 중첩 함수 inner를 반환한다. //그리고 outer 함수의 실행 컨텍스트는 실행 컨텐스트 스택에서 pop되어 없어진다. const innerFunc = outer(); //3 innerFunc(); //4 결과 : 10
위 코드의 실행 결과 (4)는 outer 함수의 지역변수 x의 값인 10이다.
이미 생명 주기가 종료되어 실행 컨텍스트 스택에서 제가된 outer함수의 지역 변수 x가 다시 부활이라도 한 듯이 동작하고 있다.
→ 외부 함수보다 중첩 함수가 더 오래 유지되는 경우 중첩함수는 이미 생명주기가 종료한 외부 함수의 변수를 참조할 수 있다. 이러한 중첩 함수를 클로저(closure)
라고 부른다.
→ 클로저는 중첩 함수가 상위 스코프의 식별자를 참조하고 있고 중첩 함수가 외부 함수보다 더 오래 유지되는 겨웅에 한정하는 것이 일반적이다.
클로저에 의해 참조되는 상위 스코프의 변수를 자유변수
라고 한다.
( 클로저 : 함수가 자유 변수에 대해 닫혀있다 라는 의미 즉, 자유 변수에 묶여있는 함수 라고 할 수 있다.)
클로저의 활용
-클로저는 상태를 안전하게 변경하고 유지하기 위해 사용한다.
- 상태가 의도치 않게 변경되지 않도록 상태를 안전하게
은닉
하고 특정 함수에게만 상태 변경을 허용하기 위해 사용한다.
const Counter = (function(){ // 1.카운트 상태 변수 let num = 0; function Counter(){ //this.num = 0; // 2.프로퍼티는 public 하므로 은닉되지 않는다. } Counter.prototype.increase = function(){ return ++num; }; Counter.prototype.decrease = function(){ return num > 0 ? --num; : 0; }; return Counter; })(); const counter = new Counter(); counter.increase(); //1 counter.increase(); //2 counter.decrease(); //1
- num은 Counter가 생성할 인스턴스의 프로퍼티가 아니라 즉시 실행 함수 내에서 선언된 변수이므로 private하다. 만약, num이 Counter 내부에 있으면 즉 생성할 인스턴스의 프로퍼티라면 public 프로퍼티가 된다.
- 생성자 함수 Counter는 프로토타입을 통해 increase,decrease 메서드를 상속받는 인스턴스를 생성한다.
//함수를 반환하는 고차 함수 //이 함수는 카운트 상태를 유지하기 위한 자유변수 counter를 기억하는 클로저를 반환한다. const counter = (function(){ //카운트 상태를 유지하기 위한 자유 변수 let counter =0; //함수를 인수로 전달받는 클로저를 반환 return function(predicate){ //인수로 전달받은 보조 함수에 상태 변경을 위힘한다. counter = predicate(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
→ 단 여기서 중요한건 함수호출이 2번 이루어질 경우 즉, 함수를 호출해 함수를 반환할 때 반호나된 함수는 자신만의 독립된 렉시컬 환경을 갖는다. 따라서 자유변수는 공유될 수 없다.
그럼, 클로저는 왜 사용하나 ?
→ 외부 상태 변경이나 가변 데이터(mutable)를 피하고 불변성(immutablility)을 지향하는 함수형 프로그래밍에서 부수 효과를 최대한 억제하여 오류를 피하고 안정성을 높이기 위해 클로저는 적극적으로 사용된다.
캡슐화
-객체의 상태(프로퍼티)와 프로퍼티를 참조하고 조작하는 동작인 메서드를 하나로 묶는 것을 말한다
-객체의 특정 프로퍼티나 메서드를 감출 목적으로 사용하기도 하는데 이를 정보 은닉
이라고 한다.
정보 은닉은 결합도를 낮추는 효과가 있다.
자바스크립트는 pulblick,private,protected 같은 접근 제한자를 제공하지 않는다. 모두 pulbic하다.
'Java Script & j Query > Java Script' 카테고리의 다른 글
[javascript]화살표함수란? (0) | 2021.06.10 |
---|---|
생성자 함수 (0) | 2021.06.03 |
Prototype(프로토 타입) (0) | 2021.06.01 |
[Java Script]-클로저란? (0) | 2021.03.31 |
[JavaScript]Form Data란? (0) | 2020.09.27 |