it-swarm.com.de

Welche Techniken können verwendet werden, um eine Klasse in JavaScript zu definieren, und welche Kompromisse haben sie?

Ich bevorzuge die Verwendung von OOP in großen Projekten wie dem, an dem ich gerade arbeite. Ich muss mehrere Klassen in JavaScript erstellen, aber wenn ich mich nicht irre, gibt es zumindest ein paar Möglichkeiten, dies zu tun. Was wäre die Syntax und warum würde es so gemacht werden?

Ich möchte die Verwendung von Bibliotheken von Drittanbietern vermeiden - zumindest zunächst.
Auf der Suche nach anderen Antworten habe ich den Artikel Objektorientierte Programmierung mit JavaScript, Teil I: Vererbung - Doc JavaScript gefunden, der die objektorientierte Programmierung in JavaScript behandelt. Gibt es eine bessere Möglichkeit, Erbschaft zu leisten? 

674
Karim

So gehen Sie vor, ohne externe Bibliotheken zu verwenden:

// Define a class like this
function Person(name, gender){

   // Add object properties like this
   this.name = name;
   this.gender = gender;
}

// Add methods like this.  All Person objects will be able to invoke this
Person.prototype.speak = function(){
    alert("Howdy, my name is" + this.name);
};

// Instantiate new objects with 'new'
var person = new Person("Bob", "M");

// Invoke methods like this
person.speak(); // alerts "Howdy, my name is Bob"

Nun ist die wirkliche Antwort viel komplexer. In JavaScript gibt es beispielsweise keine Klassen. JavaScript verwendet ein prototype-basiertes Vererbungsschema. 

Darüber hinaus gibt es zahlreiche beliebte JavaScript-Bibliotheken, die ihren eigenen Stil haben, um klassenähnliche Funktionen in JavaScript anzunähern. Sie sollten mindestens Prototype und jQuery ausprobieren. 

Die Entscheidung, welche davon die "Beste" ist, ist eine großartige Möglichkeit, einen heiligen Krieg gegen Stack Overflow zu beginnen. Wenn Sie sich mit einem größeren, JavaScript-schweren Projekt befassen, lohnt es sich auf jeden Fall, eine beliebte Bibliothek zu lernen und auf ihre Weise zu tun. Ich bin ein Prototyp, aber der Stack Overflow scheint sich auf jQuery zu neigen.

Soweit es nur "einen Weg gibt", ohne Abhängigkeiten von externen Bibliotheken, ist es meine Art, wie ich schrieb. 

730
Triptych

Die beste Methode zum Definieren einer Klasse in JavaScript besteht darin, keine Klasse zu definieren.

Ernst.

Es gibt verschiedene Arten der Objektorientierung. Einige davon sind: 

  • klassenbasiert OO (zuerst von Smalltalk eingeführt)
  • prototypbasiert OO (erstmals von Self eingeführt)
  • multimethod-based OO (zuerst von CommonLoops eingeführt, denke ich)
  • prädikatbasiert OO (keine Ahnung)

Und wahrscheinlich andere, von denen ich nichts weiß.

JavaScript implementiert prototypbasierte OO. Beim prototypbasierten OO werden neue Objekte erstellt, indem andere Objekte kopiert werden (anstatt von einer Klassenvorlage instanziiert zu werden). Methoden leben direkt in Objekten und nicht in Klassen. Die Vererbung erfolgt per Delegierung: Wenn ein Objekt keine Methode oder Eigenschaft hat, wird es nach Prototyp (en) (d. H. Dem Objekt, aus dem es geklont wurde), den Prototypen des Prototyps usw. gesucht.

Mit anderen Worten: Es gibt keine Klassen.

JavaScript hat tatsächlich einen Nice Tweak dieses Modells: Konstruktoren. Sie können Objekte nicht nur durch Kopieren vorhandener Objekte erstellen, sondern auch sozusagen "aus der Luft" konstruieren. Wenn Sie eine Funktion mit dem Schlüsselwort new aufrufen, wird diese Funktion zu einem Konstruktor, und das Schlüsselwort this zeigt nicht auf das aktuelle Objekt, sondern auf ein neu erstelltes "leeres". So können Sie ein Objekt beliebig konfigurieren. Auf diese Weise können JavaScript-Konstruktoren eine der Rollen von Klassen in traditionellen, klassenbasierten OO übernehmen: Sie dienen als Vorlage oder als Entwurf für neue Objekte.

