function outer(param){
// param을 넣고 말고는 중요한게 아니라 나중에 inner함수를 실행할 때
// outerParam을 불러올 수 있다는 점이 중요
const outerParam = `This is how ${param} works!`;
function inner() { // 이 inner 함수가 생성된 환경의 스코프는 outer함수
// outer함수의 스코프를 inner함수가 기억하고 있어서 outerParam을 호출가능
console.log(outerParam);
}
// outer 함수는 inner함수를 리턴해줌
return inner;
}
// 이 부분을 이해하는게 중요하다고 생각 함
// outer함수 실행이 끝나면 outer함수 내부 변수 outerParam은 함수 내부에서 선언이 되었기 때문에
// 더 이상 호출할 방법이 없어야 정상이지만 (Javascript context를 공부하면 좋습니다.)
// outer함수 내부에 선언된 inner함수는 함수가 생성된 환경의 스코프
// 즉, outer함 수의 스코프를 기억하고 있기 때문에
// outer 내부에서 생성된 비공개 변수 outerParam 호출이 가능하게 됩니다.
const closure = outer('CLOSURE'); // outer함수 실행결과로 inner함수를 받아 closure변수에 할당
// outer함수 실행이 완료되어 사라졌어야할 outerParam을 inner함수가 참조해 실행
// 이제서야 outer함수 컨텍스트가 종료
closure(); // This is how CLOSURE works!
// counter는 outer 함수
// changeCount는 inner함수
// 객체를 리턴하고 있고 객체 안에는 increase, decrease, show와 같은 inner함수들을 저장
const counter = function() {
let count = 0;
function changeCount(number) {
count += number;
}
return {
increase: function() {
changeCount(1);
},
decrease: function() {
changeCount(-1);
},
show: function() {
alert(count);
}
}
};
// outer함수 counter를 실행하면 outer함수 스코프를 기억하고 있는 클로저들이 담긴 객체를 반환
// counterClosure는 counter함수 내부에 정의된 count나 changeCount에 접근 가능
const counterClosure = counter();
counterClosure.increase(); //
counterClosure.show(); // 1
counterClosure.decrease();
counterClosure.show(); // 0
아래 예제보다 count 예제를 보면 좀 더 쉽게 이해하기 쉽습니다.
아래 예제는 제가 이런 식으로 사용하면 될 것 같다 생각하면서 작성해 놓은 예제 입니다. (정리겸...)
// 이 함수 안에서 선언된 변수들은 다 private(비공개) 변수들입니다.
function getPrivate(){
const log = console.log;
const token = 'AaddfdBF343DVDFD';
let API = {};
function check(reqToken) {
return token === reqToken;
}
API.register = (id, pass) => {
log(`register! id: ${id} pass: ${pass} with ${token}`);
};
API.login = (id, pass) => {
log('login!');
};
// 위처럼 하나씩 적어도 되고
//아니면 이렇게 객체에 하나씩 담아주어도 됩니다.
API = {
// spread operater를 사용한 객체 복사
...API,
update: (id, pass) => {
log('update!');
},
delete: (id) => {
log(`delete! ${id}`);
}
};
// Object.assign()을 사용한 객체 복사, 이건 그냥 해봤습니다..
API = Object.assign({}, API, { check: check });
return API;
}
// Error! API is not defined!
// getPrivate함수 안에 선언된 API변수를 전역에서 호출 가능한지 확인
API.register('id', 'pass');
// API가 api 변수에 할당 된다. (정확히는 API가 가리키는 객체의 주소가)
const api = getPrivate();
// 아래 register함수(closure)는 getPrivate함수 내에서 정의된 token변수에 접근 가능
api.register('IDDDD', 'PASSS'); // register! id: IDDDD padd: PASSS with AaddfdBF343DVDFD
// 즉시실행함수로 실행한 함수는 글로벌 네임스페이스에 변수(getPrivate)가 추가되지 않는다.
// 그리고 getPrivate라고 함수명을 붙일 필요는 없다.
// (function() { 라고 작성해도 무방
//
const api = (function getPrivate() {
const log = console.log;
const token = 'AaddfdBF343DVDFD';
let API = {};
function check(reqToken) {
return token === reqToken;
}
API.register = (id, pass) => {
log(`register! id: ${id} pass: ${pass} with ${token}`);
};
API.login = (id, pass) => {
log('login!');
};
// spread operater를 사용한 객체 복사
API = {
...API,
update: (id, pass) => {
log('update!');
},
delete: (id) => {
log(`delete! ${id}`);
}
};
// Object.assign()을 사용한 객체 복사
API = Object.assign({}, API, { check: check });
return API;
})();
api.register('IDDDD', 'PASSS'); // register! id: IDDDD padd: PASSS
이렇게 모듈을 만들면서 closure 기술? 방식으로 만드는 이유는 전역에 같은 이름을 가진 변수나 함수가 선언되어 있거나 선언될 수 있는 걸 방지해 이상없이 코드가 동작하도록 만들기 위함이다.
아래는 공부하면서 같이보면 좋아 보여서 링크들 남겨 봅니다.
const api = (function() {
// 내부 변수들: private 변수들
const log = console.log;
const token = 'AaddfdBF343DVDFD';
let API = {};
// 내부 함수: private함수
function checkToken() {
log(`check token: ${token}`);
}
API.check = function(reqToken) {
checkToken(); // 내부에만 정의된 checkToken을 실행할 수 있다.
return token === reqToken;
}
API.register = (id, pass) => {
checkToken();
// 내부에 정의된 token 변수에 접근 가능
log(`register! id: ${id} pass: ${pass} with ${token}`);
};
API.login = (id, pass) => {
checkToken();
log('login!');
};
API.update = (id, pass) => {
checkToken();
log('update!');
};
API.delete = (id) => {
checkToken();
log(`delete! ${id}`);
};
return API; // inner 함수를 가진 객체 리턴
})();
api.register('IDDDD', 'PASSS'); // register! id: IDDDD padd: PASSS
예제의 의도를 파악하기 너무 어려워요 ㅠㅠ
뭐가 클로저 함수인지 프라이빗 파라메터 인지 예제에서 구분할수가 없네요. 설명이나 보면 이해가 될만한 문서 추천좀 부탁드려도 될까요