2013-02-06 4 views
7

Wir haben diskutiert, wie man am besten mit Objekten in unserer JS-App umgeht, Stoyan Stefanovs Buch studiert, endlose SO-Posts über 'neu', 'dieses', 'Prototyp', Schließungen usw. gelesen hat dass es so viele gibt, und sie haben so viele konkurrierende Theorien, schlägt vor, dass es keine völlig offensichtliche Antwort gibt).Was ist los mit dieser Art der Programmierung von JavaScript? (Verschlüsse gegen Prototypen)

Also lass uns annehmen, dass wir uns nicht um private Daten kümmern. Wir sind zufrieden damit, Benutzern und Entwicklern zu vertrauen, dass sie sich nicht in Objekten aufhalten, die außerhalb der Definition liegen.

Angesichts dieser, was (abgesehen davon, dass es jahrzehntelang von OO Stil und Geschichte zu trotzen scheint) wäre falsch mit dieser Technik?

// namespace to isolate all PERSON's logic 
var PERSON = {}; 

// return an object which should only ever contain data. 
// The Catch: it's 100% public 

PERSON.constructor = function (name) { 
    return { 
     name: name 
    } 
} 

// methods that operate on a Person 
// the thing we're operating on gets passed in 

PERSON.sayHello = function (person) { 
    alert (person.name); 
} 

var p = PERSON.constructor ("Fred"); 
var q = PERSON.constructor ("Me"); 

// normally this coded like 'p.sayHello()' 
PERSON.sayHello(p); 
PERSON.sayHello(q); 

Offensichtlich:

  1. Es gäbe nichts, jemanden zu stoppen ‚p‘ in unheiliger Weise mutiert, oder einfach die Logik des Mensch verbreitet alle über den Platz zu landen. (Das stimmt auch mit der kanonischen 'neuen' Technik).
  2. Es wäre ein kleiner Aufwand, um 'p' in jede Funktion übergeben, die Sie wollten es verwenden.
  3. Dies ist ein seltsamer Ansatz.

Aber sind diese gut genug Gründe, es zu entlassen? Auf der positiven Seite:

  1. Es ist effizient, wie (angeblich) zu Schließungen mit repetitiver Funktionsdeklaration gegenüber.
  2. Es scheint sehr einfach und verständlich, im Gegensatz zu fummeln mit 'dies' überall.

Der entscheidende Punkt ist die vorhergehende Privatsphäre. Ich weiß, dass ich dafür geknallt werde, aber auf der Suche nach einem Feedback. Prost.

+0

Gibt es irgendeinen Punkt in Person Konstruktor mit diesem Ansatz? benutze einfach duck-typing 'PERSON.sayHello ({Name:" Fred "})' – house9

+0

meine Lieblingsansätze mit js - 1) benutze coffeescript, keine Heilung, sondern gibt die Illusion von OO-Klassen - 2) Funktionelle Konstruktor Vorlage aus " JavaScript: Die guten Teile "pg 52 - http://www.amazon.com/JavaScript-Good-Parts-Douglas-Crockford/dp/0596517742 – house9

+0

@ Haus9, aber ein Allheilmittel für was? Klassen sind nur Syntax und Syntax, die in einem vorkompilierten Paradigma mehr Sinn ergeben. Warum hängen die Leute so auf, wenn sie JS lernen wollen? Der Schlüssel zum Verständnis von JS ist das Verständnis von JS-Funktionen. Danach sind Konstrukteure ein Kinderspiel. Es ist nichts gebrochen über JS Ansatz zu OOP. Es braucht viel weniger Code, um die meisten OOP-orientierten Muster in JS zu manipulieren als die vorkompilierten Sprachen, mit denen er normalerweise verglichen wird. –

Antwort

8

