같은 줄에서 익명 함수를 호출해야하는 이유는 무엇입니까?


374

클로저에 대한 게시물을 읽고 있었으며 어디에서나 이것을 보았지만 어떻게 작동하는지에 대한 명확한 설명은 없습니다.

// Create a new anonymous function, to use as a wrapper
(function(){
    // The variable that would, normally, be global
    var msg = "Thanks for visiting!";

    // Binding a new function to a global object
    window.onunload = function(){
        // Which uses the 'hidden' variable
        alert( msg );
    };
// Close off the anonymous function and execute it
})();

Ok 새로운 익명 함수를 만든 다음 실행할 것입니다. 그런 다음이 간단한 코드가 작동해야합니다 (그렇습니다).

(function (msg){alert(msg)})('SO');

내 질문은 여기서 어떤 종류의 마술이 일어나는가? 나는 내가 쓸 때 다음과 같이 생각했다.

(function (msg){alert(msg)})

그러면 함수 ""(msg) ...와 같이 이름이 지정되지 않은 새로운 함수가 생성됩니다.

그런데 왜 이것이 작동하지 않습니까?

(function (msg){alert(msg)});
('SO');

왜 같은 줄에 있어야합니까?

게시물을 알려주거나 설명해 주시겠습니까?


2
다른 언어에서는 관련 하위 레벨 구조를 조사하려는 경우이를 함수 포인터 또는 대리자라고합니다.
— Chris Moschini

17
당신은; 첫 줄에
— Oliver Ni

작동 방식을 알았으므로 사용하지 마십시오. 익명 함수 작성 을 중단 해야 합니다 . 몇 문자 만 추가하면 함수에 실제 이름을 부여하고 자바 스크립트 코드를 훨씬 쉽게 디버깅 할 수 있습니다!
— Stijn de Witt

1
라인 (function (msg){alert(msg)})('SO');은 완전히 자체적으로 작동합니다. 이전에 게시 한 다른 익명 함수와는 아무런 관련이 없습니다. 그것들은 완전히 별개의 익명 기능입니다. 익명 함수는 이름이없고 나중에 참조 할 수 없기 때문에 즉시 호출해야합니다.
— Octopus

답변:


380

함수 정의 후에 세미콜론을 삭제하십시오.

(function (msg){alert(msg)})
('SO');

위의 작동해야합니다.

데모 페이지 : https://jsfiddle.net/e7ooeq6m/

이 게시물에서 이런 종류의 패턴에 대해 논의했습니다.

jQuery와 $ 질문

편집하다:

ECMA 스크립트 사양 을 보면 함수를 정의 할 수있는 3 가지 방법이 있습니다. (페이지 98, 섹션 13 기능 정의)

1. 함수 생성자 사용

var sum = new Function('a','b', 'return a + b;');
alert(sum(10, 20)); //alerts 30

2. 함수 선언 사용.

function sum(a, b)
{
    return a + b;
}

alert(sum(10, 10)); //Alerts 20;

3. 함수 표현

var sum = function(a, b) { return a + b; }

alert(sum(5, 5)); // alerts 10

따라서 선언과 표현의 차이점은 무엇입니까?

ECMA 스크립트 사양에서 :

FunctionDeclaration : 함수 식별자 (FormalParameterListopt) {FunctionBody}

FunctionExpression : 함수 식별자 (FormalParameterListopt) {FunctionBody}

함수 식별자에서 '식별자'는 선택 사항 입니다. 그리고 식별자를 제공하지 않으면 익명 함수를 만듭니다. 식별자를 지정할 수 없다는 의미는 아닙니다.

이것은 다음이 유효 함을 의미합니다.

var sum = function mySum(a, b) { return a + b; }

중요한 점은 mySum 함수 본문 내에서만 'mySum'을 사용할 수 있다는 것입니다. 다음 예를 참조하십시오.

var test1 = function test2() { alert(typeof test2); }

alert(typeof(test2)); //alerts 'undefined', surprise! 

test1(); //alerts 'function' because test2 is a function.

라이브 데모

이것을 다음과 비교하십시오

 function test1() { alert(typeof test1) };

 alert(typeof test1); //alerts 'function'

 test1(); //alerts 'function'

이 지식으로 무장 한 코드를 분석해 봅시다.