Nun ist JavaScript eine sehr mächtige Sprache, daher ist es ziemlich einfach, ein klassenbasiertes OO - System innerhalb von JavaScript zu implementieren, wenn Sie möchten. Sie sollten dies jedoch nur tun, wenn Sie wirklich einen Bedarf dafür haben, und nicht nur weil Java dies so tut.

212
Jörg W Mittag

ES2015 Klassen

In der ES2015-Spezifikation können Sie die Klassensyntax verwenden, die nur Zucker über dem Prototypsystem ist.

class Person {
  constructor(name) {
    this.name = name;
  }
  toString() {
    return `My name is ${ this.name }.`;
  }
}

class Employee extends Person {
  constructor(name, hours) {
    super(name);
    this.hours = hours;
  }
  toString() {
    return `${ super.toString() } I work ${ this.hours } hours.`;
  }
}

Leistungen

Der Hauptvorteil besteht darin, dass statische Analysewerkzeuge es einfacher finden, diese Syntax anzugreifen. Es ist auch für andere, die aus klassenbasierten Sprachen kommen, einfacher, die Sprache als Polyglot zu verwenden. 

Vorsichtsmaßnahmen

Seien Sie vorsichtig bei den derzeitigen Einschränkungen. Um private Eigenschaften zu erreichen, muss auf unter Verwendung von Symbols oder WeakMaps zurückgegriffen werden. In zukünftigen Versionen werden Klassen höchstwahrscheinlich um diese fehlenden Funktionen erweitert.

Unterstützung

Browser-Unterstützung ist im Moment nicht sehr gut (wird von fast allen außer IE unterstützt), aber Sie können diese Funktionen jetzt mit einem Transpiler wie Babel verwenden.

Ressourcen

78
Dale

Ich ziehe es vor, Daniel X zu verwenden. Moores{SUPER: SYSTEM}. Dies ist eine Disziplin, die Vorteile bietet, z. B. echte Instanzvariablen, auf Eigenschaften basierende Vererbung, Klassenhierarchien und Konfigurationsoptionen. Das folgende Beispiel veranschaulicht die Verwendung von echten Instanzvariablen, die meiner Meinung nach der größte Vorteil sind. Wenn Sie keine Instanzvariablen benötigen und nur mit öffentlichen oder privaten Variablen zufrieden sind, gibt es wahrscheinlich einfachere Systeme.

function Person(I) {
  I = I || {};

  Object.reverseMerge(I, {
    name: "McLovin",
    age: 25,
    homeState: "Hawaii"
  });

  return {
    introduce: function() {
      return "Hi I'm " + I.name + " and I'm " + I.age;
    }
  };
}

var fogel = Person({
  age: "old enough"
});
fogel.introduce(); // "Hi I'm McLovin and I'm old enough"

Wow, das ist an sich nicht wirklich sehr nützlich, aber schauen Sie sich das Hinzufügen einer Unterklasse an:

function Ninja(I) {
  I = I || {};

  Object.reverseMerge(I, {
    belt: "black"
  });

  // Ninja is a subclass of person
  return Object.extend(Person(I), {
    greetChallenger: function() {
      return "In all my " + I.age + " years as a ninja, I've never met a challenger as worthy as you...";
    }
  });
}

var resig = Ninja({name: "John Resig"});

resig.introduce(); // "Hi I'm John Resig and I'm 25"

Ein weiterer Vorteil ist die Fähigkeit, Vererbung auf Basis von Modulen und Merkmalen zu haben.

// The Bindable module
function Bindable() {

  var eventCallbacks = {};

  return {
    bind: function(event, callback) {
      eventCallbacks[event] = eventCallbacks[event] || [];

      eventCallbacks[event].Push(callback);
    },

    trigger: function(event) {
      var callbacks = eventCallbacks[event];

      if(callbacks && callbacks.length) {
        var self = this;
        callbacks.forEach(function(callback) {
          callback(self);
        });
      }
    },
  };
}

Ein Beispiel dafür, dass die Personenklasse das bindbare Modul enthält.

