2015-05-27 21 views
7

Tôi biết có những câu hỏi tương tự như thế này, nhưng tôi muốn xem liệu những câu trả lời đó vẫn là các tối ưu hóa hợp lệ trong các công cụ Javascript mới.Chức năng bên trong hàm dựng vs nguyên mẫu

Theo tôi, lợi ích lớn nhất về việc xác định chức năng bên trong constructor là bạn có thể dễ dàng tránh được việc phải biết giá trị của từ khoá 'này':

var Person = function() { 
    var self = this; 
    self.firstName = null; 
    self.lastName = null; 
    self.fullName = function() { 
    return self.firstName + self.lastName; 
    }; 
}; 

Cách tiếp cận này được khuyến khích bởi Knockout Managing 'this'. Đây là một lợi thế lớn, đặc biệt là khi mã được sửa đổi bởi nhiều nhà phát triển vì nó thực sự đơn giản để hiểu và sử dụng.

Cách tiếp cận khác sẽ được sử dụng nguyên mẫu đối tượng:

var Person = function() { 
    this.firstName = null; 
    this.lastName = null; 
}; 
Person.prototype.fullName = function() { 
    return this.firstName + this.lastName; 
}; 

Trong trường hợp này có ưu điểm hiệu suất vì chức năng đối tượng sẽ được tạo ra một lần. Tuy nhiên vấn đề chính tôi có với nó là nó có thể phức tạp để xử lý các từ khóa 'này'. Ví dụ trên là rất đơn giản, nhưng nếu bạn có trình xử lý sự kiện, forEach calls, jQuery each() các cuộc gọi, các phương thức được gọi từ các ngữ cảnh khác nhau, v.v., thật dễ dàng để sử dụng điều này.

Tất nhiên, nếu bạn hiểu cách 'này' hoạt động và nhận thức được cách thức các phương thức được gọi, bạn không nên có nhiều vấn đề. Tuy nhiên, theo kinh nghiệm của tôi, điều này cần có thời gian và rất dễ xảy ra lỗi, đặc biệt khi mã được tạo bởi nhiều nhà phát triển.

Tôi biết rằng các công cụ JS mới, như V8, đang áp dụng tối ưu hóa cho các trường hợp bạn khai báo hàm bên trong hàm tạo bằng cách tạo các lớp ẩn: How the V8 engine works?. Vì vậy, câu hỏi của tôi là, với những tối ưu hóa được thực hiện bởi các công cụ JS mới và sự phức tạp của việc phải xử lý từ khóa 'này', liệu nó vẫn có ý nghĩa để sử dụng phương pháp tiếp cận dựa trên nguyên mẫu không? Không. Những gì tôi sẽ mất bằng cách sử dụng cách tiếp cận của việc đưa tất cả mọi thứ bên trong constructor?

CẬP NHẬT 1:

Tôi vừa thực hiện một điểm chuẩn vi mô trên Chrome (phiên bản 42). Tôi tạo các đối tượng 1M với các hàm bên trong hàm tạo và các hàm trong nguyên mẫu. Nó là một đối tượng rất đơn giản với hai biến và ba hàm và kết quả giống như thế này:

Functions inside constructor: 1.91 seconds 
Functions in prototype: 1.10 seconds 

Âm thanh như ngay cả với những tối ưu hóa đó trong V8 vẫn nhanh hơn 73%. Tuy nhiên đây là một điểm chuẩn nhỏ. Không chắc chắn nếu đó sẽ là một sự khác biệt lớn trong các ứng dụng thế giới thực.

CẬP NHẬT 2:

Tôi cũng xem xét mức tiêu thụ bộ nhớ và cũng có những khác biệt lớn. Đối với các chức năng bên trong cấu trúc:

Shallow size: 64,000,120 
Retained size: 336,001,128 

cho các chức năng nguyên mẫu:

Shallow size: 40,000,000 
Retained size: 40,000,000 