다음과 같은 코드가 있으면

    function(msg) { alert(msg); }

함수 표현식을 작성했습니다. 이 함수 표현식을 괄호로 묶어 실행할 수 있습니다.

    (function(msg) { alert(msg); })('SO'); //alerts SO.

1
그래, 왜? 왜 인라인이어야합니까? 얼마나 많은 공백을 사용하든 상관 없습니다.
— palig

9
내가 쓴 것처럼 세미콜론은 익명 함수 정의를 종료했습니다. 이름이 없기 때문에 (익명입니다!) 더 이상 전화를 걸 수 없습니다. 세미콜론을 넣지 않으면 기능을 계속 실행할 수 있습니다.
— SolutionYogi

이 경우 자동 세미콜론 삽입은 세미콜론을 넣을 것이라고 생각했지만 그렇지 않습니다. 네 말이 맞아
— Nosredna

1
Nosredna, JS는 세미콜론을 추가 할 때 임의로 동작합니다. 이 상세한 기사를 읽으십시오 : blog.boyet.com/blog/javascriptlessons/…
— SolutionYogi

예, (function (msg) {alert (msg)}) ( 'SO'); 공장. 나는 왜 그것이 효과가 있는지 묻고있었습니다. 이것이 지정되는 위치 또는 JS 기능의 종류. 그래서 일단 전화 : (function (msg) {alert (msg)}) 함수와 함께 무슨 일이 일어날까요? GC가 될까요?
— palig

129

이를 자체 호출 함수라고합니다.

호출 할 때 (function(){})함수 객체를 반환합니다. 추가하면 ()호출되고 본문의 모든 것이 실행됩니다. 는 ;2 호출이 실패하는 이유 즉, 문장의 끝을 의미한다.


아 알았어, 그래서 그것은 단지 특별한 JS의 구문 일뿐입니까? 이 설명처럼 가장! 간단하고 짧은 :)
— palig

몸이 '탈출'될 것이라고 말하는 것은 잘못된 생각입니다. 다른 함수와 마찬가지로 실행됩니다. 익명이므로 참조를 어딘가에 저장하거나 즉시 실행하십시오.
— SolutionYogi

16
개인적으로 나는 '자기 호출 기능'이라는 용어조차 좋아하지 않습니다. 그 기능이 자체적으로 호출되는 것은 아닙니다. 프로그래머는이를 괄호로 작성했습니다.
— SolutionYogi

다른 무엇보다 특별한 "구문"이 아닙니다. 실제로 "함수 이름 (args) {BLOCK}"형식은 훨씬 "특별"합니다. 실제로 불필요한 설탕입니다. 그러나 이것이 실제로 일어나게하는 것입니다.
— jrockway

2
기사에 대한 좋은 링크. "글로벌 객체를 보호하기 위해 모든 JavaScript 응용 프로그램은 자체 호출 기능 내에서 작성되어야합니다. 이렇게하면 변수가 충돌 할 염려없이 변수를 만들 수있는 응용 프로그램 범위가 만들어집니다." "다른 응용 프로그램과 함께." 또한 함수가 종료되면 변수가 삭제되고 전역 개체는 변경되지 않습니다.
— yeahdixon

94

내가 혼란스럽게 생각한 것은 "()"가 그룹화 연산자라는 것입니다.

기본적으로 선언 된 함수는 다음과 같습니다.

전의. 1:

var message = 'SO';

function foo(msg) {
    alert(msg);
}

foo(message);

함수는 객체이며 그룹화 할 수 있습니다. 함수 주위에 parens를 던져 보자.

전의. 2 :

var message = 'SO';

function foo(msg) {  //declares foo
    alert(msg);
}

(foo)(message);     // calls foo

이제 동일한 함수를 선언하고 즉시 호출하는 대신 기본 대체를 사용하여 호출 할 때 선언 할 수 있습니다.

전의. 삼.

var message = 'SO';

(function foo(msg) {
    alert(msg);
})(message);          // declares & calls foo

마지막으로 이름을 사용하지 않기 때문에 추가 foo가 필요하지 않습니다! 함수는 익명 일 수 있습니다.

전의. 4.

var message = 'SO';

(function (msg) {   // remove unnecessary reference to foo
    alert(msg);
})(message);

