익명 함수를 사용하면 성능에 영향을 줍니까?


89

Javascript에서 명명 된 함수와 익명 함수를 사용하는 것 사이에 성능 차이가 있는지 궁금합니다.

for (var i = 0; i < 1000; ++i) {
    myObjects[i].onMyEvent = function() {
        // do something
    };
}

vs

function myEventHandler() {
    // do something
}

for (var i = 0; i < 1000; ++i) {
    myObjects[i].onMyEvent = myEventHandler;
}

첫 번째는 거의 사용되지 않는 함수로 코드를 복잡하게 만들지 않기 때문에 더 깔끔하지만 해당 함수를 여러 번 다시 선언하는 것이 중요합니까?


나는 그것이 질문에 있지 않다는 것을 알고 있지만 코드 청결성 / 가독성과 관련하여 '올바른 방법'이 중간 어딘가에 있다고 생각합니다. 드물게 사용되는 최상위 함수의 "클러 터"는 성가 시지만 호출과 함께 인라인으로 선언되는 익명 함수에 많이 의존하는 고도로 중첩 된 코드도 마찬가지입니다 (예 : node.js 콜백 지옥). 전자와 후자 모두 디버깅 / 실행 추적을 어렵게 만들 수 있습니다.
Zac B

아래 성능 테스트는 수천 번의 반복에 대해 함수를 실행합니다. 상당한 차이가 있더라도 대부분의 사용 사례는 해당 순서의 반복에서이를 수행하지 않습니다. 따라서 필요에 맞는 것을 선택하고이 특정 경우의 성능을 무시하는 것이 좋습니다.
사용자

물론 그 너무 오래된 질문의 @nickf하지만 새로운 업데이트 된 대답을 참조
이것을 Chandan Pasunoori

답변:


89

여기서 성능 문제는 익명 함수를 사용한다는 사실이 아니라 루프가 반복 될 때마다 새 함수 객체를 만드는 비용입니다.

for (var i = 0; i < 1000; ++i) {    
    myObjects[i].onMyEvent = function() {
        // do something    
    };
}

코드 본문이 같고 어휘 범위 ( closure )에 대한 바인딩이 없더라도 수천 개의 고유 한 함수 개체를 만들고 있습니다. 반면에 다음은 루프 전체의 배열 요소에 동일한 함수 참조를 할당하기 때문에 더 빠릅니다 .

function myEventHandler() {
    // do something
}

for (var i = 0; i < 1000; ++i) {
    myObjects[i].onMyEvent = myEventHandler;
}

루프에 들어가기 전에 익명 함수를 만든 다음 루프 내부에있는 동안 배열 요소에 대한 참조 만 할당하면 명명 된 함수 버전과 비교할 때 성능이나 의미 적 차이가 전혀 없음을 알 수 있습니다.

var handler = function() {
    // do something    
};
for (var i = 0; i < 1000; ++i) {    
    myObjects[i].onMyEvent = handler;
}

요컨대 이름이 지정된 함수에 대해 익명을 사용하는 데 따른 성능 비용은 관찰 할 수 없습니다.

제쳐두고 위에서 보면 다음과 같은 차이가없는 것처럼 보일 수 있습니다.

function myEventHandler() { /* ... */ }

과:

var myEventHandler = function() { /* ... */ }

전자는 함수 선언 이고 후자는 익명 함수에 대한 변수 할당입니다. 동일한 효과가있는 것처럼 보일 수 있지만 JavaScript는 약간 다르게 처리합니다. 차이점을 이해하기 위해“ JavaScript 함수 선언 모호성 ”을 읽는 것이 좋습니다 .

모든 접근 방식의 실제 실행 시간은 대개 브라우저의 컴파일러 및 런타임 구현에 의해 결정됩니다. 최신 브라우저 성능을 완전히 비교 하려면 JS Perf 사이트를 방문하십시오 .


함수 본문 앞의 괄호를 잊었습니다. 방금 테스트했는데 필수입니다.
Chinoto Vokro 2014 년

벤치 마크 결과는 매우 js 엔진에 의존하는 것 같습니다!
aleclofabbro

3
JS Perf 예제에 결함이 있습니까? 사례 1 은 함수 만 정의 하는 반면 사례 2 및 3은 실수로 함수를 호출 하는 것 같습니다 .
bluenote10

따라서이 추론을 사용하면 node.js웹 응용 프로그램을 개발할 때 익명 콜백을 만드는 것보다 요청 흐름 외부에서 함수를 만들고 콜백으로 전달하는 것이 더 낫다 는 의미 입니까?
Xavier T Mukodi

23

