2011-09-16 5 views
7
// Case A 
function Constructor() { 
    this.foo = function() { 
    ... 
    }; 
    ... 
} 

// vs 
// Case B 
function Constructor() { 
    ... 
}; 

Constructor.prototype.foo = function() { 
    ... 
} 

사람들이 프로토 타입의 사용을 조언하는 주된 이유 중 하나는 다른 접근법을 사용할 때 this.foo가 여러 번 만들어지는 프로토 타입의 경우 .foo이 한 번 만들어지는 것입니다.함수를 만드는 것은 더 많은 메모리를 소비합니까?

그러나 통역사가이를 최적화 할 것으로 기대합니다. 따라서 foofoo

함수의 복사본은 하나뿐입니다. 물론 클로저 때문에 각 개체에 대해 고유 한 범위 컨텍스트를 유지할 수 있지만 각 개체에 대한 오버 헤드가 적고 오버 헤드가 적습니다.

현대 JS 해석기는 사례 A를 최적화하여 foo 함수 하나만 복사합니까?

답변

8

예, 함수를 만드는 것은 더 많은 메모리를 사용합니다.

... 해석자는 사례 A를 단일 함수로 최적화하지 않습니다.

이유는 the JS scope chain은 함수의 각 인스턴스가 만들어 질 때 사용할 수있는 변수를 캡처해야하기 때문입니다. 즉, modern interpreters은 예전보다 사례 A에 대해서는 better이지만, 폐쇄 기능의 성능은 몇 년 전에 알려진 문제 였기 때문에 크게 나타났습니다.

Mozilla는 avoid unnecessary closures에게 이렇게 말합니다.하지만 클로저는 JS 개발자의 툴킷에서 가장 강력하고 자주 사용되는 도구 중 하나입니다.

업데이트 : 그냥 (V8, 크롬에서 JS 인터프리터 인) Node.js를을 사용하여 생성자의 1M '인스턴스'를 만들어 this test을 달렸다. caseA = true으로 나는이 메모리 사용을 얻을 :

{ rss: 212291584, 
vsize: 3279040512, 
heapTotal: 203424416, 
heapUsed: 180715856 } 

을 그리고 caseA = false와 나는이 메모리 사용을 얻을 : 거의 3 배에 의해

{ rss: 73535488, 
vsize: 3149352960, 
heapTotal: 74908960, 
heapUsed: 56308008 } 

그래서 폐쇄 기능이 확실히 훨씬 더 많은 메모리를 소모됩니다. 그러나 절대적인 의미에서 우리는 인스턴스 당 140-150 바이트의 차이에 대해서만 이야기합니다. (하지만 함수가 생성 될 때 범위 내 변수의 수에 따라 가능성이 커집니다.)

+0

"better"와 "modern interpreters"를 정의하는 몇 가지 참조가있을 수 있습니까 – Raynos

+0

테스트 결과는 제가 발견 한 것과 일치합니다. 또한 함수 내에 큰 코드 블록을 추가하여 테스트를 수행했는지 테스트합니다. 메모리 풍선이 더 빨리 - 그리고 그것은하지 않았다 ... 함수 안의 코드는 여분의 메모리를 필요로하지 않는다. – gnarf

+0

오 - 그리고 또한 개체가 생성되기 전에 diff를 측정하기 위해 테스트 전후의 메모리 사용량을 로그 아웃했습니다. – gnarf

0

자바 스크립트 인터프리터는 프로토 타입 객체를 최적화하지 않습니다. 그것의 단지 하나의 유형에 대해서만 하나의 경우 (그 여러 인스턴스 참조). 반면에 생성자는 새 인스턴스와 인스턴스 내에 정의 된 메서드를 만듭니다. 정의상 이것이 통역사 '최적화'의 문제가 아니라 일어나는 일을 단순히 이해하는 것입니다.

인터프리터가 인스턴스 메서드를 통합하고 통합하려는 경우 특정 인스턴스에서 값을 변경하기로 결정한 경우 문제가 발생할 수 있습니다 (두통을 언어에 추가하지 않는 것이 좋음)) :)

+0

컴파일러는 함수를 최적화하지 않지만 scopecontext를 최적화하지는 않습니다. – Raynos

+2

각 인터프리터가 중복을 처리하는 방법은 실제로 문제가 아닌 것 같지만 인터프리터는 여러 인스턴스 메소드간에 차이를 만들어야하며 그 차이가 더 많은 메모리를 소비해야합니다. – Marlin

+1

사례 A의 경우 컴파일러에서 생성자에 지역 변수가 없으므로 인스턴스의 범위 체인에서 변수 객체를 유지할 필요가 없습니다. 지역 변수가 사용된다면 내부 함수가 변수를 참조하지 않는 한 변수 객체를 최적화 할만큼 똑똑하지 않으면 그렇게 할 수 없습니다. 그럼에도 불구하고 프로토 타입 접근 방식을 깔끔하고 쉽게 유지 관리 할 것입니다 (물론 IMO). – RobG

2

노드에서 간략한 테스트를 한 후에 사례 A와 B 모두에서 메모리에 실제 코드 foo의 복사본이 하나만 있다고 생각합니다.

사례 A - 함수 코드에 대한 참조 및 현재 실행 범위를 저장하는 Constructor()의 각 실행에 대해 만들어진 함수 개체가 있습니다.

사례 B - 하나의 범위와 프로토 타입을 통해 공유되는 하나의 함수 개체 만 있습니다.

+0

테스트 코드를 게시 할 수 있습니까? 필자는 함수 코드가 매번 파싱되지 않도록 인터프리터 내부에서 최적화가 진행되고 있지만 생성자를 통과 할 때마다 범위 내 변수에 대한 참조를 캡처해야만 함수가 올바로 해석 될 수 있습니다. 호출됩니다. – broofa

+0

@broofa - 내 코드를 당신과 비교해 보면, 기본적으로 똑같은 ... :) - 테스트 할 함수 안에있는 40 줄의 코드를 버렸습니다. – gnarf

관련 문제