“(function () {…}) ()”와 같은 익명 함수로 전체 Javascript 파일을 래핑하는 목적은 무엇입니까?


584

최근에 많은 Javascript를 읽었으며 가져올 전체 .js 파일에서 전체 파일이 다음과 같이 래핑되어 있음을 알았습니다.

(function() {
    ... 
    code
    ...
})();

간단한 생성자 함수 세트가 아닌 이것을 수행하는 이유는 무엇입니까?


6
나는 이것이 많은 사람들에 의해 사용될 것이라고 생각하기 때문에 닫는 것을 잊지 마십시오.
dgh

5
이 기술을 "IIFE"라고합니다. 이것은 즉시 호출 된 함수 표현을 의미합니다. en.wikipedia.org/wiki/Immediately-invoked_function_expression
Adrien Be

답변:


786

일반적으로 네임 스페이스 (나중에 참조)에 있으며 멤버 함수 및 / 또는 변수의 가시성을 제어합니다. 그것을 객체 정의처럼 생각하십시오. 이에 대한 기술적 이름은 IIFE (Inmediately Invoked Function Expression )입니다. jQuery 플러그인은 일반적으로 다음과 같이 작성됩니다.

자바 스크립트에서는 함수를 중첩 할 수 있습니다. 따라서 다음은 합법적입니다.

function outerFunction() {
   function innerFunction() {
      // code
   }
}

이제 전화를 걸 수 outerFunction()있지만의 가시성은 innerFunction()의 범위로 제한 outerFunction()됩니다 outerFunction(). 기본적으로 Javascript의 변수와 동일한 원칙을 따릅니다.

var globalVariable;

function someFunction() {
   var localVariable;
}

해당 :

function globalFunction() {

   var localFunction1 = function() {
       //I'm anonymous! But localFunction1 is a reference to me!
   };

   function localFunction2() {
      //I'm named!
   }
}

위 시나리오에서는 globalFunction()어디서나 전화 를 걸 수 있지만 localFunction1또는 전화를 걸 수는 없습니다 localFunction2.

당신이 쓸 때하고있는 일은 (function() { ... })()첫 번째 괄호 안의 코드를 함수 리터럴로 만드는 것입니다 (전체 "객체"가 실제로 함수임을 의미합니다). 그 후에 ()방금 정의한 함수 (final )를 자체 호출합니다 . 앞에서 언급했듯이 이것의 가장 큰 장점은 개인 메소드 / 함수 및 속성을 가질 수 있다는 것입니다.