내 테스트 코드는 다음과 같습니다.

var dummyVar;
function test1() {
    for (var i = 0; i < 1000000; ++i) {
        dummyVar = myFunc;
    }
}

function test2() {
    for (var i = 0; i < 1000000; ++i) {
        dummyVar = function() {
            var x = 0;
            x++;
        };
    }
}

function myFunc() {
    var x = 0;
    x++;
}

document.onclick = function() {
    var start = new Date();
    test1();
    var mid = new Date();
    test2();
    var end = new Date();
    alert ("Test 1: " + (mid - start) + "\n Test 2: " + (end - mid));
}

결과 :
테스트 1 : 142ms 테스트 2 : 1983ms

JS 엔진이 Test2에서 동일한 기능임을 인식하지 못하고 매번 컴파일하는 것으로 보입니다.


3
이 테스트는 어떤 브라우저에서 수행 되었습니까?
andynil 2010

5
Chrome 23에서 나를위한 시간 : (2ms / 17ms), IE9 : (20ms / 83ms), FF 17 : (2ms / 96ms)
Davy8

당신의 대답은 더 많은 가치가 있습니다. Intel i5 4570S 사용 시간 : Chrome 41 (1/9), IE11 (1/25), FF36 (1/14). 분명히 루프의 익명 함수가 더 나빠집니다.
ThisClark

3
이 테스트는 보이는 것만 큼 유용하지 않습니다. 두 예 모두 내부 함수가 실제로 실행되는 것은 아닙니다. 실제로이 테스트는 함수를 생성하는 것이 함수를 한 번 생성하는 것보다 10000000 배 더 빠르다는 것을 보여줍니다.
Nucleon 2015

2

일반적인 디자인 원칙으로 동일한 코드를 여러 번 구현하지 않아야합니다. 대신 일반적인 코드를 함수로 가져와 여러 위치에서 해당 (일반적이고 잘 테스트되었으며 수정하기 쉬운) 함수를 실행해야합니다.

(귀하의 질문에서 추론 한 것과 달리) 내부 함수를 한 번 선언하고 해당 코드를 한 번 사용하는 경우 (그리고 프로그램에서 다른 항목이 동일하지 않은 경우) 아마도 익명 함수 (추측 사람들)가 동일한 방식으로 처리됩니다. 일반적인 명명 된 함수로 컴파일러.

특정 경우에 매우 유용한 기능이지만 많은 상황에서 사용해서는 안됩니다.


1

나는 큰 차이를 기대하지 않지만 만약 있다면 스크립팅 엔진이나 브라우저에 따라 다를 것입니다.

코드를 쉽게 찾을 수 있다면 함수를 수백만 번 호출 할 것으로 예상하지 않는 한 성능은 문제가되지 않습니다.


1

성능에 영향을 미칠 수있는 부분은 함수 선언 작업입니다. 다음은 다른 함수의 컨텍스트 내부 또는 외부에서 함수를 선언하는 벤치 마크입니다.

http://jsperf.com/function-context-benchmark

Chrome에서는 함수를 외부에서 선언하면 작업이 더 빠르지 만 Firefox에서는 그 반대입니다.

다른 예에서 내부 함수가 순수 함수가 아닌 경우 Firefox에서도 성능이 부족함을 알 수 있습니다. http://jsperf.com/function-context-benchmark-3


0

다양한 브라우저, 특히 IE 브라우저에서 루프를 더 빠르게 만드는 것은 다음과 같이 반복됩니다.

for (var i = 0, iLength = imgs.length; i < iLength; i++)
{
   // do something
}

루프 조건에 임의의 1000을 넣었지만 배열의 모든 항목을 살펴보고 싶다면 내 드리프트를 얻습니다.


0

참조는 거의 항상 참조하는 것보다 느립니다. 이렇게 생각해보세요. 1 + 1을 더한 결과를 인쇄한다고 가정 해 보겠습니다.

alert(1 + 1);

또는

a = 1;
b = 1;
alert(a + b);

나는 그것이 그것을 보는 매우 단순한 방법이라는 것을 알고 있지만 그것은 예시적인 것입니다. 여러 번 사용할 경우에만 참조를 사용하십시오. 예를 들어, 다음 예제 중 어느 것이 더 의미가 있습니까?

$(a.button1).click(function(){alert('you clicked ' + this);});
$(a.button2).click(function(){alert('you clicked ' + this);});

또는

function buttonClickHandler(){alert('you clicked ' + this);}
$(a.button1).click(buttonClickHandler);
$(a.button2).click(buttonClickHandler);