질문에 대답하려면 예 2를 다시 참조하십시오. 첫 번째 줄은 이름없는 함수를 선언하고 그룹화하지만 호출하지는 않습니다. 두 번째 줄은 문자열을 그룹화합니다. 둘 다 아무것도하지 않습니다. (Vincent의 첫 번째 예)

(function (msg){alert(msg)});  
('SO');                       // nothing.

(foo); 
(msg); //Still nothing.

그러나

(foo)
(msg); //works

6
감사. 당신의 예는 아주 분명했습니다. JavaScript의 괄호가 이런 식으로 코드의 의미를 바꿀 수 있다는 것을 알지 못했습니다. 나는 Java 배경에서 왔으므로 거의 매일 JavaScript를 사용하는 새로운 (그리고 종종 예기치 않은) 것을 배웁니다.
— hotshot309

5
단계별로 해 주셔서 감사합니다. 이것은 내가 본 다른 설명보다 훨씬 낫습니다. +1
— Wk_of_Angmar

2
AHA의 주요 순간은 여기에 있습니다. +100
— FredTheWebGuy

1
익명 함수에 대해 읽은 최고의 설명 중 하나입니다. 고마워요!
— Teknotica

23

익명 함수는 이름이 ""인 함수가 아닙니다. 단순히 이름이없는 함수입니다.

JavaScript의 다른 값과 마찬가지로 함수는 이름을 만들 필요가 없습니다. 실제로 다른 값과 마찬가지로 이름에 바인딩하는 것이 훨씬 더 유용합니다.

그러나 다른 값과 마찬가지로 이름에 바인딩하지 않고 사용하려는 경우가 있습니다. 이것이 자기 호출 패턴입니다.

다음은 함수와 숫자이며 바인딩되지 않습니다. 아무 것도하지 않으며 절대 사용할 수 없습니다 :

function(){ alert("plop"); }
2;

따라서 다른 값과 마찬가지로 변수를 변수에 저장하여 사용할 수 있습니다.

var f = function(){ alert("plop"); }
var n = 2;

구문 설탕을 사용하여 함수를 변수에 바인딩 할 수도 있습니다.

function f(){ alert("plop"); }
var n = 2;

그러나 이름을 지정할 필요가없고 더 혼란스럽고 가독성이 떨어지면 바로 사용할 수 있습니다.

(function(){ alert("plop"); })(); // will display "plop"
alert(2 + 3); // will display 5

여기서 내 함수와 숫자는 변수에 바인딩되지 않지만 여전히 사용할 수 있습니다.

이와 같이, 자기 호출 기능에는 실제 가치가없는 것처럼 보입니다. 그러나 JavaScript 범위 구분 기호는 블록이 아니라 함수라는 것을 명심해야합니다 ({}).

따라서 자체 호출 함수는 실제로 C ++, C # 또는 Java 블록과 동일한 의미를 갖습니다. 이는 내부에서 생성 된 변수가 범위 외부에서 "누설"되지 않음을 의미합니다. 이것은 전역 범위를 오염시키지 않기 위해 JavaScript에서 매우 유용합니다.


좋은 포스트. 그러면 'function () {alert ( "plop"); } '내가 그것을 실행할 때? GC가 될까요?
— palig

2
function () {alert ( "plop"); } 명령어는 단지 함수를 할당하지만 함수를 실행하거나 변수에 바인딩하지 않습니다. 작성된 함수는 변수에 바인드되지 않으므로 빠르게 GC됩니다.
— Vincent Robert

이 SO 스레드 는 여기서 이야기하는 범위를 넘어서지 만 JavaScript 네임 스페이스를 분리하는 방법을 설명하고 자체 호출 기능을 사용하는 예제를 포함합니다.
— hotshot309

19

JavaScript가 작동하는 방식입니다. 명명 된 함수를 선언 할 수 있습니다.

function foo(msg){
   alert(msg);
}

그리고 그것을 부르십시오 :

foo("Hi!");

또는 익명 함수를 선언 할 수 있습니다.

var foo = function (msg) {
    alert(msg);
}

그리고 그것을 부르십시오 :

foo("Hi!");

또는 함수를 이름에 바인딩 할 수 없습니다.

(function(msg){
   alert(msg);
 })("Hi!");

