- [위키피디아_Immediately-invoked function expression]https://en.wikipedia.org/wiki/Immediately-invoked_function_expression)
- 젼역 영역(Global Scope - public)를 오염 시키지 않기 위해서 사용한다.
- 변수를 전역(global scope)으로 선언하는 것을 피하기 위해서 이다.
- 지역 변수를 익명 함수로 위치시켜 외부와의 충돌을 방지할 수 있다.
- 함수 내에서 정의된 변수와 함수 등은 해당 영역 내부에서 접근할 수 있지만 외부에서 접근할 수는 없다. 즉, private 역이 생성 된다.
( function () { /* code */ } ) ();
( function () { /* code */ } () );! function () { /* code */ } ();
~ function () { /* code */ } ();
- function () { /* code */ } ();
+ function () { /* code */ } ();
void function () { /* code */ } ();var i = function(){ return 10; }();
true && function(){ /* code */ }();
0, function(){ /* code */ }();( 함수_이름 ( a , b ) { /* code */ }) ( a , b );- [Benalman_Immediately-Invoked Function Expression (IIFE)]http://benalman.com/news/2010/11/immediately-invoked-function-expression/#iife)
- 꿀벌개발일지_즉시실행함수 패턴
클로저(closure)를 활용하여 private 함수와 변수에 public 함수를 통해 가능한데, 이렇게 클로저를 활용하는 것을 `모듈``이라 한다.
- 하지만 JavaScript 모듈 이라는 것을 지원하지 않음(ES5)
- Front-End Env. 확장자가 .js 인 파일이 자체로 독립적이지 않다. > 충돌 가능성 높다.
- Back-End Env. 확장자가 .js 인 파일은 자체로 독립적이다. > 충돌 X
- 웹 브라우저에서 아래 파일들을 읽어들이면 모두 전역에 노출된 상태이기에 같은 이름의 변수, 함수는 충돌이 발생하게 고, 프로그램은 망가지게 된다.
module-A.js
module-B.js
module-C.js
module-D.js-
JavaScript 에서는 모듈 패턴이 없지만,
IIFE 패턴을 활용하여 모듈처럼 사용한다. -
결국
모듈이란? 전역과 구분되는 영역이 존재하면, 그 내부의 변수, 함수는 외부에 노출되지 않기에 안전하게 로그래밍이 유지된다. -
IIFE 패턴을 사용하여 전역과 구분되는 영역(모듈)을 만들었지만, 외부에서 접근이 원천 봉쇄되기 때문에 내부의 로그래밍만 동작하게 되고, 외부에서는 접근할 수 없어 재사용 할 수 없다.
-
노출(명시적 공개) 패턴: 함수가 반환(return)하는 값(객체, 배열, 함수, 숫자, 문자, 불리언, null)을 노출 시켜 출된 함수를 받아줄(기억해줄) 변수가 필요하다.(외부에 변수로 공개하는 방법)
// counter : 네임스페이스 객체
// IIFE에 의해 counter에는 Object가 참조된다.
var counter = (function() {
// private 변수
var privateCounter = 0;
// private 함수
function changeBy(val) {
privateCounter += val;
}
// counter 네임스페이스 객체가 갖게되는 객체
// 클로저가 형성되어 내부 함수에서 외부함수 및 변수 접근 가능
return {
// scope chain에 의해 changeBy 함수 접근 가능
increase: function(value) {
changeBy(value||1);
},
decrease: function(value) {
changeBy(-value||-1);
},
// scope chain에 의해 getCounter 변수 접근 가능
getCounter: function() {
return privateCounter;
}
};
})();
counter.increase(); // privateCounter: 1
counter.increase(3); // privateCounter: 4
counter.decrease(); // privateCounter: 3
counter.decrease(2); // privateCounter: 1
counter.getCounter(); // privateCounter: 1- 네임스페이스를 사용함으로써 변수 충돌가능성을 없앨 수 있다.
- 클로저를 활용해 모듈패턴을 작성할 경우, private 변수와 함수(내부함수)에 접근 가능한 함수(public)를 정의할 수 다.
즉, 네임스페이스 객체는 public 함수를 통해 private 변수와 함수에 접근 할 수 있다.
- 이름 그대로, 이름 공간을 선언하여 다른 공간과 구분하기 위한 것.
(구분이 가능하도록 정해놓은 범위나 영역) - 여러 스크립트가 한 페이지 안에 함께 있는 소스코드에서 변수가 많아질수록
변수의 이름이 겹칠 수 있는 문제점을 완하기 위해 사용
- 하나의 전역 객체를 생성한 다음 모든 함수, 객체, 변수를 전역객체에 추가하여 구현하는 방법
// 전역 객체 하나 생성
var globVariable = {};
// 객체의 프로퍼티로 추가
globVariable.num = 1;
// 함수 추가
globVariable.increaseNum = function(){
console.log('increase!');
}
globVariable.decreaseNum = function(){
console.log('decrease!');
}모든 변수, 함수에 상위 객체명을 붙여야 하기 때문에 코드 양이 많아진다는 단점.
매번 객체에 접근할 때 마다 체인이 길어지고 이름이 중첩된다.
- 이미 있는 것을 재정의하는 일을 방지하기 위하여 사용하는 방법.
// 1번 방식
if(type of app === "undefined"){
var app = {};
}
// 2번 방식 == 1번 방식
var app = app || {};- 외부함수의 변수에 접근할 수 있는
내부함수의 영역을 일컫는다. - 내부함수는 외부함수의
변수뿐만 아니라 파라미터(매개변수)에도 접근 할 수 있다. - 단 내부함수는 외부함수의
arguments객체를 호출 할 수는 없다. - 외부(함수 or 전역)에서
내부함수를 참조할 수 없다.-> 클로저를 통한 은닉화
function showName(firstName, lastName) {
var nameIntro = "Your name is ";
// 이 내부함수는 외부함수의 변수뿐만 아니라 파라미터까지 사용할 수 있다.
function makeFullName() {
return nameIntro + firstName + " " + lastName;
}
return makeFullName();
}
showName("Michael", "Jackson"); // Your name is Michael Jackson///외부 함수
var countDownMaker = function(n) {
var count = n || 10;
// 내부 함수
// 함수 정의 없이 함수 값을 바로 반환할 수 있다.
return function (step) {
// 어떻게 해야 count가 1씩 증가하게 될까?
// var count = 0;
count -= (step || 1);
return count;
}
// 내부 함수를 외부로 내보낸다.
// return increaseCount;
};- 외부함수가 리턴된 이후에도 여전히 내부함수가
외부함수의 변수에 접근하고 있다. - 자바스크립트의 함수가 실행되었을때, 함수는 자신이 생성되었을때와
동일한 스코프 체인을 사용한다.
function celebrityName(firstName) {
var nameIntro = "This is celebrity is ";
// 이 내부 함수는 외부함수의 변수와 파라미터에 접근할 수 있습니다.
function lastName(theLastName) {
return nameIntro + firstName + " " + theLastName;
}
return lastName;
}
var mjName = celebrityName("Michael"); // 여기서 celebrityName 외부함수가 리턴된다.
// 외부함수가 위에서 리턴된 후에, 클로저(lastName)가 호출된다.
// 아직, 클로저는 외부함수의 변수와 파라미터에 접근 가능하다.
mjName("Jackson"); // This celebrity is Michael Jackson- JavaScript는 함수 뿐만 아니라,
모든 데이터 유형을 반환할 수 있다. - 객체를 반환하는 래퍼 함수를 사용하여 클로저를 활용할 수 있다.
// 카운트를 관리하는 객체
function makeCountManager(init_count) {
// 관리할 카운트 값을 초기화
var memoried_count = init_count = init_count || 0;
return {
// 카운트 증가 메서드
increase: function(step) {
init_count += (step || 1);
return init_count;
},
// 카운트 감소 메서드
decrease: function(step) {
init_count -= (step || 1);
return init_count;
},
// 카운트 초기화 메서드
reset: function(value) {
init_count = value || memoried_count;
},
// 현재 카운트 값을 반환하는 메서드
getCount: function() {
return init_count;
}
};
}- 클로저가 갱신된 외부함수의 변수에 접근함으로써, 외부 함수의
변수가 for문에 의해 변경될 경우의도치 않은버그가 생할 수 있다.
function celebrityIDCreator(theCelebrities) {
var i;
var uniqueID = 100;
for (i=0; i<theCelebrities.length; i++) {
theCelebrities[i]["id"] = function() {
return uniqueID + i;
}
}
return theCelebrities;
}
var actionCelebs = [{name:"Stallone", id:0}, {name:"Cruise", id:0}, {name:"Willis", id:0}];
var createIdForActionCelebs = celebrityIDCreator(actionCelebs);
var stalloneID = createIdForActionCelebs[0];
console.log(stalloneID.id); // 103-
위의 예제에서, 익명의
내부함수가 실행될 시점에 i의 값은 3이다(배열의 크기만큼 증가한 값). -
숫자 3은 uniqueID에 더해져 모든 celebritiesID에 103을 할당한다. 그래서,
기대(100,101,102)와 달리 모든 리턴된 열의 id=103이 된다. -
이런 결과가 나타난 이유는,
클로저는 최종 갱신된 변수(i)에 대해서만 접근할 수 있으므로, 외부 함수가 전체 for문을 행하고 리턴한 최종 i의 값을 리턴하게 된다. 100+3=103. -
이런 부작용을 고치기 위해서 “
즉시 호출된 함수 표현식(Immediately Invoked Function Expression. IIFE)”을 사용할 수 다.
function celebrityIDCreator(theCelebrities) {
var i;
var uniqueID = 100;
for (i=0; i<theCelebrities.length; i++) {
theCelebrities[i]["id"] = function(j) {
// j 파라미터는 호출시 즉시 넘겨받은(IIFE) i의 값이 된다.
return function() {
// for문이 순환할때마다 현재 i의 값을 넘겨주고, 배열에 저장한다.
return uniqueID + j;
} () // 함수의 마지막에 ()를 추가함으로써 함수를 리턴하는 대신 함수를 즉시 실행하고 그 결과값을 리턴한다.
} (i); // i 변수를 파라미터로 즉시 함수를 호출한다.
}
return theCelebrities;
}
var actionCelebs = [{name:"Stallone", id:0}, {name:"Cruise", id:0}, {name:"Willis", id:0}];
var createIdForActionCelebs = celebrityIDCreator(actionCelebs);
var stalloneID = createIdForActionCelebs[0];
console.log(stalloneID.id); // 100
var cruiseID = createIdForActionCelebs[1];
console.log(cruiseID.id); // 101- 클로저를 통해
내부 변수를 참조하는 동안에는 내부 변수가 차지하는 메모리를Gabage Collector가 회수하지 않는다.라서 클로저가 사용되는 동안 메모리가 소모되기 때문에끝나면 참조를 제거하는 것이 좋다.
function hello(name) {
var _name = name;
return function() {
console.log('Hello, ' + _name);
};
}
var hello1 = hello('승민');
var hello2 = hello('현섭');
var hello3 = hello('유근');
hello1(); // 'Hello, 승민'
hello2(); // 'Hello, 현섭'
hello3(); // 'Hello, 유근'
// 여기서 메모리를 release 시키기 클로저의 참조를 제거해야 한다.
hello1 = null;
hello2 = null;
hello3 = null;