두 번째는 더 많은 줄이 있더라도 더 나은 연습입니다. 이 모든 것이 도움이 되었기를 바랍니다. (그리고 jquery 구문은 누구도 버리지 않았습니다)


0

@nickf

(댓글을 달 수있는 담당자가 있었으면하지만이 사이트는 방금 찾았습니다)

내 요점은 명명 / 익명 함수와 반복에서 실행 + 컴파일하는 사용 사례 사이에 혼란이 있다는 것입니다. 내가 설명했듯이 anon + named의 차이는 그 자체로는 무시할 만합니다. 잘못된 사용 사례라고 말하고 있습니다.

당연한 것 같지만, 최선의 조언은 "멍청한 일을하지 말라"(이 유스 케이스의 지속적인 블록 이동 + 객체 생성이 하나입니다)라고 생각하고 확실하지 않으면 테스트하십시오!



0

익명 개체는 명명 된 개체보다 빠릅니다. 그러나 더 많은 함수를 호출하는 것은 더 비싸고 익명 함수를 사용하여 얻을 수있는 비용 절감 정도를 능가합니다. 호출 된 각 함수는 호출 스택에 추가되어 작지만 사소한 양의 오버 헤드를 발생시킵니다.

그러나 암호화 / 복호화 루틴을 작성하거나 성능에 비슷하게 민감한 것을 작성하지 않는 한, 다른 많은 사람들이 언급했듯이 빠른 코드보다 우아하고 읽기 쉬운 코드를 최적화하는 것이 항상 좋습니다.

잘 설계된 코드를 작성한다고 가정하면 속도 문제는 인터프리터 / 컴파일러를 작성하는 사람들의 책임이어야합니다.


0

@nickf

그것은 다소 어려운 테스트이지만, 분명히 방법 1 (컴파일 N 번, JS 엔진에 따라 다름)과 방법 2 (한 번 컴파일)의 비용이 발생 하는 실행 및 컴파일 시간을 비교하고 있습니다. 그런 식으로 코드를 작성하는 보호 관찰을 통과하는 JS 개발자를 상상할 수 없습니다.

훨씬 더 현실적인 접근 방식은 익명 할당입니다. 실제로 document.onclick 메서드에 사용하는 것은 다음과 비슷하며 실제로 anon 메서드를 선호합니다.

유사한 테스트 프레임 워크 사용 :


function test(m)
{
    for (var i = 0; i < 1000000; ++i) 
    {
        m();
    }
}

function named() {var x = 0; x++;}

var test1 = named;

var test2 = function() {var x = 0; x++;}

document.onclick = function() {
    var start = new Date();
    test(test1);
    var mid = new Date();
    test(test2);
    var end = new Date();
    alert ("Test 1: " + (mid - start) + "ms\n Test 2: " + (end - mid) + "ms");
}

0

@nickf 답변에 대한 의견에서 지적했듯이

함수를 백만 번 생성하는 것보다 한 번 빠르게 생성하고 있습니다.

단순히 예입니다. 그러나 그의 JS 성능이 보여 주듯이, 그것은 시간이 지남에 따라 실제로 더 빨라지는 것을 보여주는 백만 배나 느리지 않습니다.

나에게 더 흥미로운 질문은 다음과 같습니다.

반복 된 생성 + 실행이 한 번 + 반복되는 실행 과 어떻게 비교 됩니까 ?

함수가 복잡한 계산을 수행하는 경우 함수 객체를 만드는 데 걸리는 시간은 거의 무시할 수 있습니다. 그러나 실행 이 빠른 경우 창조 의 오버 헤드는 어떻습니까? 예를 들면 :

// Variant 1: create once
function adder(a, b) {
  return a + b;
}
for (var i = 0; i < 100000; ++i) {
  var x = adder(412, 123);
}

// Variant 2: repeated creation via function statement
for (var i = 0; i < 100000; ++i) {
  function adder(a, b) {
    return a + b;
  }
  var x = adder(412, 123);
}

// Variant 3: repeated creation via function expression
for (var i = 0; i < 100000; ++i) {
  var x = (function(a, b) { return a + b; })(412, 123);
}

JS Perf 는 함수를 한 번만 만드는 것이 예상대로 더 빠르다는 것을 보여줍니다. 그러나 간단한 추가와 같은 매우 빠른 작업으로도 함수를 반복적으로 생성하는 오버 헤드는 몇 퍼센트에 불과합니다.

그 차이는 함수 객체를 생성하는 것이 복잡하고 실행 시간을 무시할 수있는 수준으로 유지하는 경우 (예 : 전체 함수 본문이 if (unlikelyCondition) { ... }.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.