함수는 함수를 반환 할 수도 있습니다.

function make_foo() {
    return function(msg){ alert(msg) };
}

(make_foo())("Hi!");

본문에 "var"로 정의 된 변수가 다음 make_foo에 의해 반환 된 각 함수에 의해 닫히는 것은 가치가 없습니다.make_foo . 이것은 폐쇄이며, 한 기능에 의해 변경된 값이 다른 기능에 의해 보일 수 있음을 의미합니다.

원하는 경우 정보를 캡슐화 할 수 있습니다.

function make_greeter(msg){
    return function() { alert(msg) };
}

var hello = make_greeter("Hello!");

hello();

거의 모든 프로그래밍 언어이지만 Java가 작동하는 방식입니다.


8

당신이 보여주는 코드

(function (msg){alert(msg)});
('SO');

두 문장 으로 구성됩니다 . 첫 번째는 함수 객체를 생성하는 표현식입니다 (저장되지 않기 때문에 가비지 수집 됨). 두 번째는 문자열을 생성하는 표현식입니다. 함수를 문자열에 적용하려면, 문자열이 생성 될 때 (위에도 표시됨) 함수에 인수로 문자열을 전달하거나 실제로 함수를 변수에 저장해야합니다. 나중에 여가에 적용하십시오. 이렇게 :

var f = (function (msg){alert(msg)});
f('SO');

변수에 익명 함수 (람다 함수)를 저장하면 효과적으로 이름을 부여 할 수 있습니다. 따라서 일반 함수를 정의 할 수도 있습니다.

function f(msg) {alert(msg)};
f('SO');

7

이전 의견을 요약하면 다음과 같습니다.

function() {
  alert("hello");
}();

변수에 지정되지 않으면 구문 오류가 발생합니다. 코드는 함수 구문 (또는 정의)으로 구문 분석되어 닫는 괄호가 구문 상 올바르지 않게됩니다. 함수 부분에 괄호를 추가하면 인터프리터 (및 프로그래머)에게 다음과 같이 함수 표현식 (또는 호출)임을 알 수 있습니다.

(function() {
  alert("hello");
})();

이것은 자체 호출 기능으로, 익명으로 작성되고 선언 된 동일한 행에서 호출이 발생하므로 즉시 실행됩니다. 이 자체 호출 함수는 인수없는 함수를 호출하는 친숙한 구문과 함수 이름 주위에 괄호를 추가하여 표시됩니다 (myFunction)();.

이 좋은의 SO 토론 자바 스크립트 함수 구문은 .


3

이 답변은 질문과 엄격하게 관련이 없지만 이러한 종류의 구문 기능은 함수에만 국한되지 않는다는 것을 알고 자 할 것입니다. 예를 들어, 우리는 항상 다음과 같이 할 수 있습니다 :

alert(
    {foo: "I am foo", bar: "I am bar"}.foo
); // alerts "I am foo"

기능과 관련이 있습니다. 그것들은 Function.prototype에서 상속받은 객체이므로 다음과 같이 할 수 있습니다.

Function.prototype.foo = function () {
    return function () {
        alert("foo");
    };
};

var bar = (function () {}).foo();

bar(); // alerts foo

그리고 함수를 실행하기 위해 함수를 괄호로 묶을 필요조차 없습니다. 어쨌든 결과를 변수에 할당하려고 시도하는 한.

var x = function () {} (); // this function is executed but does nothing

function () {} (); // syntax error

함수를 선언하자마자 함수로 할 수있는 또 다른 일은 new연산자 를 호출하고 객체를 얻는 것입니다. 다음은 동일합니다.

var obj = new function () {
    this.foo = "bar";
};

var obj = {
    foo : "bar"
};

3

JavaScript 함수에는 하나 이상의 속성이 있습니다. 동일한 익명 함수를 재귀 적으로 호출하려는 경우.

(function forInternalOnly(){

  //you can use forInternalOnly to call this anonymous function
  /// forInternalOnly can be used inside function only, like
  var result = forInternalOnly();
})();

//this will not work
forInternalOnly();// no such a method exist

2
+1 작은 샘플을 추가하여 더 명확 해졌습니다 :-) 처음 읽을 때 4 번 다시 읽어야했습니다.
— xanatos

