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


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.