2009-06-03 13 views
10

Ich versuche, meinen Kopf rund um die Idee der Klassen, Datensichtbarkeit und Schließungen (speziell in Javascript) und ich bin auf der jQuery-Dokumentseite für Typen, es erwähnt, dass Verschlüsse zum Ausblenden von Daten verwendet werden :Javascript Closure und Datensichtbarkeit

Das Muster ermöglicht es Ihnen, Objekte mit Methoden zu erstellen, die mit Daten arbeiten, die nach außen nicht sichtbar sind - die Basis der objektorientierten Programmierung.

das Beispiel:

function create() { 
    var counter = 0; 
    return { 
    increment: function() { 
     counter++; 
    }, 
    print: function() { 
     console.log(counter); 
    } 
    } 
} 
var c = create(); 
c.increment(); 
c.print(); // 1 

durch den variablen Zähler mit dem Schlüsselwort var erklärt, ist es bereits vor Ort innerhalb der Funktion/Klassendefinition scoped. Soweit ich weiß und sagen kann, ist es von außen nicht zugänglich. Fehle ich etwas aus Sicht der Datensichtbarkeit?

Zweitens ist es ein Vorteil, dass die Klasse wie oben zu schreiben im Vergleich wie folgt:

function create() { 
    var counter = 0; 
    this.increment = function() { 
     counter++; 
    } 
    this.print = function() { 
     console.log(counter); 
    } 
    return this; 
} 
var c = create(); 
c.increment(); 
c.print(); // 1 

Wie ich es verstehe, sind diese mehr oder weniger semantisch die gleiche Sache - die erste ist nur mehr „jQuery Stil ". Ich frage mich nur, ob es einen Vorteil oder eine andere Nuance gibt, die ich aus dem ersten Beispiel nicht ganz verstehe. Wenn ich richtig liege, erzeugen beide Beispiele Schließungen, indem sie auf Daten zugreifen, die außerhalb ihres eigenen Bereichs deklariert wurden.

http://docs.jquery.com/Types#Closures

Antwort

8

Zunächst haben Sie Recht, dass beide Versionen Verschlüsse verwenden.

Die erste Version ist sauberer (meiner Meinung nach) und beliebter in modernen Javascript. Der größte potentielle Nachteil des ersten Stils besteht darin, dass Sie dem Prototyp des Konstruktors keine Objekte effektiv zuweisen können, was nützlich (und effizienter) ist, wenn Sie viele gleiche Objekte erstellen.

Der zweite Stil, ich habe eigentlich nie in der Produktion Javascript gesehen. Normalerweise würden Sie create mit new instanziiert, statt this in der create() Funktion zurückkehrt, etwa so:

function create() { 
    var counter = 0; 
    this.increment = function() { 
     counter++; 
    } 
    this.print = function() { 
     console.log(counter); 
    } 
} 
var c = new create(); 
c.increment(); 
c.print(); // 1 
+0

Und persönlich, das ist die Art, wie ich es vorziehe, Objekte zu erschaffen, nachdem ich unzählige Techniken ausprobiert habe. Ich denke, das ist so viel sauberer und einfacher zu verstehen. Aber das bin ich. :) –

+3

Minor nit: die Rückgabe von "this" ist gleichbedeutend damit, dass bei der Verwendung des neuen Operators überhaupt nichts zurückgegeben wird. Ohne den neuen Operator ist "this" der aktuelle Kontext (oft mal global), und das führt dazu, dass Sie globale Variablen überlisten. – ken

1

Ihr zweites Beispiel immer noch Verschlüsse verwendet werden, da die Schrittweite und Druckfunktionen auf eine Variable handeln noch sonst außerhalb des Gültigkeitsbereichs ist — durch die Zeit, die Sie c.increment() rufen die Funktion erstellen bereits verlassen hat.

Ich mag das erste Beispiel, weil es die „this“ Stichwort, und in javascript „this“ sein kann tückisch — vermeidet es nicht immer auf das bezieht, was es scheint, wie es sollte.

+0

Ich bin ziemlich sicher, die Handlung der Verwendung der Variable in der anonymen Funktion bedeutet, dass es persistent bleibt, obwohl die Funktion beendet wurde und normalerweise diese Variable verworfen würde. – Dave

2

Nun, es ist mir egal in einen religiösen Krieg zu bekommen, wie Objekte in JavaScript erstellen, da Manche Menschen sind der festen Überzeugung, dass es einen richtigen und einen falschen Weg gibt.

Allerdings möchte ich etwas in Ihrem zweiten Satz von Code darauf hinweisen, dass nicht zu pikant ist - nämlich die Tatsache, dass Sie Dinge neue Eigenschaften auf das Objekt im this Schlüsselwort zuweisen - wissen Sie, was das ist Objekt ist?Es ist nicht ein leeres Objekt, wenn Sie Instanziierung Syntax wie folgt verwendet werden:

var c = new create(); 

Wenn Sie das tun, das this Stichwort im Inneren des Körpers der Konstruktorfunktion ein ganz neues Objekt, als ob die erste Zeile zugeordnet ist in der Körper waren so etwas wie:

this = {}; 

Aber wenn man create() als eine Funktion aufrufen, wie Sie in diesem Beispiel tun, sind zu ändern Sie den Bereich außerhalb der Definition der Funktion (wie erwähnt-durch @seanmonster in den Kommentaren).

+0

Wenn Sie die Funktion keinem Prototyp oder anderem Objekt zuweisen, ist "dies" eigentlich das globale Objekt (Fenster). – seanmonstar