3

질문자의 질문에 대한 나의 이해는 다음과 같습니다.

이 마술은 어떻게 작동합니까?

(function(){}) ('input')   // Used in his example

내가 틀렸을 수도 있습니다. 그러나 사람들이 익숙한 일반적인 관행은 다음과 같습니다.

(function(){}('input') )

그 이유는 JavaScript 괄호 AKA ()가 명령문을 포함 할 수없고 구문 분석기가 함수 키워드를 발견하면이를 함수 선언이 아니라 함수 표현식으로 구문 분석하는 것입니다.

출처 : 블로그 게시물 즉시 호출 함수 표현 (IIFE)


3

대괄호가없는 예 :

void function (msg) { alert(msg); }
('SO');

(이것은 void, afaik의 유일한 실제 사용입니다)

또는

var a = function (msg) { alert(msg); }
('SO');

또는

!function (msg) { alert(msg); }
('SO');

잘 작동합니다. 는 void할당과 빅뱅뿐만 아니라, 표현식이 평가하는 원인이된다. 마지막은 함께 작동 ~, +, -, delete, typeof, 단항 연산자 일부는 ( void물론이다). 작동하지 않는이 나오긴의 있습니다 ++, --때문에 변수의 요구 사항으로.

줄 바꿈이 필요하지 않습니다.


ie11의 @Bergi가 delete작동합니다. 와도 'use strict';. delete (3 + 4);
— Nina Scholz

죄송합니다. " 2) Type (ref)이 Reference가 아닌 경우 true를 반환합니다. "해결할 수없는 실제 참조에 대해서만 오류가 발생합니다.
— Bergi

1

자체 실행 익명 기능입니다. 첫 번째 대괄호 세트는 실행될 표현식을 포함하고 두 번째 대괄호 세트는 해당 표현식을 실행합니다.

(function () {
    return ( 10 + 20 );
})();

Peter Michaux , 중요한 괄호 쌍의 차이점에 대해 설명 .

부모 네임 스페이스에서 변수를 숨기려고 할 때 유용한 구성입니다. 함수 내의 모든 코드는 함수의 전용 범위에 포함되어 있으므로 함수 외부에서 전혀 액세스 할 수 없으므로 실제로는 비공개입니다.

보다:

  1. 폐쇄 (컴퓨터 과학)
  2. 자바 스크립트 네임 스페이스
  3. 자바 스크립트 괄호의 중요한 쌍

0

또 다른 관점

먼저 익명 함수를 선언 할 수 있습니다.

var foo = function(msg){
 alert(msg);
}

그런 다음 호출하십시오.

foo ('Few');

때문에 foo는 = 기능 (MSG) {경고 (MSG);} 당신은 교체 할 수 있도록 foo는 같은 :

function(msg){
 alert(msg);
} ('Few');

그러나 구문 분석 할 때 함수를 선언하는 구문 오류가 발생하지 않도록 전체 익명 함수를 중괄호로 묶어야합니다. 그런 다음

(function(msg){
 alert(msg);
}) ('Few');

이렇게하면 쉽게 이해할 수 있습니다.


0

당신이했을 때 :

(function (msg){alert(msg)});
('SO');

('SO')세미콜론으로 인해 이전에 기능을 종료했습니다 . 방금 쓴다면 :

(function (msg){alert(msg)})
('SO');

작동합니다.

실제 예 : http://jsfiddle.net/oliverni/dbVjg/


0

작동하지 않는 간단한 이유 ;는 익명 함수의 끝을 나타내는 것이 아닙니다 . 를 빼고 때문이다 ()함수 호출의 끝에,이 함수를 호출하지 않습니다. 그건,

function help() {return true;}

당신이 호출하면 result = help();이 함수를 호출하고 true를 돌려줍니다.

전화하면 result = help; 가 아닙니다. 도움말이 결과에 할당 될 데이터처럼 취급되는 할당입니다.

당신이 한 일은 세미콜론을 추가하여 익명의 함수를 선언 / 인스턴스화하는 것입니다.

(function (msg) { /* Code here */ });

그런 다음 괄호를 사용하여 다른 명령문에서 호출하려고 시도했습니다 ... 함수에는 이름이 없기 때문에 분명히 작동하지 않습니다.

('SO');