(function() {
   var private_var;

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

첫 번째 예에서는 globalFunction이름 으로 명시 적으로 호출 하여 실행합니다. 즉, 당신은 globalFunction()그것을 실행하려고 할 것입니다. 그러나 위 예제에서 함수를 정의하는 것이 아닙니다. 한 번에 정의 하고 호출합니다. 즉, JavaScript 파일이로드되면 즉시 실행됩니다. 물론, 당신은 할 수 있습니다 :

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

IIFE를 사용할 때 전역 범위를 오염시키지 마십시오 (결과적으로 이름이 없기 때문에 함수를 여러 번 호출 할 수 없음을 의미 함). 이 함수는 실제로 문제가되지 않는 경우에만 실행되도록되어 있습니다).

IIFE의 깔끔한 점은 내부를 정의하고 원하는 부분 만 외부 세계에 노출시킬 수 있다는 것입니다 (이름 공간 지정의 예를 통해 기본적으로 자체 라이브러리 / 플러그인을 만들 수 있음).

var myPlugin = (function() {
 var private_var;

 function private_function() {
 }

 return {
    public_function1: function() {
    },
    public_function2: function() {
    }
 }
})()

이제 전화 myPlugin.public_function1()를 걸 수 있지만 액세스 할 수 없습니다 private_function()! 클래스 정의와 매우 비슷합니다. 이것을 더 잘 이해하려면 다음 링크를 참조하십시오.

편집하다

나는 언급하는 것을 잊었다. 마지막으로 (), 당신은 당신이 원하는 모든 것을 전달할 수 있습니다. 당신의 jQuery 플러그인을 만들 때 예를 들어, 전달 jQuery또는 $과 같이 :

(function(jQ) { ... code ... })(jQuery) 

그래서 여기서하고있는 것은 하나의 매개 변수 ( jQ로컬 변수 라고 하며 해당 함수 에만 알려진 함수)를 취하는 함수를 정의하는 것입니다 . 그럼 당신은있는 거 자체 호출 기능 및 매개 변수 전달 (또한 jQuery,하지만 사람은 외부 세계와 실제 jQuery를 자체에 대한 참조에서이다). 이 작업을 수행 할 필요는 없지만 몇 가지 장점이 있습니다.

  • 전역 매개 변수를 재정의하고 로컬 범위에 적합한 이름을 지정할 수 있습니다.
  • 스코프 체인을 전역 범위로 이동하지 않고 로컬 범위에서 검색하는 것이 더 빠르기 때문에 약간의 성능 이점이 있습니다.
  • 압축에는 이점이 있습니다 (최소화).

이전에 이러한 함수가 시작시 자동으로 실행되는 방법에 대해 설명했지만 자동으로 실행되는 경우 누가 인수를 전달합니까? 이 기법은 필요한 모든 매개 변수가 이미 전역 변수로 정의되어 있다고 가정합니다. 따라서 jQuery가 전역 변수로 아직 정의되지 않은 경우이 예제는 작동하지 않습니다. 짐작할 수 있듯이 jquery.js가 초기화하는 동안 수행하는 한 가지 작업은 'jQuery'전역 변수와 더 유명한 '$'전역 변수를 정의하는 것입니다.이 변수는 jQuery가 포함 된 후이 코드가 작동하도록합니다.


14
매우 멋지다. 나는 네임 스페이스를 잘 이해하고 있지만, 나는 당신의 마지막 예를 많이 보았고 사람들이 무엇을 성취하려고하는지 알 수 없었다. 이것은 실제로 일을 정리합니다.
Andrew Kou

34
멋진 게시물. 고마워
Darren

4
나는 앞뒤에 세미콜론 ';'을 추가한다고 생각합니다. 예제를 완성시킬 것입니다- ;(function(jQ) { ... code ... })(jQuery);누군가 스크립트에서 세미콜론을 빠뜨린 경우, 특히 스크립트를 축소하고 다른 스크립트와 연결하려는 경우에는 세미콜론을 끊지 않습니다.
Taras Alenin

3
좋은 게시물, 나는 개인 변수에 중점을 둡니다. 나는 또한 모듈 패턴 / 클로저 (public_function1 & public_function2)의 개방 및 범위를 약간 벗어나더라도 변수를 전달하는 방법을 좋아합니다. 나는 또한 대답을 덧붙였다. 이것은 내가 생각하는 것의 구문과 함수 진술과 함수 표현의 차이점과 내가 "단순한 규칙"대 "이 결과를 얻는 유일한 방법"이라고 생각하는 것에 중점을 둔 것에 중점을 둔다.
Adrien Be

4
훌륭한 게시물, 변수를 자체 실행 함수에 전달하는 것이 어떻게 도움이되는지에 대해 더 생각합니다. 자체 실행 기능의 컨텍스트는 데이터가 없습니다. 이를 통해 컨텍스트를 전달할 수 있습니다. (function (context) { ..... })(this)그러면 부모 컨텍스트에 원하는 것을 첨부하여 노출시킬 수 있습니다.
Callum Linington

79

한마디로

요약

가장 간단한 형태로,이 기술은 코드를 함수 범위 안에 넣는 것을 목표로 합니다.

다음과 같은 가능성을 줄이는 데 도움이됩니다.

  • 다른 응용 프로그램 / 라이브러리와 충돌
  • 우수한 (전 세계적으로 가장 가능성이 높은) 오염

그것은 하지 않는 문서가 준비되면 감지 - 그것은 어떤 종류의하지 document.onload않고window.onload

일반적으로 Immediately Invoked Function Expression (IIFE)또는 로 알려져 있습니다 Self Executing Anonymous Function.

코드 설명

var someFunction = function(){ console.log('wagwan!'); };

(function() {                   /* function scope starts here */
  console.log('start of IIFE');

  var myNumber = 4;             /* number variable declaration */
  var myFunction = function(){  /* function variable declaration */
    console.log('formidable!'); 
  };
  var myObject = {              /* object variable declaration */
    anotherNumber : 1001, 
    anotherFunc : function(){ console.log('formidable!'); }
  };
  console.log('end of IIFE');
})();                           /* function scope ends */

someFunction();            // reachable, hence works: see in the console
myFunction();              // unreachable, will throw an error, see in the console
myObject.anotherFunc();    // unreachable, will throw an error, see in the console

위의 예에서 함수에 정의 된 (즉,을 사용하여 선언 된) 변수 var는 "비공개"이고 함수 범위 내에서만 액세스 할 수 있습니다 (Vivin Paliath가 말한 것처럼). 다시 말해, 이러한 변수는 함수 외부에서 보이거나 도달 할 수 없습니다. 라이브 데모를 참조하십시오 .

Javascript에는 기능 범위가 있습니다. "함수에 정의 된 매개 변수와 변수는 함수 외부에 표시되지 않으며, 함수 내에서 정의 된 변수는 함수 내 어디에서나 볼 수 있습니다." ( "자바 스크립트 : 좋은 부분"에서).


자세한 내용은

대체 코드

결국, 이전에 게시 된 코드는 다음과 같이 수행 될 수 있습니다.

var someFunction = function(){ console.log('wagwan!'); };

var myMainFunction = function() {
  console.log('start of IIFE');

  var myNumber = 4;
  var myFunction = function(){ console.log('formidable!'); };
  var myObject = { 
    anotherNumber : 1001, 
    anotherFunc : function(){ console.log('formidable!'); }
  };
  console.log('end of IIFE');
};

myMainFunction();          // I CALL "myMainFunction" FUNCTION HERE
someFunction();            // reachable, hence works: see in the console
myFunction();              // unreachable, will throw an error, see in the console
myObject.anotherFunc();    // unreachable, will throw an error, see in the console

라이브 데모를 참조하십시오 .


뿌리

반복 1

언젠가 누군가는 "myMainFunction"이라는 이름을 피하는 방법이 있어야한다고 생각했을 것입니다. 왜냐하면 우리가 원하는 것은 바로 그것을 실행하는 것이기 때문입니다. "

기본으로 돌아 가면 다음을 알 수 있습니다.

  • expression: 가치를 평가하는 것. 즉3+11/x
  • statement: 코드 행으로 무언가를 수행하지만 값으로 평가 되지 않습니다 . 즉if(){}

마찬가지로 함수 표현식은 값으로 평가됩니다. 그리고 하나의 결과 (나는 가정합니까?)는 즉시 호출 할 수 있다는 것입니다.

 var italianSayinSomething = function(){ console.log('mamamia!'); }();

보다 복잡한 예는 다음과 같습니다.

var someFunction = function(){ console.log('wagwan!'); };

var myMainFunction = function() {
  console.log('start of IIFE');

  var myNumber = 4;
  var myFunction = function(){ console.log('formidable!'); };
  var myObject = { 
    anotherNumber : 1001, 
    anotherFunc : function(){ console.log('formidable!'); }
  };
  console.log('end of IIFE');
}();

someFunction();            // reachable, hence works: see in the console
myFunction();              // unreachable, will throw an error, see in the console
myObject.anotherFunc();    // unreachable, will throw an error, see in the console

라이브 데모를 참조하십시오 .

반복 2

다음 단계는 " var myMainFunction =우리가 그것을 사용하지 않으면 왜 !"라는 생각입니다.

대답은 간단합니다. 아래와 같이 제거해보십시오.

 function(){ console.log('mamamia!'); }();

라이브 데모를 참조하십시오 .

"함수 선언은 호출 할 수 없으므로 "작동하지 않습니다 .

비결은 제거함으로써 함수 표현식함수 선언 으로 var myMainFunction =변환 했다는 것 입니다. 이에 대한 자세한 내용은 "리소스"링크를 참조하십시오.

다음 질문은 "왜 다른 식으로 함수 표현식으로 유지할 수 var myMainFunction =없습니까?

대답은 "당신이 할 수있는 것"이며 실제로 당신이 이것을 할 수있는 많은 방법이 있습니다 : +, a !, a -또는 아마도 한 쌍의 괄호로 묶는 것 (지금은 컨벤션에 의해 이루어짐), 그리고 나는 더 믿습니다. 예를 들어 :

 (function(){ console.log('mamamia!'); })(); // live demo: jsbin.com/zokuwodoco/1/edit?js,console.

또는

 +function(){ console.log('mamamia!'); }(); // live demo: jsbin.com/wuwipiyazi/1/edit?js,console

또는

 -function(){ console.log('mamamia!'); }(); // live demo: jsbin.com/wejupaheva/1/edit?js,console

따라서 일단 "대체 코드"에 해당 수정 사항이 추가되면 "코드 설명"예제에서 사용한 코드와 정확히 동일한 코드로 돌아갑니다.

var someFunction = function(){ console.log('wagwan!'); };

(function() {
  console.log('start of IIFE');

  var myNumber = 4;
  var myFunction = function(){ console.log('formidable!'); };
  var myObject = { 
    anotherNumber : 1001, 
    anotherFunc : function(){ console.log('formidable!'); }
  };
  console.log('end of IIFE');
})();

someFunction();            // reachable, hence works: see in the console
myFunction();              // unreachable, will throw an error, see in the console
myObject.anotherFunc();    // unreachable, will throw an error, see in the console

다음에 대해 자세히 알아보십시오 Expressions vs Statements.


범위 이해하기

한 가지 궁금한 점은 "함수 내에 변수를 '적절하게'정의하지 않으면 어떻게됩니까? 즉, 간단한 대입을 수행합니까?"

(function() {
  var myNumber = 4;             /* number variable declaration */
  var myFunction = function(){  /* function variable declaration */
    console.log('formidable!'); 
  };
  var myObject = {              /* object variable declaration */
    anotherNumber : 1001, 
    anotherFunc : function(){ console.log('formidable!'); }
  };
  myOtherFunction = function(){  /* oops, an assignment instead of a declaration */
    console.log('haha. got ya!');
  };
})();
myOtherFunction();         // reachable, hence works: see in the console
window.myOtherFunction();  // works in the browser, myOtherFunction is then in the global scope
myFunction();              // unreachable, will throw an error, see in the console

라이브 데모를 참조하십시오 .

기본적으로 현재 범위에서 선언되지 않은 변수에 값이 할당 된 경우 "변수를 찾거나 전역 범위에 도달 할 때까지 범위 체인을 조회합니다 (이 시점에서 변수가 생성됩니다)".

브라우저 환경 (nodejs와 같은 서버 환경)에서 전역 범위는 window객체에 의해 정의됩니다 . 따라서 우리는 할 수 있습니다 window.myOtherFunction().

이 주제에 대한 나의 "우수 사례"팁 은 숫자, 객체 또는 함수이든 전역 범위에 있을 때든 무엇이든 정의 할 때 항상 사용 var하는 것 입니다. 이렇게하면 코드가 훨씬 간단 해집니다.

노트 :

  • javascript 에는 (업데이트 : ES6에 추가 된 블록 범위 로컬 변수) 가 없습니다 .block scope
  • 자바 스크립트에는 function scope& 만 있습니다 global scope( window브라우저 환경에서 범위)

다음에 대해 자세히 알아보십시오 Javascript Scopes.


자원


다음 단계

IIFE개념 을 얻으면 module pattern이 IIFE 패턴을 활용하여 일반적으로 수행됩니다. 재미있게 보내세요 :)