+0

Hee hee, mein Schlechter - die Grammatik deines Kommentars ist so subtil und ich nahm es, um etwas Gegenteiliges zu sagen. Das tut mir leid. Ich muss von diesen komischen Medikamenten abhauen ... –

+0

Ich schätze deine Antwort - obwohl sie normalerweise richtig ist, um genauer zu sein, wie ich in meiner jetzt bearbeiteten Antwort reflektiert habe, könnte das Schlüsselwort etwas anderes als das globale Objekt sein auf wo die Funktionsdefinition gefunden wird. Wenn seine "create" -Funktion innerhalb einer Konstruktorfunktion verschachtelt wäre, die vollständig gültig ist, würde sich das Schlüsselwort this von dem globalen Geltungsbereich unterscheiden. Also, noch einmal - ich weiß es zu schätzen, dass Sie das Problem lösen, denn es kann knifflige Angelegenheit sein ... –

1

Christian Heilmann hat einen ziemlich anständigen Artikel über the module pattern, den Sie beschreiben, der Ihnen helfen könnte, Ihren Kopf darum zu wickeln und warum es nützlich ist.

2

Sie sollten das Beispiel gegen diesen Schnipsel

function create() { 
    this.counter = 0; 
    this.increment = function() { 
     this.counter++; 
    }; 
    this.print = function() { 
     console.log(counter); 
    } 
} 

var c = new create(); 
c.increment(); 
c.print(); // 1 

So vergleichen, wenn neue erstellen() aufgerufen wird es das neue Objekt mit zwei Methoden und eine Instanzvariable (nämlich: Zähler) initialisiert. Javascript muss nicht Verkapselung per se, so dass Sie c.counter zugreifen können, wie folgt:

var c = new create(); 
c.increment(); 
c.counter = 0; 
c.print(); // 0 

Durch Verschlüsse (wie in Ihren Beispielen gezeigt) Zähler ist nun ein Beispiel Feld länger, sondern eine lokale Variable. Zum einen können Sie nicht von außerhalb der Funktion create() zugreifen. Andererseits können increment() und print() zugreifen, da sie sich über den umschließenden Bereich schließen. Also enden wir mit einer ziemlich guten Emulation der objektweisen Kapselung.

3

durch den variablen Zähler mit das Schlüsselwort var deklarieren, ist es bereits lokal innerhalb der Funktion/Klasse Definition scoped. Soweit ich weiß und erzählen kann, ist es von außen nicht zugänglich von . Fehle ich etwas aus einer Datensichtbarkeit Perspektive.

Es ist nicht, dass die counter Variable von außerhalb der Funktion nicht zugänglich ist, zu beginnen, dann ist es, dass es die increment und print Funktionen zugänglich ist nach create Funktion verlassen, dass closures so nützlich macht.

+0

+1 für die Beantwortung des Teils der Frage, die alle anderen aus irgendeinem Grund vermeiden –

0

In Ihrem zweiten Beispiel, wenn Sie create() im Rahmen der Funktion aufrufen, this ist das globale Objekt (was immer der Fall ist, wenn Sie eine "bare" -Funktion aufrufen, ohne es als Konstruktor oder Zugriff darauf verwenden als eine Eigenschaft (zB ein "Methoden" -Aufruf)). In Browsern ist das globale Objekt window. Also, wenn Sie nachfolgenden Zeiten erstellen aufrufen, erstellt er neue Verschlüsse, aber Sie sie dann wie zuvor mit dem gleichen globalen Objekt zuweisen, die alten Funktionen zu überschreiben, das ist nicht das, was Sie wollen:

var c = create(); // c === window 
c.increment(); 
c.print(); // 1 
var c2 = create(); // c2 === c === window 
c.print(); // 0 
c2.print(); // 0 
increment(); // called on the global object 
c.print(); // 1 
c2.print(); // 1 

Die Lösungen, wie andere haben darauf hingewiesen, ist zu verwenden new create().

1

Diese Syntax macht mehr Sinn für mich von einem OOP Hintergrund kommen:

Create = function { 
    // Constructor info. 

    // Instance variables 
    this.count = 10; 
} 

Create.prototype = { 

    // Class Methods 
    sayHello : function() { 
      return "Hello!"; 
    }, 

    incrementAndPrint : function() { 
      this.count++; 

      // Inner method call. 
      this.print(); 
    }, 

    print : function() { 
     return this.count; 
    } 


} 

var c = new Create(); 
alert(c.incrementAndPrint()); 
0
MYAPP = (function(){ 
    var v1,v2; 
    return { 
    method1:function(){}, 
    method2:function(){} 
    }; 
})(); 

ich immer verwende Verschlüsse wie dies in meiner Anwendung, wie dies zu tun, alle meine eigenen definierten Methoden in MYAPP Namespace , die v1 und v2 sind nur durch Methoden in MYAPP zugänglich.In meiner Anwendung schreibe ich oft nur eine "app.js" -Datei, alle meine js-Codes innerhalb. Ich denke, Sie können eine Methode definieren, die "Registrierung" genannt wird, um private Variable in MYAPP zu definieren, dann können Sie sie in Ihren Methoden verwenden. Alle zusätzlichen Variablen und Methoden sollten von der Registrierungsmethode definiert werden, wenn Sie zusätzliche Codes in der HTML-Datei hinzufügen möchten, genau wie die Methode JQuery.extend. Ich habe gehört, wenn zu viele Schließungen im IE-Browser verwenden, sind Sie leicht, Stapelüberlauf zu bekommen. (Meiner Meinung nach)