Hoặc tối ưu hóa với lớp ẩn không phải là tốt hay tôi đang thiếu một cái gì đó về điều đó. Tôi đang sử dụng mã monomorphic (constructors không có args) theo đề nghị của V8, nhưng không chắc chắn nếu tôi đang làm một cái gì đó sai.

UPDATE 3:

Dưới đây là liên kết của bài kiểm tra tôi đã làm trong trường hợp ai đó có thể chỉ ra một cái gì đó sai trong đó: http://jsperf.com/dg-constructor-vs-prototype

+0

FWIW, tôi đã thêm một trường hợp thử nghiệm thứ 3, các "POJO" mà không sử dụng nguyên mẫu hoặc từ khóa "mới", nó chỉ tạo ra một đối tượng với phương pháp nào và biến tư nhân. Nó có thể so sánh về hiệu năng với ví dụ về hàm tạo. Dù bằng cách nào, các phương pháp nguyên mẫu luôn luôn nhanh hơn, mặc dù với sự phức tạp thêm. http://jsperf.com/dg-constructor-vs-prototype/2 –

+0

Cảm ơn Scott. Vâng, có vẻ như vào thời điểm này, cách hiệu quả nhất là sử dụng nguyên mẫu. Đó là một sự xấu hổ bởi vì tôi yêu sự đơn giản của việc gán 'điều này' cho 'bản thân' và chắc chắn rằng 'tự' chỉ vào đối tượng thay vì phải suy nghĩ 'cái này' trỏ tới điều gì. – dgaviola

+0

Vâng, tôi hoàn toàn đồng ý. Đối với các đối tượng đơn giản (không thừa kế), sử dụng 'prototype' là không có gì khác hơn là tối ưu hóa vi mô, với nhược điểm là phải hiểu và quản lý cẩn thận việc sử dụng' this'. –

Trả lời

3

tôi thực hiện một thử nghiệm nhanh. Nếu bạn khai báo hàm trong hàm tạo, hai cá thể đối tượng có các thể hiện hàm khác nhau ngay cả sau khi tối ưu hóa. Tuy nhiên với nguyên mẫu, bạn chỉ có một thể hiện của hàm giải thích sự khác biệt hiệu suất.

var Person = function() { 
 
     var self = this; 
 
     self.firstName = null; 
 
     self.lastName = null; 
 
     self.fullName = function() { 
 
      return self.firstName + self.lastName; 
 
     }; 
 
    }; 
 

 
    Person.prototype.fullName2 = function() { 
 
     return this.firstName + this.lastName; 
 
    }; 
 

 
    var a = new Person(); 
 
    var b = new Person(); 
 

 
    console.log(a.fullName == b.fullName); // returns false 
 
    console.log(a.fullName2 == b.fullName2); // returns true

+0

Có, tôi đã cập nhật câu hỏi với một số bài kiểm tra tôi đã làm. Không nên tạo một chức năng khi sử dụng tối ưu hóa? Tôi tự hỏi nếu các đối tượng cần phải được tạo ra trong một số cách đặc biệt để tận dụng tối ưu hóa đó. – dgaviola

+0

Dường như không có cách nào để tối ưu hóa. Mặc dù v8 tạo các lớp ẩn, các hàm bên trong hàm tạo là các biến mẫu. Cách giải quyết duy nhất tôi có thể nghĩ là sử dụng nguyên mẫu và, như 'self', xác định' var pro = Person.prototype; '. Sau đó thay vì 'self.fullName = function()' sử dụng 'pro.fullName = function()'. Ít nhất là mã sẽ giống nhau :) Đối với vấn đề 'this', một cái gì đó như http://think-robot.com/2009/06/hitch-object-oriented-event-handlers-with-jquery/ cho jQuery có thể được áp dụng . –

+0

Lợi thế của việc sử dụng 'self' là bạn có thể sử dụng bất cứ nơi nào biết nó sẽ trỏ đến đối tượng. Tôi đang sử dụng một số cách giải quyết tương tự như những gì bạn nói, nhưng bạn cần phải nhớ để làm điều đó. Nhưng vâng, có lẽ không còn lựa chọn nào khác để có hiệu quả. – dgaviola

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