MDN에서 클로저 정의
클로저는 함수와 그 함수가 선언된 렉시컬 환경과의 조합이다.
위를 보면 크게 와닿지가 않는다. 그럼 더 설명해보겠다.
클로저를 공부하기 앞서 실행컨텍스트에 대한 지식이 있으면 이해하기가 훨씬 쉽다.
const x = 1;
function outerFunc() {
const x = 10;
function innerFunc() {
console.log(x); // 10
}
innerFunc();
}
outerFunc();
innerFunc
함수는 outerFunc
함수 내부에서 중첩된 함수이다. 따라서, 외부 함수 outerFunc
의 x 변수에 접근이 가능하다. 만약 innerFunc
함수가 outerFunc
의 함수의 내부에서 정의된 중첩 함수가 아니라면 innerFunc
함수를 outerFunc
함수의 내부에서 호출한다 하더라도 outerFunc
함수의 변수에 접근할 수 없다. (이해가 가지 않는다면 실행컨텍스트를 공부하고 오자! ! !)
const x = 1;
function outerFunc() {
const x = 10;
innerFunc();
}
function innerFunc() {
console.log(x); // 1
}
outerFunc();
접근할 수 없는 이유는 자바스크립트는 렉시컬 스코프를 따르는 프로그래밍 언어이기 때문이다.
자바스크립트 엔진은 함수를 어디서 호출했는지가 아니라 함수를 어디서 정의했는지에 따라 상위 스코프를 결정한다. 이것이 렉시컬 스코프(정적 스코프)라 한다.
const x = 1;
function outer() {
const x = 10;
const inner = function () { console.log(x); };
return inner;
}
const innerFunc = outer();
innerFunc(); // 10
위에 실행 결과를 예측해보면 outer함수는 중첩함수 inner를 반환하고 종료되므로 실행 컨텍스트는 실행 컨텍스트 스택에서 제거된다. 이때 outer 함수의 지역 변수 x와 변수 값 10을 저장하고 있던 outer 함수의 실행 컨택스트가 제거되었으므로 outer 함수의 지역 변수 x 또한 생명주기를 마감하기 때문에 위 코드 결과가 1을 출력할거 같지만 실제로 위 코드 결과는 10을 출력한다. 이미 생명 주기가 종료되어 실행 컨텍스트 스택에서 제거된 outer 함수의 지역변수 x가 다시 부활한것처럼 동작한다.
이처럼 외부 함수보다 중첩 함수가 더 오래 유지되는 경우 중첩함수는 이미 생명주기가 종료한 외부 함수의 변수를 참조할 수 있다. 이러한 중첩 함수를 우리는 클로저라고 부른다 !
자유변수: 클로저에 참조되는 상위 스코프의 변수
클로저는 상태를 안전하게 변경하고 유지하기 위해 사용한다. 즉, 상태가 의도치 않게 변경되지 않도록 상태를 안전하게 은닉하고 특정 함수에게만 상태 변경을 허용하기 위해 사용한다.
let num = 0;
const increase = function () {
return ++num;
};
console.log(increase()); // 1
console.log(increase()); // 2
console.log(increase()); // 3
num == 100;
console.log(increase()); // 101
위 코드 문제점
카운트 변수가 전역 변수를 통해 관리하므로 언제든지 누구나 접근 및 변경이 가능하다는 것이 문제 !
해결 1. 카운트 변수를 전역에서 지역 변수로 변경
--> 함수가 호출될 때마다 초기화되므로 카운트 변수 값이 변경되지 않으므로 해결x
해결 2. 클로저 사용
즉시 실행 함수 사용
const increase = (function () { // 즉시 실행 함수
let num = 0;
return function () {
return ++num; // increase 함수의 변수 참조, 생명 주기 더 김 -> 클로저
};
}());
console.log(increase()); // 1
console.log(increase()); // 2
console.log(increase()); // 3
const counter = (function () { // 즉시 실행 함수
let num = 0;
return { // 객체 리터럴을 반환 --> 객체 리터럴은 스코프 생성 x --> 메서드들의 상위 스코프는 외부 함수
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
캡슐화 - 객체의 상태를 나타내는 프로퍼티와 프로퍼티를 참조하고 조작할 수 있는 동작인 메서드를 하나로 묶는 것
정보 은닉- 객체의 특정 프로퍼티나 메서드를 감출 목적으로 사용하는 것
자바스크립트는 접근 제한자를 제공하지 않는다. 자바스크립트의 객체의 모든 프로퍼티와 메서드는 기본적으로 외부에 공개되어 있다. (public)
function Person(name, age) { this.name = name; // public let _age = age; // private (지역 변수) this.sayHi = function () { console.log(`Hi! My name is ${this.name}. I am ${_age}.`); }; // 객체 생성될 때마다 중복 생성 --> 프로토타입 메서드로 변경하기 } const me = new Person('Lee', 20); me.sayHi; // Hi! My name is Lee. I am 20. console.log(me.name); // Lee console.log(me._age); // undefined