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


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.