function Person(I) {
  I = I || {};

  Object.reverseMerge(I, {
    name: "McLovin",
    age: 25,
    homeState: "Hawaii"
  });

  var self = {
    introduce: function() {
      return "Hi I'm " + I.name + " and I'm " + I.age;
    }
  };

  // Including the Bindable module
  Object.extend(self, Bindable());

  return self;
}

var person = Person();
person.bind("eat", function() {
  alert(person.introduce() + " and I'm eating!");
});

person.trigger("eat"); // Blasts the alert!

Offenlegung: Ich bin Daniel X. Moore und dies ist mein{SUPER: SYSTEM}. Es ist der beste Weg, eine Klasse in JavaScript zu definieren.

56
Daniel X Moore
var Animal = function(options) {
    var name = options.name;
    var animal = {};

    animal.getName = function() {
        return name;
    };

    var somePrivateMethod = function() {

    };

    return animal;
};

// usage
var cat = Animal({name: 'tiger'});
41
liammclennan

Im Folgenden werden die Möglichkeiten beschrieben, Objekte in Javascript zu erstellen, die ich bisher verwendet habe

Beispiel 1:

obj = new Object();
obj.name = 'test';
obj.sayHello = function() {
    console.log('Hello '+ this.name);
}

Beispiel 2

obj = {};
obj.name = 'test';
obj.sayHello = function() {
    console.log('Hello '+ this.name);
}
obj.sayHello();

Beispiel 3:

var obj = function(nameParam) {
    this.name = nameParam;
}
obj.prototype.sayHello = function() {
    console.log('Hello '+ this.name);
}

Beispiel 4: Tatsächlicher Nutzen von Object.create (). Bitte beziehen Sie sich auf [diesen Link]

var Obj = {
    init: function(nameParam) {
        this.name = nameParam;
    },
    sayHello: function() {
        console.log('Hello '+ this.name);
    }
};
var usrObj = Object.create(Obj);  // <== one level of inheritance

usrObj.init('Bob');
usrObj.sayHello();

Beispiel 5 (Angepasstes Crockford Object.create):

Object.build = function(o) {
   var initArgs = Array.prototype.slice.call(arguments,1)
   function F() {
      if((typeof o.init === 'function') && initArgs.length) {
         o.init.apply(this,initArgs)
      }
   }
   F.prototype = o
   return new F()
}
MY_GLOBAL = {i: 1, nextId: function(){return this.i++}}  // For example

var userB = {
    init: function(nameParam) {
        this.id = MY_GLOBAL.nextId();
        this.name = nameParam;
    },
    sayHello: function() {
        console.log('Hello '+ this.name);
    }
};
var bob = Object.build(userB, 'Bob');  // Different from your code
bob.sayHello();


Um die Antwort mit ES6/ES2015 auf dem neuesten Stand zu halten

Eine Klasse ist wie folgt definiert:

class Person {
    constructor(strName, numAge) {
        this.name = strName;
        this.age = numAge;
    }

    toString() {
        return '((Class::Person) named ' + this.name + ' & of age ' + this.age + ')';
    }
}

let objPerson = new Person("Bob",33);
console.log(objPerson.toString());
29
Amol M Kulkarni

Ich denke, Sie sollten Douglas Crockfords Prototypal Inheritance in JavaScript und Classical Inheritance in JavaScript lesen.

Beispiele von seiner Seite:

Function.prototype.method = function (name, func) {
    this.prototype[name] = func;
    return this;
};

Bewirken? Es erlaubt Ihnen, Methoden auf elegante Weise hinzuzufügen:

function Parenizor(value) {
    this.setValue(value);
}

Parenizor.method('setValue', function (value) {
    this.value = value;
    return this;
});

Ich empfehle auch seine Videos: Advanced JavaScript .

Weitere Videos finden Sie auf seiner Seite: http://javascript.crockford.com/ In John Reisigs Buch finden Sie viele Beispiele von Douglas Crockfors Website.

24
Jarek

Weil ich den Fabrikplan von YUI/Crockford nicht zugeben werde und ich die Dinge gerne in sich geschlossen und erweiterbar halte, ist dies meine Variation:

function Person(params)
{
  this.name = params.name || defaultnamevalue;
  this.role = params.role || defaultrolevalue;

  if(typeof(this.speak)=='undefined') //guarantees one time prototyping
  {
    Person.prototype.speak = function() {/* do whatever */};
  }
}

