2009-07-25 35 views
8

Có bất kỳ nhược điểm nào khi sử dụng "lớp" JavaScript với mẫu này không?JavaScript "lớp học"

var FooClass = function() 
{ 
    var private = "a private variable"; 
    this.public = "a public variable"; 

    var privatefn = function() { ... }; 
    this.publicfn = function() { ... }; 
}; 

var foo = new FooClass(); 
foo.public = "bar"; 
foo.publicfn(); 

Trả lời

14

Những gì bạn đang làm trong ví dụ của bạn không phải là mô hình "lớp" mà mọi người nghĩ đến trong JS - thường mọi người đang nghĩ đến mô hình lớp "bình thường" của Java/C#/C++/v.v. giả mạo với thư viện.

Thay vào đó ví dụ của bạn thực sự là khá bình thường và thiết kế JS tốt, nhưng cho đầy đủ tôi sẽ thảo luận về sự khác biệt hành vi bạn sẽ thấy giữa "thành viên" tư nhân và công cộng bạn có

var private = "a private variable"; 
this.public = "a public variable"; 

Truy cập private từ bên trong bất kỳ chức năng nào của bạn sẽ nhanh hơn rất nhiều so với việc truy cập public vì vị trí của private có thể được xác định khá tốt chỉ với một tra cứu tĩnh của công cụ JS. Nỗ lực truy cập public yêu cầu tìm kiếm, các công cụ JS hiện đại nhất thực hiện mức độ tra cứu bộ nhớ đệm, nhưng nó vẫn đắt hơn một truy cập var phức tạp đơn giản.

var privatefn = function() { ... }; 
this.publicfn = function() { ... }; 

Các quy tắc tra cứu tương tự áp dụng cho các chức năng như với biến trên truy cập, sự khác biệt duy nhất thực sự (trong ví dụ của bạn) là nếu chức năng của bạn được gọi là, nói privatefn() vs this.publicfn(), privatefn sẽ luôn nhận được sự toàn cầu đối tượng cho this. Nhưng cũng nếu có ai đó làm

f = foo.publicfn; 
f(); 

Sau đó, các cuộc gọi đến f sẽ có đối tượng toàn cầu như thisnhưng nó sẽ có thể thay đổi biến private.

Cách thông thường hơn để thực hiện các chức năng công cộng (giải quyết chức năng công cộng tách biệt sửa đổi vấn đề thành viên riêng) là đặt các chức năng công khai trên mẫu thử nghiệm, ví dụ:

Foo.prototype.publicfn = function() { ... } 

nào buộc các chức năng công cộng để không sửa đổi thông tin cá nhân - có một số lần, nơi đây không phải là một lựa chọn, nhưng đó là thực hành tốt vì nó cũng làm giảm sử dụng bộ nhớ một chút, thực hiện:

function Foo1() { 
    this.f = function(){ return "foo" }; 
} 

vs

function Foo2() { 
} 
Foo2.prototype.f = function(){ return "foo" }; 

trong Foo1 bạn có một bản sao của đối tượng chức năng cho mỗi thể hiện của Foo1 (không phải tất cả các emory, chỉ cần đối tượng, ví dụ. new Foo1().f !== new Foo2().f) trong khi ở Foo2 chỉ có một đối tượng hàm duy nhất.

3

Điều này tốt cho đến nay, nhưng có một cấp truy cập khác mà bạn đã bỏ qua.

this.publicfn thực sự là phương thức được cá nhân hóa vì nó có quyền truy cập vào các thành viên và chức năng riêng tư.

Để thêm các phương pháp đó là công khai nhưng không priveleged, sửa đổi các nguyên mẫu như sau:

FooClass.prototype.reallypublicfn = function() { ... }; 

lưu ý rằng phương pháp này không được tiếp cận với thành viên private của FooClass nhưng nó có thể truy cập thông qua bất kỳ trường hợp FooClass.

Một cách khác để đạt được điều này đang trở lại những phương pháp từ các nhà xây dựng

var FooClass = function() 
{ 
    var private = "a private variable"; 
    this.public = "a public variable"; 

    var privatefn = function() { ... }; 
    this.publicfn = function() { ... }; 

    return { 
    reallypublicfn: function() { ...} 
    } 
}; 

var foo = new FooClass(); 
foo.public = "bar"; 
foo.publicfn(); 

Về cơ bản, những phương pháp của dữ liệu ẩn giúp bạn tuân thủ kỹ thuật OOP truyền thống. Nói chung, cải thiện ẩn dữ liệu và đóng gói trong các lớp học của bạn là một điều tốt. Đảm bảo khớp nối thấp giúp dễ dàng thay đổi mọi thứ trên đường, vì vậy việc tiết lộ công khai càng ít càng tốt thực sự là lợi ích của bạn.

Xem https://developer.mozilla.org/en/Introduction_to_Object-Oriented_JavaScript để biết tổng quan đơn giản và http://www.crockford.com/javascript/private.html để biết chi tiết về cách hoàn thành những điều này.

+1

dụ FooClass của bạn không đúng. Các ngữ nghĩa mới có hiệu quả: "var foo = {}; foo .__ proto__ = FooClass.prototype; var temp = FooClass.call (tạm thời); if (IsObject (temp)) foo = temp;". Điều này có nghĩa trong ví dụ foo của bạn sẽ là đối tượng mới "{reallypublicfn: function() {...}}" vì vậy nó sẽ không có nguyên mẫu FooClass hoặc hàm publicfn – olliej

3