매우 도움이됩니다. 고마워요!
Christoffer Helgelin이

니스, 나는 데모 버전을 선호한다 :)
Fabrizio Bertoglio

좋은 설명입니다. 감사합니다!
Vikram Khemlani

26

브라우저의 Javascript에는 실제로 함수 범위와 전역 범위의 두 가지 유효 범위 만 있습니다.

변수가 함수 범위에 없으면 전역 범위에 있습니다. 전역 변수는 일반적으로 나쁘기 때문에 라이브러리 변수를 자체적으로 유지하는 구조입니다.


1
그러나 생성자 함수 자체가 자체 변수에 대한 범위를 제공하지 않습니까?
Andrew Kou

1
예,이 라이브러리에 정의 된 각 함수는 고유 한 로컬 변수를 정의 할 수 있지만 라이브러리 외부에서 누출되지 않고 함수간에 변수를 공유 할 수 있습니다.
Gareth

@Gareth, 이것은 범위 내에서 "글로벌"변수를 허용합니다 (;
Francisco Presencia 2016.

2
@FranciscoPresencia "범위 내의 글로벌"은 기본적으로 "범위"의 의미이기 때문에 유용한 문구가 아닙니다. "전역"범위의 핵심은 다른 모든 범위가 액세스 할 수 있는 범위라는 입니다.
Gareth

19

이를 폐쇄라고합니다. 기본적으로 함수 내부의 코드를 밀봉하여 다른 라이브러리가 방해하지 않도록합니다. 컴파일 된 언어로 네임 스페이스를 만드는 것과 비슷합니다.

예. 내가 쓴다고 가정 해보십시오.

(function() {

    var x = 2;

    // do stuff with x

})();

이제 다른 라이브러리는 x내 라이브러리에서 사용하기 위해 만든 변수에 액세스 할 수 없습니다 .


7
용어에주의하십시오. 네임 스페이스는 네임 스페이스를 주소 지정하여 (일반적으로 접두사를 사용하여) 외부에서 변수에 액세스 할 수 있음을 의미합니다. 여기에 설명되어 있지 않은 자바 스크립트에서는 이것이 가능하지만
Gareth

네임 스페이스와 정확히 같지는 않지만 동의하려는 속성이있는 객체를 반환하여 비슷한 기능을 제공 할 수 있습니다 (function(){ ... return { publicProp1: 'blah' }; })();. 분명히 네임 스페이스와 완벽하게 평행하지는 않지만 그렇게 생각하는 것이 도움이 될 수 있습니다.
Joel

귀하의 예에서 x는 여전히 개인 변수입니다 ... IIFE로 래핑하더라도. 계속해서 함수 외부의 x에 액세스하려고하면 할 수 없습니다.
RayLoveless

포인트가 유효하지 않습니다. 다음 함수에서도 다른 라이브러리는 x에 액세스 할 수 없습니다. function () {var x = 2}
RayLoveless

@RayLoveless 동의합니다. 나는 그 주장과 모순되지 않는다. 사실, 나는이 답변의 마지막 문장과 같은 주장을했다.
Joel

8

함수 클로저를 다음과 같이 사용할 수 있습니다 일부 html5 객체에 대한 브라우저 지원을 결정하는이 방법 에서처럼 더 큰 표현식의 데이터 .

   navigator.html5={
     canvas: (function(){
      var dc= document.createElement('canvas');
      if(!dc.getContext) return 0;
      var c= dc.getContext('2d');
      return typeof c.fillText== 'function'? 2: 1;
     })(),
     localStorage: (function(){
      return !!window.localStorage;
     })(),
     webworkers: (function(){
      return !!window.Worker;
     })(),
     offline: (function(){
      return !!window.applicationCache;
     })()
    }

무엇입니까! 하다?
1.21 기가 와트

!! 값을 부울 (true / false) 표현으로 변환합니다.
Liam

7

변수를 로컬로 유지하는 것 외에도 전역 변수를 사용하여 라이브러리를 작성할 때 라이브러리에서 사용할 짧은 변수 이름을 지정할 수 있습니다. jQuery를 사용하면 jQuery.noConflict ()를 사용하여 jQuery를 가리키는 $ 변수를 비활성화 할 수 있으므로 jQuery 플러그인 작성에 자주 사용됩니다. 비활성화 된 경우 코드는 여전히 $를 사용할 수 있으며 다음과 같은 경우 중단되지 않습니다.

(function($) { ...code...})(jQuery);

3
  1. 같은 창에서 다른 방법 / 라이브러리와 충돌을 피하려면
  2. 글로벌 범위를 피하고 로컬 범위로 설정하십시오.
  3. 디버깅 속도를 높이려면 (로컬 범위)
  4. JavaScript에는 기능 범위 만 있으므로 코드 컴파일에도 도움이됩니다.

1

또한 스코프 함수에서 'strict strict'를 사용하여 코드가 "strict mode"에서 실행되도록해야합니다. 아래에 표시된 샘플 코드

(function() {
    'use strict';

    //Your code from here
})();

왜 엄격하게 사용해야합니까?
nbro

이 기사를 확인하십시오 : stackoverflow.com/questions/1335851/…
Neha Jain

실제로 질문에 대답하지 않습니다!
Pritam Banerjee

Pritam, 좋은 연습 사용. 답변을 투표하기 전에 적절한 조사를하십시오
Neha Jain

1
'엄격한 사용'은 나쁜 프로그래머를 스스로로부터 구합니다. 그리고 대부분의 프로그래머는 나쁜 프로그래머이기 때문에 그들이하지 말아야 할 일을하지 못하게하고 코드가 빠르게 엉망이되는 것을 막을 수 있습니다.
MattE
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.