[240401] 23장 클로저 (2)

23장 클로저 (2)

클로저의 활용

  • 상태를 안전하게 변경하고 유지하기 위해 사용
  • 상태를 안전하게 은닉하고 특정 함수에게만 상태 변경을 허용
    • 특정 함수에게만 상태 변경을 허용하여 안전하게 유지 가능
const increase = (function () {
	// 카운트 상태 변수
	let num = 0; 
	return function() {
		// 카운트 상태를 1만큼 증가시킨다.
		return ++num;
	}
}());

console.log(increase()); // 1
console.log(increase()); // 2
console.log(increase()); // 3
// 함수를 인수로 전달받고 함수를 반환하는 고차 함수
// 이 함수는 카운트 상태를 유지하기 위한 자유 변수 couter를 기억하는 클로저를 반환.
function makeCounter(aux) {
	let counter = 0;
	
	return function () {
		// 인수로 전달받은 보조 함수에 상태 변경을 위임한다.
		counter = aux(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

// increaser 함수와는 별개의 독립된 렉시컬 환경을 갖기 때문에 카운터 상태가 연동되지 않음
const decreaser = makeCounter(decreaser);
console.log(decreaser()); // -1
console.log(decreaser()); // -2

캡슐화와 정보 은닉

const Person = (function () {
	let _age = 0; // private
	
	// 생성자 함수
	function Person(name, age) {
		this.name = name; // public
		_age = age; 
	}
	
	// 프로토타입 메서드
	Person.prototype.sayHi = function() {
		console.log(`Hi ! My name is ${this.name}. I am ${_age}.`);
	};
	
	// 생성자 함수를 반환
	return Person; 
}());

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

const me = new Person('Kim', 30);
me.sayHi(); // Hi! My name is Kim. I am 30.
console.log(me.name); // Kim
console.log(me._age); // undefined
  • 현재는 클래스에 private 필드 정의 가능

자주 발생하는 실수

// Bad 
var funcs = [];

for(var i = 0; i < 3; i++) {
	funcs[i] = function () { return i; };
}

for(var j = 0; j < funcs.length; j++) {
	console.log(funcs[j]()); // 3 3 3
}
  • var 로 선언하면 i 가 전역이기 때문에 예상했던 0 1 2 가 나오지 않음
  • let 키워드 사용하면 원하는 대로 나옴
// 클로저 사용
var funcs = []; 

for(var i = 0; i < 3; i++) {
	funcs[i] = (function (id) {
		return function () {
			return id;
		};
	}(i));
}

for(var j = 0; j < funcs.length; j++) {
	console.log(funcs[j]());
}
const funcs = [];

for(let i = 0; i < 3; i++) {
	funcs[i] = function () { return i; };
}

for(let j = 0; j < funcs.length; j++) {
	console.log(funcs[j]()); // 0 1 2
}
  1. for 문의 변수 선언문에서 let 키워드로 선언한 초기화 변수를 사용한 for 문이 평가되면 먼저 새로운 렉시컬 환경을 생성하고 초기화 변수 식별자와 값을 등록 , 렉시컬 환경을 현재 실행중인 컨텍스트의 렉스컬 환경으로 교체
  2. 2,3,4 for 문의 코드 블록이 반복 실행되면 새로운 렉시컬 환경을 생성하고 식별자 값을 등록함 새롭게 생성된 렉시컬 환경 교체
  3. for 문이 종료되면 이전의 렉시컬 환경으로 돌아감

→ let 이나 const 키워드를 사용하는 반복문은 코드 블록을 반복 실행할 때마다 새로운 렉시컬 환경을 생성하여 반복할 다아시의 상태를 스냅숏 찍는 것처럼 저장한다.

Categories:

Updated:

Leave a comment