var Robert = new Person({name:'Bob'});

wo idealerweise die Art des Tests so ähnlich ist wie bei der ersten prototypisierten Methode

16
annakata

Wenn Sie sich für einfach entscheiden, können Sie das "neue" Schlüsselwort vollständig vermeiden und einfach Factory-Methoden verwenden. Ich bevorzuge dies manchmal, weil ich JSON gerne zum Erstellen von Objekten benutze.

function getSomeObj(var1, var2){
  var obj = {
     instancevar1: var1,
     instancevar2: var2,
     someMethod: function(param)
     {  
          //stuff; 
     }
  };
  return obj;
}

var myobj = getSomeObj("var1", "var2");
myobj.someMethod("bla");

Ich bin mir jedoch nicht sicher, was der Performance-Hit für große Objekte ist.

15
Sam
var Student = (function () {
    function Student(firstname, lastname) {
        this.firstname = firstname;
        this.lastname = lastname;
        this.fullname = firstname + " " + lastname;
    }

    Student.prototype.sayMyName = function () {
        return this.fullname;
    };

    return Student;
}());

var user = new Student("Jane", "User");
var user_fullname = user.sayMyName();

So kompiliert TypeScript die Klasse mit dem Konstruktor zu JavaScript.

12
Mick

Der einfache Weg ist:

function Foo(a) {
  var that=this;

  function privateMethod() { .. }

  // public methods
  that.add = function(b) {
    return a + b;
  };
  that.avg = function(b) {
    return that.add(b) / 2; // calling another public method
  };
}

var x = new Foo(10);
alert(x.add(2)); // 12
alert(x.avg(20)); // 15

Der Grund für that ist, dass this an etwas anderes gebunden werden kann, wenn Sie eine Methode als Ereignishandler angeben. Sie speichern den Wert während der Instantiierung und verwenden ihn später.

Edit: Es ist definitiv nicht der beste Weg, nur ein einfacher Weg. Ich warte auch auf gute Antworten!

10
orip

Sie möchten wahrscheinlich einen Typ mithilfe des Faltmusters erstellen:

    // Here is the constructor section.
    var myType = function () {
        var N = {}, // Enclosed (private) members are here.
            X = this; // Exposed (public) members are here.

        (function ENCLOSED_FIELDS() {
            N.toggle = false;
            N.text = '';
        }());

        (function EXPOSED_FIELDS() {
            X.count = 0;
            X.numbers = [1, 2, 3];
        }());

        // The properties below have access to the enclosed fields.
        // Careful with functions exposed within the closure of the
        // constructor, each new instance will have it's own copy.
        (function EXPOSED_PROPERTIES_WITHIN_CONSTRUCTOR() {
            Object.defineProperty(X, 'toggle', {
                get: function () {
                    var before = N.toggle;
                    N.toggle = !N.toggle;
                    return before;
                }
            });

            Object.defineProperty(X, 'text', {
                get: function () {
                    return N.text;
                },
                set: function (value) {
                    N.text = value;
                }
            });
        }());
    };

    // Here is the prototype section.
    (function PROTOTYPE() {
        var P = myType.prototype;

        (function EXPOSED_PROPERTIES_WITHIN_PROTOTYPE() {
            Object.defineProperty(P, 'numberLength', {
                get: function () {
                    return this.numbers.length;
                }
            });
        }());

        (function EXPOSED_METHODS() {
            P.incrementNumbersByCount = function () {
                var i;
                for (i = 0; i < this.numbers.length; i++) {
                    this.numbers[i] += this.count;
                }
            };
            P.Tweak = function () {
                if (this.toggle) {
                    this.count++;
                }
                this.text = 'tweaked';
            };
        }());
    }());

Dieser Code gibt Ihnen einen Typ namens myType. Es wird interne private Felder haben, die toggle und text heißen. Es wird auch diese exponierten Mitglieder haben: die Felder count und numbers; die Eigenschaften toggle, text und numberLength; die Methoden incrementNumbersByCount und Tweak.

Das Faltmuster ist hier ausführlich beschrieben: Javascript Faltmuster

9
intrepidis

Code golf für @ liammclennans Antwort .

