2009-07-25 8 views
8

Gibt es irgendwelche Nachteile bei der Verwendung einer JavaScript "Klasse" mit diesem Muster?JavaScript "Klassen"

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(); 

Antwort

14

Was Sie in Ihrem Beispiel tun, ist nicht das "Klassen" -Muster, an das die Leute in JS denken - typischerweise denken die Leute an das eher "normale" Klassenmodell von Java/C#/C++/etc mit Bibliotheken gefälscht.

Stattdessen ist Ihr Beispiel ist eigentlich ziemlich normal und gut JS Design, aber der Vollständigkeit halber werde ich Verhalten Unterschiede diskutieren werden Sie zwischen den privaten und öffentlichen „Mitglieder“ Sie haben

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

Zugriff auf private von innen sehen Jede Ihrer Funktionen wird sehr viel schneller sein als der Zugriff auf public, da die Position private mit einer statischen Suche durch die JS-Engine relativ gut bestimmt werden kann. Versuche, auf public zuzugreifen, erfordern eine Suche, die meisten modernen JS-Engines führen ein gewisses Maß an Lookup-Caching aus, aber es ist immer noch teurer als ein einfacher Zugriff auf den var-Bereich.

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

Die gleichen Suchregeln für diese Funktionen anwenden wie bei der oben genannten Variable zugreift, der einzige wirkliche Unterschied (in Ihrem Beispiel) ist, dass, wenn Ihre Funktionen aufgerufen werden, sagen privatefn() vs this.publicfn(), privatefn wird immer die globale bekommen Objekt für this. Aber auch, wenn jemand tut

f = foo.publicfn; 
f(); 

Dann wird der Anruf an f wird das globale Objekt als thisaber wird es in der Lage sein, die private Variable zu ändern.

Der normalere Weg jedoch, öffentliche Funktionen zu tun (was löst die abgetrennte öffentliche Funktion ändern private Mitglieder Problem) ist öffentliche Funktionen auf den Prototyp, z.

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

Welche öffentlichen Funktionen zwingt nicht private Informationen zu ändern - es gibt einige Zeiten, in denen dies nicht möglich ist, aber es ist eine gute Übung, da es die Verwendung Speicher reduziert auch leicht nehmen:

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

vs

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

In Foo1 Sie für jede Instanz von Foo1 eine Kopie des Funktionsobjekt (nicht alle der Emory, nur das Objekt, z.B. new Foo1().f !== new Foo2().f), während in Foo2 nur ein einziges Funktionsobjekt vorhanden ist.

3

Das ist gut so weit, aber es gibt noch eine weitere Zugriffsebene, die Sie weggelassen haben.

this.publicfn ist wirklich eine privilegierte Methode, da es Zugriff auf private Mitglieder und Funktionen hat.

Methoden hinzufügen, die öffentlich sind, aber nicht privilegiert, ändern Sie den Prototyp wie folgt:

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

beachten Sie, dass diese Methode auf private Mitglieder FooClass keinen Zugriff hat, aber es ist durch jede Instanz von FooClass zugänglich.

Ein anderer Weg, dies zu erreichen, kehrt versteckt diese Methoden aus dem Konstruktor

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(); 

Grundsätzlich sind diese Methoden der Daten Hilfe Sie zu den traditionellen OOP-Techniken haften. Im Allgemeinen ist es eine gute Sache, das Verbergen und Einkapseln von Daten in Ihren Klassen zu verbessern. Die Gewährleistung einer geringen Kopplung macht es viel einfacher, die Dinge auf der Straße zu ändern, so dass es Ihnen zu Gute kommt, so wenig wie möglich öffentlich zu exponieren.

Für eine einfache Übersicht siehe https://developer.mozilla.org/en/Introduction_to_Object-Oriented_JavaScript und http://www.crockford.com/javascript/private.html für Details, wie Sie diese Dinge erreichen.

+1

Ihr FooClass Beispiel ist falsch. Die Semantik von new ist effektiv: "var foo = {}; foo .__ proto__ = FooClass.prototype; var temp = FooClass.call (temp); if (IsObject (temp)) foo = temp;". Das heißt, in Ihrem Beispiel wird foo das neue Objekt "{reallypublicfn: function() {...}}" sein, so dass es den FooClass-Prototyp oder die publicfn-Funktion – olliej