Nhược điểm chính là bạn sẽ kết thúc với một bản sao của publicfn cho mỗi trường hợp của FooClass. Nếu bạn sẽ được tạo ra rất nhiều đối tượng FooClass, nó sẽ hiệu quả hơn để ghi

FooClass.prototype.publicfn = function() { ... }; 
+1

lưu ý rằng hai cách tạo publicfn này không tương đương . một là tư nhân và người kia thì không –

1

Nó phụ thuộc vào nhu cầu của bạn và hiệu suất tương đối. Javascript không phải là loại ngôn ngữ an toàn nhất và không phải là rất mạnh liên quan đến khả năng hiển thị thành viên. Theo truyền thống, bạn có thể có chế độ hiển thị "riêng tư", "có đặc quyền công khai" và "công khai" trong một loại Javascript.

Bạn có thể khai báo các thành viên đặc quyền tư nhân và công cộng sử dụng:

function FooClass() 
{ 
    var privateVar = 1; 
    function privateFn() 
    { 
     return privateVar; // etc... 
    } 
    this.publicVar = 2; 
    this.publicFn = function() 
    { 
     return privateFn(); 
    } 
} 

Ví dụ này sử dụng một kết thúc chức năng, trong đó bao gồm một tuyên bố chức năng bao gồm các giá trị từ phạm vi nơi hàm được xác định. Điều này có thể chấp nhận được khi khả năng hiển thị thành viên là cần thiết nhưng có thể dẫn đến chi phí. Trình thông dịch JavaScript không thể sử dụng lại định nghĩa privateFn hoặc publicFn cho mọi instantiation vì chúng tham chiếu đến các biến hoặc hàm trong phạm vi bên ngoài. Kết quả là mọi trường hợp của FooClass dẫn đến không gian lưu trữ bổ sung cho privateFn và publicFn. Nếu loại này sử dụng không thường xuyên hoặc ở mức vừa phải thì hiệu suất phạt là neglegible. Nếu loại được sử dụng rất thường xuyên trong trang, hoặc nếu trang là nhiều hơn một phong cách "AJAX" nơi bộ nhớ không được giải phóng thường xuyên kể từ khi trang không được dỡ bỏ sau đó hình phạt có thể được nhìn thấy rõ hơn.

Cách tiếp cận khác là sử dụng thành viên mẫu thử nghiệm. Đây là những thành viên công khai không được phép. Vì Javascript không hoàn toàn an toàn loại và tương đối dễ sửa đổi sau khi được tải, loại an toàn và khả năng hiển thị của thành viên không đáng tin cậy để kiểm soát quyền truy cập vào loại nội bộ. Vì lý do hiệu suất, một số khung như ASP.NET Ajax thay vì sử dụng đặt tên thành viên để suy ra các quy tắc hiển thị. Ví dụ: cùng loại trong ASP.NET Ajax có thể trông giống như:

function FooClass2() 
{ 
    this._privateVar = 1; 
    this.publicVar = 2; 
} 
FooClass2.prototype = 
{ 
    _privateFn : function() 
    { 
     return this._privateVar; 
    }, 
    publicFn : function() 
    { 
     return this._privateFn(); 
    } 
} 
FooClass2.registerClass("FooClass2"); 

Trong trường hợp này, thành viên riêng lẻ chỉ có tên riêng, tiền tố "_" được coi là biến thành viên riêng. Nó có nhược điểm của việc ngăn chặn các thành viên được thực sự riêng tư, nhưng ngược lại cho phép vá trong bộ nhớ của đối tượng. Các lợi ích chính khác là tất cả các chức năng được tạo ra một lần bởi các thông dịch viên và động cơ và tái sử dụng hơn và hơn cho loại. Từ khóa "this" sau đó đề cập đến thể hiện của loại mặc dù tham chiếu hàm là chính nó.

Một cách để thấy sự khác biệt trong hành động là để thử điều này với cả hai loại (nếu bạn không có ASP.NET Ajax, bạn có thể bỏ qua những dòng cuối cùng trong FooClass2 mà các cuộc gọi registerClass())

var fooA = new FooClass(), fooB = new FooClass(); 
alert(fooA.publicFn===fooB.publicFn); // false 

var foo2A = new FooClass2(), foo2B = new FooClass2(); 
alert(foo2A.publicFn===foo2B.publicFn); // true 

Vì vậy, vấn đề an toàn loại và khả năng hiển thị thành viên so vớihiệu suất và khả năng vá trong bộ nhớ

0

Ngoài ra, nếu tôi có thể đề cập đến điều gì đó hữu ích cho điều này - Với prototype bạn có thể thêm các phương thức bổ sung sau này vào mã để mở rộng chức năng từ phạm vi bên ngoài. Như một lợi ích nhỏ, toàn bộ cơ thể với tất cả các phương pháp sẽ không được trả lại mọi lúc, do đó nó tăng tốc độ biên soạn các buổi biểu diễn. Nếu bạn có tất cả các phương thức được khai báo bên trong hàm, thì điều này sẽ mất nhiều thời gian hơn để render. Vì vậy, lời khuyên của tôi, áp dụng những điều này sau này chỉ khi chúng trở nên có liên quan trong mã hiện tại.

Ví dụ:

// inside 
function fn(arg) { 
    this.val = arg; 
    fn.prototype.getVal =()=> { 
     console.log(this.val); 
    } 
} 
var func = new fn('value'); 
func.getVal(); 


// declare extern methods 
function fn2(arg) { 
    this.val = arg; 
} 
fn2.prototype.getVal =()=> { 
    console.log(this.val); 
} 
var func2 = new fn2('value'); 
func2.getVal(); 
Các vấn đề liên quan