인터프리터는 두 번째 줄의 괄호를 새로운 지시 / 문으로 간주하므로 다음과 같이해도 작동하지 않습니다.

(function (msg){/*code here*/});('SO');

여전히 작동하지 않지만 인터프리터는 공백과 캐리지를 무시하고 전체 코드를 하나의 명령문으로 간주하므로 세미콜론을 제거하면 작동합니다.

(function (msg){/*code here*/})        // This space is ignored by the interpreter
('SO');

결론 : 함수 호출은 ()다른 함수에 의해 호출되는 것과 같은 특정 조건, 즉 onload = 'help'가 괄호가 포함되지 않은 경우에도 도움말 기능을 실행하지 않는 한 끝이없는 함수 호출이 아닙니다. 나는 setTimeout과 setInterval도 이러한 유형의 함수 호출을 허용한다고 생각하며, 인터프리터는 장면 뒤에 괄호를 추가하여 "함수 호출은 괄호가없는 함수 호출이 아닙니다."


왜 이것이 많은 다운 보트를 받았는지 이해할 수 없습니다. 나는 그것이 적절한 대답이라고 생각합니까? : /
— Daniel Cheung

0
(function (msg){alert(msg)})
('SO');

많은 JavaScript 프레임 워크에서 사용하는 클로저로 익명 함수를 사용하는 일반적인 방법입니다.

이 함수는 코드가 컴파일 될 때 자동으로 호출됩니다.

;첫 번째 줄에 배치 하면 컴파일러는 두 줄을 다른 줄로 처리했습니다. 따라서 위와 동일한 결과를 얻을 수 없습니다.

이것은 또한 다음과 같이 쓸 수 있습니다 :

(function (msg){alert(msg)}('SO'));

자세한 내용은 JavaScript / Anonymous Functions 를 참조하십시오 .


내가 아는 한, JavaScript는 "컴파일"되지 않습니다
— Daniel Cheung

0
  1. 익명 함수는 런타임에 동적으로 선언되는 함수입니다. 일반 함수와 같은 방식으로 이름이 부여되지 않기 때문에 익명 함수라고합니다.

    익명 함수는 함수 선언 대신 함수 연산자를 사용하여 선언됩니다. 함수 연산자를 사용하여 식을 배치 할 수있는 곳이면 어디에서나 새 함수를 만들 수 있습니다. 예를 들어, 새로운 함수를 함수 호출에 대한 매개 변수로 선언하거나 다른 객체의 속성을 할당 할 수 있습니다.

    명명 된 함수의 일반적인 예는 다음과 같습니다.

    function flyToTheMoon () {alert ( "줌! 줌! 줌!"); } flyToTheMoon (); 다음은 익명 함수와 동일한 예입니다.

    var flyToTheMoon = function () {alert ( "줌! 줌! 줌!"); } flyToTheMoon ();

    자세한 내용은 여기를 참조하십시오 :

    http://helephant.com/2008/08/23/javascript-anonymous-functions/


0

IIFE는 단순히 함수를 구획화 msg하고 전역 네임 스페이스를 "오염"시키지 않도록 변수를 숨 깁니다 . 실제로 10 억 달러의 웹 사이트를 구축하지 않는 한 간단하게 유지하고 다음과 같이하십시오.

var msg = "later dude";
window.onunload = function(msg){
  alert( msg );
};

다음 과 같은 공개 모듈 패턴을msg 사용 하여 속성의 네임 스페이스를 지정할 수 있습니다.

var myScript = (function() {
    var pub = {};
    //myscript.msg
    pub.msg = "later dude";
    window.onunload = function(msg) {
        alert(msg);
    };
    //API
    return pub;
}());

-1

익명 함수는 제공하는 입력에서 출력을 생성 할 수 있도록 즉시 함수를 정의하는 원샷 거래입니다. 입력을 제공하지 않은 것을 제외하고. 대신, 당신은 두 번째 줄 ( 'SO')에 무언가를 썼습니다; -기능과 관련이없는 독립적 인 진술. 무엇을 기대 했습니까? :)


100 % 정확하지 않습니다. 이것은 익명의 기능이기도하며 재사용되어야 var foo = function() {};합니다. 그래도 다른 것은 괜찮습니다.
— Felix Kling
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.