3

Der Hauptnachteil ist, dass Sie mit einer Kopie von publicfn für jede Instanz von FooClass enden werden. Wenn Sie eine Menge von FooClass Objekten werde zu schaffen, wäre es

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

nicht hat, dass diese beiden Möglichkeiten, publicfn zu erstellen, nicht gleichwertig sind . einer ist privelegiert und der andere nicht –

1

Es hängt Schreib effizienter sein, auf Ihre Bedürfnisse und die relative Performance. Javascript ist nicht die typsicherste Sprache und nicht sehr stark in Bezug auf die Sichtbarkeit der Mitglieder. Traditionell können Sie die Sichtbarkeit "privat", "öffentlich privilegiert" und "öffentlich" innerhalb eines Javascript-Typs haben.

können Sie erklären, private und öffentliche privilegierte Mitglieder mit:

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

In diesem Beispiel werden eine Funktion Schließung, die aus einer Funktionsdeklaration besteht, die Werte aus dem Geltungsbereich umfasst, wobei die Funktion definiert ist. Dies ist akzeptabel, wenn die Sichtbarkeit der Elemente erforderlich ist, jedoch zu einem Overhead führen kann. Der JavaScript-Interpreter kann die privateFn- oder publicFn-Definitionen nicht für jede Instanz wiederverwenden, da sie auf Variablen oder Funktionen im äußeren Bereich verweisen. Daher führt jede Instanz von FooClass zu zusätzlichem Speicherplatz für privateFn und publicFn. Wenn der Typ nur selten oder in Maßen verwendet wird, ist die Leistungseinbuße vernachlässigbar. Wenn der Typ sehr häufig auf der Seite verwendet wird oder wenn die Seite eher ein "AJAX" -Stil ist, bei dem der Speicher nicht so häufig freigegeben wird, da die Seite nicht entladen wird, kann die Strafe besser sichtbar sein.

Ein alternativer Ansatz ist die Verwendung von Prototyp-Mitgliedern. Dies sind nicht privilegierte öffentliche Mitglieder. Da Javascript nicht vollständig typsicher ist und nach dem Laden relativ einfach zu ändern ist, sind Typsicherheit und Sichtbarkeit der Elemente nicht so zuverlässig, um den Zugriff auf interne Typen zu steuern. Aus Gründen der Leistung verwenden einige Frameworks wie ASP.NET Ajax stattdessen Member-Naming, um Sichtbarkeitsregeln abzuleiten. Zum Beispiel könnte die gleiche Art in ASP.NET Ajax wie folgt aussehen:

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

In diesem Fall sind die privaten scoped Mitglieder nur dem Namen nach sind privat, das „_“ Präfix gilt eine private Variable bedeuten. Es hat den Nachteil, dass das Mitglied nicht wirklich privat ist, sondern das In-Memory-Patchen des Objekts zulassen. Der andere Hauptvorteil besteht darin, dass alle Funktionen einmal vom Interpreter und der Engine erstellt und immer wieder für den Typ wiederverwendet werden. Das Schlüsselwort "this" verweist dann auf die Instanz des Typs, obwohl die Funktionsreferenz selbst identisch ist.

Eine Möglichkeit, den Unterschied in Aktion zu sehen ist dies mit beiden Typen versuchen (wenn Sie nicht ASP.NET Ajax haben, können Sie die letzte Zeile in FooClass2 ignorieren, ruft register())

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 

Also ist es eine Frage des Typs Sicherheit und Sichtbarkeit der Mitglieder im Vergleich zuLeistung und die Fähigkeit, im Speicher zu patch

0

Auch, wenn ich etwas Nützliches zu diesem erwähnen kann - Mit können Sie zusätzliche Methoden später im Code hinzufügen, um die Funktion von einem externen Bereich zu erweitern. Als kleinen Vorteil wird der ganze Körper mit all den Methoden nicht jedes Mal gerendert, daher beschleunigt es das Zusammenstellen von Leistungen. Wenn Sie bereits alle deklarierten Methoden innerhalb der Funktion haben, würde das Rendern etwas länger dauern. Also, mein Ratschlag, wenden Sie diese später nur an, wenn sie im aktuellen Code relevant werden.

Beispiel:

// 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(); 
Verwandte Themen