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

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

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

Một trong những lý do chính người tư vấn cho việc sử dụng các nguyên mẫu là .foo được tạo ra một lần trong trường hợp của các nguyên mẫu nơi như this.foo được tạo ra nhiều lần khi sử dụng phương pháp khác.Liệu tạo chức năng tiêu thụ bộ nhớ hơn

Tuy nhiên, người ta mong đợi người phiên dịch có thể tối ưu hóa điều này. Vì vậy, chỉ có một bản sao của hàm foo trong trường hợp A.

Tất nhiên bạn vẫn sẽ có một ngữ cảnh phạm vi duy nhất cho từng đối tượng vì đóng cửa nhưng có ít chi phí hơn thì chức năng mới cho từng đối tượng.

Trình thông dịch JS hiện đại tối ưu hóa trường hợp A sao cho chỉ có một bản sao của hàm foo?

Trả lời

8

Có, tạo chức năng sử dụng nhiều bộ nhớ hơn.

... và, không, người phiên dịch không tối ưu hóa trường hợp A xuống một chức năng duy nhất.

Lý do là the JS scope chain yêu cầu mỗi phiên bản của hàm để nắm bắt các biến có sẵn cho nó tại thời điểm được tạo. Điều đó nói rằng, modern interpretersbetter về Case A so với trước đây, nhưng phần lớn là do hiệu suất của các hàm đóng đã là một vấn đề đã biết vài năm trước.

Mozilla nói với avoid unnecessary closures vì lý do này, nhưng đóng cửa là một trong những công cụ mạnh mẽ nhất và thường được sử dụng trong bộ công cụ của nhà phát triển JS.

Cập nhật: Chỉ cần chạy this test tạo ra trường hợp 1M 'Constructor, sử dụng node.js (V8, trình thông dịch JS trong Chrome). Với caseA = true tôi nhận được sử dụng bộ nhớ này:

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

Và với caseA = false tôi nhận được sử dụng bộ nhớ này:

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

Vì vậy, các chức năng đóng cửa chắc chắn tiêu thụ bộ nhớ nhiều hơn đáng kể, bởi hầu như 3X. Nhưng theo nghĩa tuyệt đối, chúng ta chỉ nói về sự khác biệt của ~ 140-150 byte mỗi trường hợp. (Tuy nhiên, điều đó có thể sẽ tăng tùy thuộc vào số lượng biến trong phạm vi mà bạn có khi hàm được tạo).

+0

Chúng tôi có thể có một số tham chiếu xác định "tốt hơn" và "thông dịch viên hiện đại" – Raynos

+0

Bài kiểm tra của bạn phù hợp với những gì tôi tìm thấy trong tôi --- Tôi cũng đã thêm một số khối mã lớn bên trong hàm để kiểm tra xem bộ nhớ bóng nhanh hơn - và nó không ... Mã bên trong hàm không mất thêm bộ nhớ. – gnarf

+0

Ồ - và tôi cũng đã đăng xuất sử dụng bộ nhớ trước và sau khi thử nghiệm để đo độ chênh lệch trước khi đối tượng được tạo ra – gnarf

0

Trình thông dịch javascript cũng không tối ưu hóa các đối tượng mẫu thử nghiệm. Nó chỉ đơn thuần là một trường hợp chỉ có một trong số chúng cho mỗi loại (nhiều tham chiếu của các cá thể). Các nhà xây dựng, mặt khác, tạo ra các cá thể mới và các phương thức được định nghĩa bên trong chúng. Vì vậy, theo định nghĩa, điều này thực sự không phải là vấn đề về tối ưu hóa thông dịch viên '' mà chỉ đơn giản là hiểu được những gì đang diễn ra. Trên một mặt lưu ý, nếu thông dịch viên thử và hợp nhất các phương pháp thể hiện bạn sẽ gặp phải nếu bạn quyết định thay đổi giá trị của một trong một trường hợp cụ thể (tôi muốn không thêm ngôn ngữ đó vào ngôn ngữ đó).) :)

+0

trình biên dịch tối ưu hóa chức năng, nó không tối ưu hóa scopecontext mặc dù. – Raynos

+2

Làm thế nào mỗi thông dịch viên xử lý các bản sao không thực sự giống như vấn đề, trình thông dịch sẽ cần thiết lập một sự khác biệt giữa nhiều phương pháp thể hiện và sự khác biệt đó sẽ tiêu thụ nhiều bộ nhớ hơn – Marlin

+1

Đối với trường hợp A, tôi mong đợi một trình biên dịch sẽ thấy rằng có không có biến cục bộ nào trong hàm tạo nên không cần phải giữ đối tượng biến của nó trên chuỗi phạm vi của các cá thể. Nếu các biến cục bộ được sử dụng, nó không thể làm điều đó trừ khi nó đủ thông minh để tối ưu hóa đối tượng biến đi nếu hàm bên trong không tham chiếu đến chúng. Bất kể, tôi sẽ sử dụng phương pháp tiếp cận nguyên mẫu làm chỗ ngồi của nó và dễ dàng duy trì hơn (IMO tất nhiên). – RobG

2

Tôi tin rằng, sau một số thử nghiệm ngắn gọn trong nút, trong cả hai trường hợp A và B chỉ có một bản sao của mã thực tế cho hàm foo trong bộ nhớ.

Trường hợp A - có đối tượng hàm được tạo cho mỗi lần thực hiện Constructor() lưu trữ tham chiếu đến mã chức năng và phạm vi thực thi hiện tại của nó.

Trường hợp B - chỉ có một phạm vi, một đối tượng hàm, được chia sẻ qua nguyên mẫu.

+0

Bạn có thể đăng mã thử nghiệm của mình không? Tôi chắc chắn có một số tối ưu hóa đang diễn ra bên trong trình thông dịch để tránh * phân tích cú pháp * mã chức năng mỗi lần, nhưng mỗi lần truyền qua hàm tạo phải nắm bắt các tham chiếu đến bất kỳ biến trong phạm vi nào để chúng có thể được giải quyết đúng khi hàm này được viện dẫn. – broofa

+0

@broofa - so sánh mã của tôi với máy của bạn, về cơ bản nó giống nhau ... :) - Tôi vừa bán được 40 dòng mã từ một thứ khác bên trong hàm để kiểm tra ... – gnarf

Các vấn đề liên quan