Skip to content

Latest commit

 

History

History
307 lines (265 loc) · 12.6 KB

File metadata and controls

307 lines (265 loc) · 12.6 KB

즉시 실행 함수(IIFE)

함수 실행 방법

1. 함수의 이름을 선언 : 함수 실행(호출) <- 이름(함수 이름, 함수 값을 참조하는 변수 이름)

2. 이름이 없는 함수 (익명 함수, 무명 함수)

3. 즉시 실행 함수 : 이름 호출 없이 즉시 실행되는 JavaScript 프로그래밍 언어의 관용어구: 이디엄(idiom)

즉시 실행 함수를 사용하는 이유

  • 젼역 영역(Global Scope - public)를 오염 시키지 않기 위해서 사용한다.
  • 변수를 전역(global scope)으로 선언하는 것을 피하기 위해서 이다.
  • 지역 변수를 익명 함수로 위치시켜 외부와의 충돌을 방지할 수 있다.
  • 함수 내에서 정의된 변수와 함수 등은 해당 영역 내부에서 접근할 수 있지만 외부에서 접근할 수는 없다. 즉, private 역이 생성 된다.

일반적인 IIFE 작성 방법

( function  ()  { /* code */ } ) (); 
( function  ()  { /* code */ } () );

함수 표현식을 적용한 IIFE 작성 방법

! 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 );

참고링크

모듈(Module) 패턴

모듈(Module)

클로저(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 변수와 함수에 접근 할 수 있다.

네임 스페이스(NameSpace Pattern)

  • 이름 그대로, 이름 공간을 선언하여 다른 공간과 구분하기 위한 것.
    (구분이 가능하도록 정해놓은 범위나 영역)
  • 여러 스크립트가 한 페이지 안에 함께 있는 소스코드에서 변수가 많아질수록
    변수의 이름이 겹칠 수 있는 문제점을 완하기 위해 사용

1. 객체 리터럴 네임 스페이싱

  • 하나의 전역 객체를 생성한 다음 모든 함수, 객체, 변수를 전역객체에 추가하여 구현하는 방법
// 전역 객체 하나 생성
var globVariable = {};
// 객체의 프로퍼티로 추가
globVariable.num = 1;
// 함수 추가
globVariable.increaseNum = function(){
    console.log('increase!');
}
globVariable.decreaseNum = function(){
    console.log('decrease!');
}

모든 변수, 함수에 상위 객체명을 붙여야 하기 때문에 코드 양이 많아진다는 단점.
매번 객체에 접근할 때 마다 체인이 길어지고 이름이 중첩된다.

2. 범용 네임스페이스 함수

  • 이미 있는 것을 재정의하는 일을 방지하기 위하여 사용하는 방법.
// 1번 방식
if(type of app === "undefined"){
    var app = {};
}
// 2번 방식 == 1번 방식
var app = app || {};

참고링크

클로저(Closures)

클로저란?

  • 외부함수의 변수에 접근할 수 있는 내부함수의 영역을 일컫는다.
  • 내부함수는 외부함수의 변수뿐만 아니라 파라미터(매개변수)에도 접근 할 수 있다.
  • 단 내부함수는 외부함수의 arguments객체를 호출 할 수는 없다.
  • 외부(함수 or 전역)에서 내부함수를 참조할 수 없다. -> 클로저를 통한 은닉화

1. 기본적인 클로저

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;
};

2. 클로저 규칙과 부수 효과

  • 외부함수가 리턴된 이후에도 여전히 내부함수가 외부함수의 변수에 접근하고 있다.
  • 자바스크립트의 함수가 실행되었을때, 함수는 자신이 생성되었을때와 동일한 스코프 체인을 사용한다.
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;
    }
  };
}

3. 클로저 비꼬기

  • 클로저가 갱신된 외부함수의 변수에 접근함으로써, 외부 함수의 변수가 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

4. 클로저 성능

  • 클로저를 통해 내부 변수를 참조하는 동안에는 내부 변수가 차지하는 메모리를 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;