var Animal = function (args) {
  return {
    name: args.name,

    getName: function () {
      return this.name; // member access
    },

    callGetName: function () {
      return this.getName(); // method call
    }
  };
};

var cat = Animal({ name: 'tiger' });
console.log(cat.callGetName());

3
tponthieux

MooTools (Meine objektorientierten Werkzeuge) konzentriert sich auf die Idee von classes Sie können sogar mit Vererbung erweitern und implementieren. 

Wenn es gemastert wird, ist es ein lächerlich wiederverwendbares, leistungsstarkes Javascript.

2
Ryan Florence

Objektbasierte Klassen mit Vererbung

var baseObject = 
{
     // Replication / Constructor function
     new : function(){
         return Object.create(this);   
     },

    aProperty : null,
    aMethod : function(param){
      alert("Heres your " + param + "!");
    },
}


newObject = baseObject.new();
newObject.aProperty = "Hello";

anotherObject = Object.create(baseObject); 
anotherObject.aProperty = "There";

console.log(newObject.aProperty) // "Hello"
console.log(anotherObject.aProperty) // "There"
console.log(baseObject.aProperty) // null

Einfach, süß und fertig.

2
Ulad Kasach

Eine Basis

function Base(kind) {
    this.kind = kind;
}

Eine Klasse

// Shared var
var _greeting;

(function _init() {
    Class.prototype = new Base();
    Class.prototype.constructor = Class;
    Class.prototype.log = function() { _log.apply(this, arguments); }
    _greeting = "Good afternoon!";
})();

function Class(name, kind) {
    Base.call(this, kind);
    this.name = name;
}

// Shared function
function _log() {
    console.log(_greeting + " Me name is " + this.name + " and I'm a " + this.kind);
}

Aktion

var c = new Class("Joe", "Object");
c.log(); // "Good afternoon! Me name is Joe and I'm a Object"

Am Beispiel von Triptychon könnte dies noch einfacher sein:

    // Define a class and instantiate it
    var ThePerson = new function Person(name, gender) {
        // Add class data members
        this.name = name;
        this.gender = gender;
        // Add class methods
        this.hello = function () { alert('Hello, this is ' + this.name); }
    }("Bob", "M"); // this instantiates the 'new' object

    // Use the object
    ThePerson.hello(); // alerts "Hello, this is Bob"

Dadurch wird nur eine einzelne Objektinstanz erstellt. Dies ist jedoch immer noch hilfreich, wenn Sie mehrere Namen für Variablen und Methoden in einer Klasse einkapseln möchten. Normalerweise würde der Konstruktor keine "Bob, M" -Argumente enthalten, z. B. wenn die Methoden Aufrufe an ein System mit eigenen Daten wären, beispielsweise eine Datenbank oder ein Netzwerk.

Ich bin bei JS noch zu neu, um zu sehen, warum dies nicht die Sache prototype verwendet.

1
Roland

//new way using this and new
function Persons(name) {
  this.name = name;
  this.greeting = function() {
    alert('Hi! I\'m ' + this.name + '.');
  };
}

var gee=new Persons("gee");
gee.greeting();

var gray=new Persons("gray");
gray.greeting();

//old way
function createPerson(name){
 var obj={};
 obj.name=name;
 obj.greeting = function(){
 console.log("hello I am"+obj.name);
 }; 
  return obj;
}

var gita=createPerson('Gita');
gita.greeting();

0
Avinash Maurya

JavaScript ist objektorientiert , aber es unterscheidet sich grundlegend von anderen OOP Sprachen wie Java, C # oder C++. Versuchen Sie nicht, es so zu verstehen. Werfen Sie das alte Wissen raus und beginnen Sie von vorne. JavaScript braucht ein anderes Denken.

Ich würde vorschlagen, ein gutes Handbuch oder etwas zu diesem Thema zu bekommen. Ich selbst habe ExtJS Tutorials das Beste für mich gefunden, obwohl ich das Framework vorher oder nach dem Lesen nicht verwendet habe. Aber es gibt eine gute Erklärung dafür, was in der JavaScript-Welt ist. Sorry, es scheint, dass der Inhalt entfernt wurde. Hier ist ein Link zu archive.org copy stattdessen. Funktioniert heute. : P

0
Vilx-