Da ist nichts in sich falsch. Es wird jedoch auf viele Vorteile verzichtet, die mit dem Prototypsystem von Javascript verbunden sind.

  • Ihr Objekt weiß nichts über sich selbst, außer dass es ein Objektliteral ist. So wird instanceof Ihnen nicht helfen, seinen Ursprung zu identifizieren. Du wirst feststecken nur mit der Ente tippen.
  • Ihre Methoden sind im Wesentlichen Namespaced statische Funktionen, wo Sie sich wiederholen müssen, indem Sie das Objekt als erstes Argument übergeben. Indem Sie ein prototypisiertes Objekt haben, können Sie dynamischen Versand nutzen, so dass p.sayHello() verschiedene Dinge für PERSON oder ANIMAL tun können, je nachdem, welchen Typ Javascript kennt. Dies ist eine Form von Polymorphismus. Ihr Ansatz erfordert, dass Sie den Typ jedes Mal, wenn Sie eine Methode aufrufen, benennen (und möglicherweise einen Fehler machen).
  • Sie brauchen eigentlich keine constructor Funktion, da Funktionen bereits Objekte sind.Ihre PERSON Variable kann auch die Konstruktorfunktion sein.
  • Was Sie hier getan haben, ist ein Modul Muster erstellen (wie ein Namespace).

    Hier ist ein weiteres Muster, das hält, was Sie haben, sondern liefert die oben genannten Vorteile:

    function Person(name) 
    { 
        var p = Object.create(Person.prototype); 
        p.name = name; // or other means of initialization, use of overloaded arguments, etc. 
        return p; 
    } 
    Person.prototype.sayHello = function() { alert (this.name); } 
    
    var p = Person("Fred"); // you can omit "new" 
    var q = Person("Me"); 
    p.sayHello(); 
    q.sayHello(); 
    console.log(p instanceof Person); // true 
    var people = ["Bob", "Will", "Mary", "Alandra"].map(Person); 
         // people contains array of Person objects 
    
    +0

    Ich entschuldige mich dafür, dass ich nicht früher akzeptiert habe, vielen Dank für die ausgezeichneten Antworten. Prost! – GregT

    1

    Ja, verstehe ich wirklich nicht, warum Sie den Konstruktor Ansatz ausweichen sind versuchen, oder warum sie sogar einen Filz Englisch: www.doc-o-matic.com/webhelp/TdlgEditE.html Es ist notwendig, syntaktischen Zucker über Funktionskonstruktoren (Object.create und bald classes) zu legen, wenn Konstruktoren selbst ein eleganter, flexibler und vollkommen vernünftiger Ansatz für OOP sind, egal wie viele Gründe von Leuten wie Crockford dafür gegeben werden, sie nicht zu mögen (weil Leute vergessen, das neue Schlüsselwort zu benutzen - ernsthaft?). JS ist stark funktionsorientiert und seine OOP-Mechanik unterscheidet sich nicht. Es ist besser, dies zu umarmen als sich davor zu verstecken, IMO.

    Zu allererst Ihre Punkte unter "Offensichtlich" in JavaScript

    1. kaum noch erwähnenswert aufgeführt. Hohe Veränderlichkeitsgrade sind By-Design. Wir haben keine Angst vor uns selbst oder anderen Entwicklern in JavaScript. Das private vs. public-Paradigma ist nicht nützlich, weil es uns vor Dummheit schützt, sondern weil es es einfacher macht, die Intention hinter dem Code des anderen Entwicklers zu verstehen.

    2. Der Aufwand beim Aufrufen ist nicht das Problem. Der Ärger kommt später, wenn es unklar ist, warum Sie getan haben, was Sie dort getan haben. Ich sehe nicht wirklich, was Sie erreichen wollen, dass die Kernsprachenansätze nicht besser für Sie sind.

    3. Dies ist JavaScript. Es ist seit Jahren für JS Devs seltsam. Schwitzen Sie das nicht, wenn Sie einen besseren Weg finden, etwas zu tun, das besser in der Lage ist, ein Problem in einer bestimmten Domäne zu lösen, als dies bei einer typischen Lösung der Fall wäre. Stellen Sie nur sicher, dass Sie den Punkt des typischen Ansatzes verstehen, bevor Sie versuchen, ihn zu ersetzen, so wie es viele von anderen Sprachparadigmen zu JS haben. Es ist einfach, mit JS triviale Sachen zu machen, aber sobald du an dem Punkt bist, an dem du mehr OOP-getrieben bekommst, kannst du alles über die Kernsprache lernen, so dass du ein wenig mehr Skepsis auf verbreitete Meinungen verbreiten kannst von Leuten, die ein Seitenleben machen und JavaScript so gestalten, dass es gruseliger und voller tödlicher Sprengfallen ist, als es wirklich ist.

    Jetzt Ihre Punkte unter "positive Seite"

    1. Zunächst einmal, sich wiederholende Funktionsdefinition war wirklich nur in schweren Looping Szenario etwas zu befürchten. Wenn Sie regelmäßig Objekte in ausreichend großer Menge erstellen, die schnell genug für die nicht-prototypisierten öffentlichen Methodendefinitionen ein Perf-Problem darstellen, werden Sie wahrscheinlich in kürzester Zeit mit Speichernutzungsproblemen bei nicht-trivialen Objekten konfrontiert. Ich spreche jedoch in der Vergangenheitsform, weil es nicht mehr wirklich relevant ist. In modernen Browsern sind Funktionen, die in anderen Funktionen definiert sind, in der Regel leistungssteigernd, da moderne JIT-Compiler funktionieren. Unabhängig davon, welche Browser Sie unterstützen, sind einige pro Objekt definierte Funktionen kein Problem, es sei denn, Sie erwarten Zehntausende von Objekten.

    2. In der Frage von einfach und verständlich, es ist nicht für mich, weil ich nicht sehe, welchen Sieg Sie hier gewonnen haben.Anstatt ein Objekt zu verwenden, muss ich nun sowohl das Objekt als auch den Pseudo-Konstruktor zusammen verwenden. Wenn ich die Definition nicht ansehe, würde das für mich die Funktion bedeuten, die Sie mit einem "neuen" Schlüsselwort zum Erstellen von Objekten verwenden . Wenn ich neu in Ihrer Codebasis wäre, würde ich eine Menge Zeit damit verschwenden, herauszufinden, warum Sie es so gemacht haben, um zu vermeiden, andere Bedenken zu brechen, die ich nicht verstanden habe.

    Meine Fragen seien:

    Warum nicht einfach fügen Sie alle Methoden in der Objektliteral im Konstruktor in erster Linie? Da gibt es kein Performance-Problem, und das war noch nie so. Der einzige andere mögliche Gewinn ist, dass Sie neue Methoden hinzufügen können, nachdem Sie neue Objekte damit erstellt haben, aber das ist der Prototyp für richtige Konstruktoren (Prototyp-Methoden btw eignen sich hervorragend für Speicher in älteren Browsern, da sie nur einmal definiert sind).

    Und wenn Sie das Objekt weitergeben müssen, damit die Methoden wissen, was die Eigenschaften sind, warum wollen Sie überhaupt Objekte? Warum nicht nur Funktionen, die einfache Datenstrukturobjekte mit bestimmten Eigenschaften erwarten? Es ist nicht wirklich OOP mehr.

    Aber mein Hauptkritikpunkt

    Sie sind der wichtigste Punkt der OOP fehlt, das ist etwas, JavaScript von Menschen einen besseren Job nicht versteckt hat, als die meisten Sprachen. Betrachten Sie das folgende:

    function Person(name){ 
        //var name = name; //<--this might be more clear but it would be redundant 
        this.identifySelf = function(){ alert(name); } 
    } 
    
    var bob = new Person(); 
    bob.identifySelf(); 
    

    Nun ändern Sie den Namen Bob mit identifiziert, ohne das Objekt zu überschreiben oder die Methode, die beide Dinge, die Sie würde nur tun, wenn es klar waren Sie wollten nicht mit der Arbeit Objekt wie ursprünglich entworfen und konstruiert. Du kannst natürlich nicht. Das macht jedem, der diese Definition sieht, klar, dass der Name in diesem Fall tatsächlich eine Konstante ist. In einem komplexeren Konstruktor würde es feststellen, dass das einzige, was erlaubt ist, Namen zu ändern oder zu modifizieren, die Instanz selbst ist, es sei denn, der Benutzer hat eine nicht-validierende Setter-Methode hinzugefügt, die albern wäre, weil das im Grunde (Sie sehen Java Enterprise Beans) ZENTRALER ZWECK VON OOP.

    klare Aufteilung der Verantwortung ist der Schlüssel

    die Schlüsselwörter Vergessen sie für eine Sekunde in jedem Buch setzen und darüber nachdenken, was der Sinn ist. Vor OOP war alles nur ein Haufen von Funktionen und Datenstrukturen, auf die alle diese Funktionen eingingen. Mit OOP haben Sie meistens eine Reihe von Methoden, die mit einer Menge von Daten gebündelt sind, die nur das Objekt selbst tatsächlich verändert.

    Sagen wir also etwas mit Ausgang schief gegangen ist:

    • In unserem rein verfahrens Haufen von Funktionen gibt es keine wirkliche Begrenzung für die Anzahl der Hände, die diese Daten nach oben haben messed könnte. Wir könnten eine gute Fehlerbehandlung haben, aber eine Funktion könnte so verzweigen, dass der ursprüngliche Schuldige schwer aufzuspüren ist.

    • In einer richtigen OOP-Design, wo Daten in der Regel hinter einem Objekt-Gatekeeper ist weiß ich, dass nur ein Objekt tatsächlich die Änderungen verantwortlich machen kann.

    Objekte alle ihre Daten die meiste Zeit auszusetzen ist wirklich nur marginal besser als die alte prozeduralen Ansatz.Alles, was Sie wirklich tun, ist, Ihnen einen Namen zu geben, mit dem Sie losgelöste Methoden kategorisieren können.

    Much Ado About 'this'

    Ich habe nie verstanden, die ungebührliche auf das 'this' Schlüsselwort zugeordnet Aufmerksamkeit chaotisch und verwirrend sein. Es ist wirklich nicht so eine große Sache. 'this' identifiziert die Instanz, mit der Sie arbeiten. Das ist es. Wenn die Methode nicht als Eigenschaft aufgerufen wird, wird sie nicht wissen, nach welcher Instanz gesucht werden soll. Daher wird standardmäßig das globale Objekt verwendet. Das war dumm (undefined wäre besser gewesen), aber es funktioniert nicht richtig in diesem Szenario sollte in einer Sprache erwartet werden, in der Funktionen auch wie Daten tragbar sind und sehr leicht an andere Objekte angehängt werden können. Verwenden Sie "dies" in einer Funktion, wenn:

    • Es ist definiert und als eine Eigenschaft einer Instanz aufgerufen.

    • Es ist als Event-Handler übergeben (die es als ein Mitglied der Sache, die angehört wird nennen).

    • Sie verwenden Call- oder Apply-Methoden, um sie temporär als Eigenschaft eines anderen Objekts zu bezeichnen, ohne sie als solche zu definieren.

    Aber denken Sie daran, es ist die Berufung, die wirklich zählt. Das Zuweisen einer öffentlichen Methode zu einigen Variablen und das Aufrufen von dieser Variable führen zu einer globalen Aktion oder zu einem Fehler im strikten Modus. Ohne als Objekteigenschaften referenziert zu werden, interessieren sich Funktionen nur wirklich für den Umfang, in dem sie definiert wurden (ihre Schließungen) und für die Art und Weise, wie Sie sie übergeben.

    +0

    Vielen Dank für die ausgezeichnete Antwort. Prost – GregT