From 149b599368b4e27cf7d05f270a43519f599372fd Mon Sep 17 00:00:00 2001 From: MDN Date: Tue, 18 Jan 2022 00:57:02 +0000 Subject: [CRON] sync translated content --- files/de/_redirects.txt | 1 + files/de/_wikihistory.json | 14 +- .../objects/classes_in_javascript/index.html | 291 ++++++++++++++ .../objects/object-oriented_js/index.html | 290 -------------- files/es/_redirects.txt | 2 + files/es/_wikihistory.json | 40 +- .../objects/classes_in_javascript/index.html | 308 +++++++++++++++ .../objects/classes_in_javascript/index.html | 401 +++++++++++++++++++ .../javascript/objects/inheritance/index.html | 400 ------------------- .../objects/object-oriented_js/index.html | 307 --------------- files/fr/_redirects.txt | 6 +- files/fr/_wikihistory.json | 34 +- .../objects/classes_in_javascript/index.md | 309 +++++++++++++++ .../objects/classes_in_javascript/index.md | 258 ++++++++++++ .../learn/javascript/objects/inheritance/index.md | 258 ------------ .../javascript/objects/object-oriented_js/index.md | 309 --------------- files/ja/_redirects.txt | 4 +- files/ja/_wikihistory.json | 28 +- .../objects/classes_in_javascript/index.html | 292 ++++++++++++++ .../objects/classes_in_javascript/index.html | 413 +++++++++++++++++++ .../javascript/objects/inheritance/index.html | 412 ------------------- .../objects/object-oriented_js/index.html | 291 -------------- files/ko/_redirects.txt | 2 + files/ko/_wikihistory.json | 20 +- .../objects/classes_in_javascript/index.html | 288 ++++++++++++++ .../objects/classes_in_javascript/index.html | 395 +++++++++++++++++++ .../javascript/objects/inheritance/index.html | 394 ------------------- .../objects/object-oriented_js/index.html | 287 -------------- files/pt-br/_redirects.txt | 6 +- files/pt-br/_wikihistory.json | 18 +- .../objects/classes_in_javascript/index.html | 276 +++++++++++++ .../objects/classes_in_javascript/index.html | 403 +++++++++++++++++++ .../javascript/objects/inheritance/index.html | 403 ------------------- .../objects/object-oriented_js/index.html | 276 ------------- files/ru/_redirects.txt | 6 +- files/ru/_wikihistory.json | 44 +-- .../objects/classes_in_javascript/index.html | 287 ++++++++++++++ .../objects/classes_in_javascript/index.html | 267 +++++++++++++ .../javascript/objects/inheritance/index.html | 267 ------------- .../objects/object-oriented_js/index.html | 287 -------------- files/zh-cn/_redirects.txt | 2 + files/zh-cn/_wikihistory.json | 52 +-- .../objects/classes_in_javascript/index.html | 268 +++++++++++++ .../objects/classes_in_javascript/index.html | 435 +++++++++++++++++++++ .../javascript/objects/inheritance/index.html | 434 -------------------- .../objects/object-oriented_js/index.html | 267 ------------- files/zh-tw/_redirects.txt | 2 + files/zh-tw/_wikihistory.json | 24 +- .../objects/classes_in_javascript/index.html | 278 +++++++++++++ .../objects/classes_in_javascript/index.html | 211 ++++++++++ .../javascript/objects/inheritance/index.html | 210 ---------- .../objects/object-oriented_js/index.html | 277 ------------- 52 files changed, 5541 insertions(+), 5513 deletions(-) create mode 100644 files/de/learn/javascript/objects/classes_in_javascript/index.html delete mode 100644 files/de/learn/javascript/objects/object-oriented_js/index.html create mode 100644 files/es/conflicting/learn/javascript/objects/classes_in_javascript/index.html create mode 100644 files/es/learn/javascript/objects/classes_in_javascript/index.html delete mode 100644 files/es/learn/javascript/objects/inheritance/index.html delete mode 100644 files/es/learn/javascript/objects/object-oriented_js/index.html create mode 100644 files/fr/conflicting/learn/javascript/objects/classes_in_javascript/index.md create mode 100644 files/fr/learn/javascript/objects/classes_in_javascript/index.md delete mode 100644 files/fr/learn/javascript/objects/inheritance/index.md delete mode 100644 files/fr/learn/javascript/objects/object-oriented_js/index.md create mode 100644 files/ja/conflicting/learn/javascript/objects/classes_in_javascript/index.html create mode 100644 files/ja/learn/javascript/objects/classes_in_javascript/index.html delete mode 100644 files/ja/learn/javascript/objects/inheritance/index.html delete mode 100644 files/ja/learn/javascript/objects/object-oriented_js/index.html create mode 100644 files/ko/conflicting/learn/javascript/objects/classes_in_javascript/index.html create mode 100644 files/ko/learn/javascript/objects/classes_in_javascript/index.html delete mode 100644 files/ko/learn/javascript/objects/inheritance/index.html delete mode 100644 files/ko/learn/javascript/objects/object-oriented_js/index.html create mode 100644 files/pt-br/conflicting/learn/javascript/objects/classes_in_javascript/index.html create mode 100644 files/pt-br/learn/javascript/objects/classes_in_javascript/index.html delete mode 100644 files/pt-br/learn/javascript/objects/inheritance/index.html delete mode 100644 files/pt-br/learn/javascript/objects/object-oriented_js/index.html create mode 100644 files/ru/conflicting/learn/javascript/objects/classes_in_javascript/index.html create mode 100644 files/ru/learn/javascript/objects/classes_in_javascript/index.html delete mode 100644 files/ru/learn/javascript/objects/inheritance/index.html delete mode 100644 files/ru/learn/javascript/objects/object-oriented_js/index.html create mode 100644 files/zh-cn/conflicting/learn/javascript/objects/classes_in_javascript/index.html create mode 100644 files/zh-cn/learn/javascript/objects/classes_in_javascript/index.html delete mode 100644 files/zh-cn/learn/javascript/objects/inheritance/index.html delete mode 100644 files/zh-cn/learn/javascript/objects/object-oriented_js/index.html create mode 100644 files/zh-tw/conflicting/learn/javascript/objects/classes_in_javascript/index.html create mode 100644 files/zh-tw/learn/javascript/objects/classes_in_javascript/index.html delete mode 100644 files/zh-tw/learn/javascript/objects/inheritance/index.html delete mode 100644 files/zh-tw/learn/javascript/objects/object-oriented_js/index.html (limited to 'files') diff --git a/files/de/_redirects.txt b/files/de/_redirects.txt index c7f0e18eae..1909a749bb 100644 --- a/files/de/_redirects.txt +++ b/files/de/_redirects.txt @@ -377,6 +377,7 @@ /de/docs/Learn/JavaScript/First_steps/Erster_Blick /de/docs/Learn/JavaScript/First_steps/A_first_splash /de/docs/Learn/JavaScript/First_steps/Was_ist_JavaScript /de/docs/Learn/JavaScript/First_steps/What_is_JavaScript /de/docs/Learn/JavaScript/First_steps/lustige_geschichten_generator /de/docs/Learn/JavaScript/First_steps/Silly_story_generator +/de/docs/Learn/JavaScript/Objects/Object-oriented_JS /de/docs/Learn/JavaScript/Objects/Classes_in_JavaScript /de/docs/Learn/Server-side/Erste_Schritte /de/docs/Learn/Server-side/First_steps /de/docs/Lokalisierung /de/docs/Glossary/Localization /de/docs/MDN/Contribute/Structures /de/docs/MDN/Structures diff --git a/files/de/_wikihistory.json b/files/de/_wikihistory.json index 815ea683ef..85a1b47a83 100644 --- a/files/de/_wikihistory.json +++ b/files/de/_wikihistory.json @@ -1454,6 +1454,13 @@ "22tcp" ] }, + "Learn/JavaScript/Objects/Classes_in_JavaScript": { + "modified": "2020-07-16T22:32:04.669Z", + "contributors": [ + "Timbuktu1982", + "bluefor2" + ] + }, "Learn/JavaScript/Objects/JSON": { "modified": "2020-07-16T22:32:24.377Z", "contributors": [ @@ -1461,13 +1468,6 @@ "DirkMassmann" ] }, - "Learn/JavaScript/Objects/Object-oriented_JS": { - "modified": "2020-07-16T22:32:04.669Z", - "contributors": [ - "Timbuktu1982", - "bluefor2" - ] - }, "Learn/JavaScript/Objects/Object_prototypes": { "modified": "2020-07-16T22:32:18.828Z", "contributors": [ diff --git a/files/de/learn/javascript/objects/classes_in_javascript/index.html b/files/de/learn/javascript/objects/classes_in_javascript/index.html new file mode 100644 index 0000000000..9a320da8d9 --- /dev/null +++ b/files/de/learn/javascript/objects/classes_in_javascript/index.html @@ -0,0 +1,291 @@ +--- +title: Objektorientiertes JavaScript für Beginner +slug: Learn/JavaScript/Objects/Classes_in_JavaScript +tags: + - Anfänger + - Artikel + - Erstellen + - Erzeugen + - Instanzen + - JavaScript + - Konstruktor + - Lernen + - OOJS + - OOP + - Objekt + - Objektorientiert + - codescripting +translation_of: Learn/JavaScript/Objects/Object-oriented_JS +original_slug: Learn/JavaScript/Objects/Object-oriented_JS +--- +
{{LearnSidebar}}
+ +
{{PreviousMenuNext("Learn/JavaScript/Objects/Basics", "Learn/JavaScript/Objects/Object_prototypes", "Learn/JavaScript/Objects")}}
+ +

Mit den nun bereits erlangten Grundlagen werden wir uns jetzt auf objektorientiertes JavaScript (OOJS) konzentrieren - dieser Artikel vermittelt Grundlagen der Theorie der objektorientierten Programmierung (OOP). Anschließend wird näher betrachtet, wie JavaScript Objektklassen über Konstruktor-Funktionen emuliert und wie Objekt-Instanzen erzeugt werden.

+ + + + + + + + + + + + +
Voraussetzungen: +

Grundlegende Computerkenntnisse, ein grundlegendes Verständnis von HTML und CSS, Vertrautheit mit den Grundlagen von JavaScript (siehe erste Schritte und Bausteine) und OOJS-Grundlagen (siehe Einführung in Objekte).

+
Ziel:Die grundlegende Theorie hinter der objektorientierten Programmierung und wie diese in JavaScript umgesetzt ist ("alles ist ein Objekt") zu verstehen, und wie man Konstruktoren und Objektinstanzen erstellt.
+ +

Objektorientierte Programmierung - Grundlagen

+ +

Um zu beginnen möchten wir Ihnen eine vereinfachende und umfangreiche Übersicht darüber geben, was objektorientierte Programmierung (OOP) ist. Wir sagen vereinfachend, weil OOP schnell sehr kompliziert werden kann und an dieser Stelle eine vollständige Einführung sehr wahrscheinlich mehr verwirren als helfen würde. Die Grundidee von OOP ist, dass wir Objekte verwenden, um Dinge aus der realen Welt zu modellieren, die wir in unseren Programmen abbilden wollen und/oder eine einfache Möglichkeit bieten möchten, auf Funktionen zuzugreifen, die sonst nur schwer oder gar nicht genutzt werden könnten.

+ +

Objekte können in Beziehung stehende Daten und Code enthalten, die Informationen über die Sache darstellen, die Sie modellieren möchten, sowie Funktionalitäten bzw. Verhalten, die Sie erhalten bzw. bereitstellen möchten. Objektdaten (und oft auch Funktionen) können geordnet (das Fachwort dafür lautet "gekapselt") innerhalb eines Objektpakets gespeichert werden (dem ein bestimmter Name gegeben werden kann, auf den man sich beziehen kann, der manchmal auch "Namensraum" genannt wird), wodurch es leicht strukturiert und zugänglich wird. Objekte werden auch häufig als Datenspeicher verwendet, die einfach über das Netzwerk gesendet werden können.

+ +

Definieren einer Objektvorlage

+ +

Betrachten wir ein einfaches Programm, das Informationen über die Schüler und Lehrer einer Schule anzeigt. Hier betrachten wir die OOP-Theorie im Allgemeinen, nicht im Zusammenhang mit einer bestimmten Programmiersprache.

+ +

Um damit zu beginnen, könnten wir zu unserem person-Objekt aus dem vorhergehenden Kapitel zurückkehren, in dem wir Informationen und Funktionalitäten einer Person definiert hatten. Es gibt viele Dinge, die man über eine Person wissen kann (ihre Adresse, Größe, Schuhgröße, DNA-Profil, Ausweisnummer, signifikante Persönlichkeitsmerkmale ...), aber in diesem Fall sind wir nur daran interessiert, ihren Namen, ihr Alter, ihr Geschlecht und ihre Interessen zu betrachten. Und wir wollen auch in der Lage sein, eine kurze Erläuterung über sie auf der Grundlage dieser Daten zu schreiben und sie dazu zu bringen, "Hallo" zu sagen. Dies wird als "Abstraktion" bezeichnet - ein einfaches Modell einer komplexeren Sache wird erstellt, das die wichtigsten Aspekte in einer Weise darstellt, die für die Zwecke unseres Programms leicht zu bearbeiten sind.

+ +

+ +

Erstellen von realen Objekten

+ +

Von unserer Klasse können wir Objektinstanzen erstellen - Objekte die Informationen und Funktionalitäten enthalten, die in der Klasse definiert worden. Von unserer Klasse person können wir nun einige tatsächliche Personen erzeugen:

+ +

+ +

Wenn eine Objektinstanz aus einer Klasse erzeugt wurde, wird die Konstruktorfunktion der Klasse ausgeführt, um die Objektinstanz zu erzeugen. Dieser Vorgang der Erzeugung einer Objektinstanz aus einer Klasse wird als Instanziierung bezeichnet - die Objektinstanz wird von der Klasse aus instanziiert.

+ +

Spezialisierte Klassen

+ +

In diesem Fall wollen wir keine allgemeinen Leute - wir wollen Lehrer und Schüler, die beide spezifischere Typen von Menschen sind. In OOP können wir neue Klassen erstellen, die auf anderen Klassen basieren - diese neuen Unterklassen können die Daten- und Funktionalitäten ihrer Elternklasse erben, so dass Sie die Funktionalitäten, die allen Objekttypen gemeinsam ist, wiederverwenden können, anstatt sie duplizieren zu müssen.  Da wo sich Funktionalitäten zwischen den Klassen unterscheiden soll, können bei Bedarf spezialisierte Features direkt in den betroffenen Klassen entsprechend definieren.

+ +

+ +

Das ist wirklich sehr nützlich - Lehrer und Schüler haben viele gemeinsame Merkmale wie Name, Geschlecht und Alter, so dass es praktisch ist, diese Merkmale nur einmal zu definieren. Sie können dasselbe Merkmal auch separat in verschiedenen Klassen definieren, da jede Definition dieses Merkmals in einem anderen Namensraum liegt. Die Begrüßung eines Schülers könnte z.B. die Form "Yo, ich bin firstName" haben (z.B. Yo, ich bin Sam), während ein Lehrer etwas formelleres verwenden könnte, wie z.B. "Hallo, mein Name ist prefix lastName und ich unterrichte Subject". (z.B. Hallo, ich heiße Mr. Griffiths und unterrichte Chemie).

+ +
+

Hinweis: Das Fachwort für die Fähigkeit, mehrere Objekttypen mit der gleichen Funktionalität zu implementieren, nennt man Polymorphismus. Nur für den Fall, dass Sie sich das fragen.

+
+ +

Sie können nun Objektinstanzen aus Ihren Unterklassen erzeugen. Beispiel:

+ +

+ +

Im weiteren Verlauf dieses Kapitels werden wir uns damit beschäftigen, wie die OOP-Theorie in JavaScript in die Praxis umgesetzt werden kann.

+ +

Konstruktoren und Objektinstanzen

+ +

JavaScript verwendet spezielle Funktionen, die "Konstruktor-Funktionen" genannt werden, um Objekte und deren Eigenschaften zu definieren und zu initialisieren. Sie sind nützlich, weil Sie oft auf Situationen stoßen werden, in denen Sie nicht wissen, wie viele Objekte Sie erstellen werden müssen. Konstruktoren bieten die Möglichkeit, so viele Objekte wie nötig auf einfache und effektive Weise zu erstellen, indem sie alle erforderlichen Daten und Funktionen an diese Objekte anhängen.

+ +

Lassen Sie uns nun das Erstellen von Klassen über Konstruktoren und das Erstellen von Objektinstanzen aus ihnen heraus speziell in JavaScript untersuchen. Zuerst möchten wir Sie bitten, eine neue lokale Kopie der oojs.html-Datei zu erstellen, die wir im vorhergehenden Kapitel bereits benutzt haben.

+ +

Ein einfaches Beispiel

+ +
    +
  1. Fangen wir damit an, wie man eine Person mit einer normalen Funktion definieren könnte. Fügen Sie diese Funktion innerhalb des Skript-Elements der oojs.html hinzu: +
    function createNewPerson(name) {
    +  var obj = {};
    +  obj.name = name;
    +  obj.greeting = function() {
    +    alert('Hi! I\'m ' + obj.name + '.');
    +  };
    +  return obj;
    +}
    +
  2. +
  3. Sie können nun eine neue Person erstellen, indem Sie diese Funktion aufrufen - bitte geben Sie die folgenden Zeilen in der JavaScript-Konsole Ihres Browsers ein: +
    var salva = createNewPerson('Salva');
    +salva.name;
    +salva.greeting();
    + Das funktioniert zwar ganz gut, aber es ist ein bisschen umständlich. Wenn wir wissen, dass wir ein Objekt erstellen wollen, warum müssen wir dann explizit ein neues leeres Objekt erstellen und es zurückgeben? Glücklicherweise bietet uns JavaScript eine praktische Vereinfachung in Form von Konstruktorfunktionen - nutzen wir jetzt eine!
  4. +
  5. Ersetzen Sie die vorher implementierte Funktion durch folgende: +
    function Person(name) {
    +  this.name = name;
    +  this.greeting = function() {
    +    alert('Hi! I\'m ' + this.name + '.');
    +  };
    +}
    +
  6. +
+ +

Die Konstruktorfunktion ist die JavaScript-Version einer Klasse. Sie werden feststellen, dass sie alle Eigenschaften hat, die man in einer Funktion erwartet, obwohl sie weder etwas zurückgibt oder explizit ein Objekt erzeugt - sie definiert im Grunde nur Eigenschaften und Methoden. Sie werden sehen, dass dieses Schlüsselwort auch hier verwendet wird - es besagt im Grunde, dass immer dann, wenn eine dieser Objektinstanzen erzeugt wird, die Eigenschaft name des Objekts gleich dem Namenswert ist, der an den Konstruktoraufruf übergeben wird, und die Methode greeting() wird ebenfalls den Namenswert verwenden, der an den Konstruktoraufruf übergeben wird.

+ +
+

Hinweis: Der Name einer Konstruktorfunktion beginnt normalerweise mit einem Großbuchstaben - diese Konvention wird verwendet, um Konstruktorfunktionen im Code leichter erkennbar zu machen.

+
+ +

Wie rufen wir also einen Konstruktor auf, um einige Objekte zu erstellen?

+ +
    +
  1. Fügen Sie die folgenden Zeilen unterhalb Ihres vorherigen Codezusatzes ein: +
    var person1 = new Person('Bob');
    +var person2 = new Person('Sarah');
    +
  2. +
  3. Speichern Sie Ihren Code, laden Sie ihn im Browser neu und geben Sie die folgenden Zeilen in Ihre JS-Konsole ein: +
    person1.name
    +person1.greeting()
    +person2.name
    +person2.greeting()
    +
  4. +
+ +

Cool! Sie werden nun sehen, dass wir zwei neue Objekte auf der Seite haben, die jeweils unter einem anderen Namespace gespeichert sind - wenn Sie auf ihre Eigenschaften und Methoden zugreifen, müssen Sie Aufrufe mit person1 oder person2 starten; die darin enthaltene Funktionalität ist sauber verpackt, damit sie nicht mit anderen Funktionen kollidiert. Sie haben jedoch die gleiche Namenseigenschaft und die gleiche Methode greeting() zur Verfügung. Beachten Sie, dass Sie Ihren eigenen Namenswert verwenden, der Ihnen bei der Erstellung zugewiesen wurde. Das ist ein Grund, warum es sehr wichtig ist, diesen zu verwenden, so dass Sie Ihre eigenen Werte verwenden werden, und nicht irgendeinen anderen Wert.

+ +

Sehen wir uns die Konstruktoraufrufe noch einmal genauer an:

+ +
var person1 = new Person('Bob');
+var person2 = new Person('Sarah');
+ +

In jedem Fall wird das neue Schlüsselwort verwendet, um dem Browser mitzuteilen, dass wir eine neue Objektinstanz erstellen wollen, gefolgt vom Funktionsnamen mit den erforderlichen Parametern in Klammern. Das Ergebnis wird in einer Variablen gespeichert - sehr ähnlich wie bei dem Aufruf einer Standardfunktion. Jede Instanz wird entsprechend dieser Definition erzeugt:

+ +
function Person(name) {
+  this.name = name;
+  this.greeting = function() {
+    alert('Hi! I\'m ' + this.name + '.');
+  };
+}
+ +

Nach dem Anlegen der neuen Objekte enthalten die Variablen person1 und person2 die folgenden Objekte:

+ +
{
+  name: 'Bob',
+  greeting: function() {
+    alert('Hi! I\'m ' + this.name + '.');
+  }
+}
+
+{
+  name: 'Sarah',
+  greeting: function() {
+    alert('Hi! I\'m ' + this.name + '.');
+  }
+}
+ +

Beachten Sie, dass wir beim Aufruf unserer Konstruktor-Funktion jedes Mal greeting() definieren, was nicht ideal ist. Um dies zu vermeiden, können wir stattdessen Funktionen auf dem Prototypen definieren, die wir uns später genauer ansehen werden.

+ +

Erstellen unseres finalen Konstruktors

+ +

Das Beispiel, das wir oben betrachtet haben, war nur ein einfaches Beispiel, um den Einstieg zu erleichtern. Lassen Sie uns nun weitermachen und unsere finale Konstruktor-Funktion Person() erstellen.

+ +
    +
  1. Entfernen Sie den bisher eingefügten Code und fügen Sie nachfolgenden Konstruktor als Ersatz hinzu - dies ist im Prinzip genau dasselbe, wie das einfache Beispiel, nur etwas komplexer: +
    function Person(first, last, age, gender, interests) {
    +  this.name = {
    +     first : first,
    +     last : last
    +  };
    +  this.age = age;
    +  this.gender = gender;
    +  this.interests = interests;
    +  this.bio = function() {
    +    alert(this.name.first + ' ' + this.name.last + ' is ' + this.age + ' years old. He likes ' + this.interests[0] + ' and ' + this.interests[1] + '.');
    +  };
    +  this.greeting = function() {
    +    alert('Hi! I\'m ' + this.name.first + '.');
    +  };
    +}
    +
  2. +
  3. Fügen Sie nun unter dem Code von oben folgende Zeile ein, um eine Objektinstanz zu erzeugen: +
    var person1 = new Person('Bob', 'Smith', 32, 'male', ['music', 'skiing']);
    +
  4. +
+ +

Sie werden nun sehen, dass Sie auf die Eigenschaften und Methoden zugreifen können, genau wie wir es zuvor getan haben - probieren Sie das in Ihrer JS-Konsole aus:

+ +
person1['age']
+person1.interests[1]
+person1.bio()
+// etc.
+ +
+

Hinweis: Wenn Sie Probleme haben, dies zum Laufen zu bringen, vergleichen Sie Ihren Code mit unserer Version - siehe oojs-class-finished.html (hier können Sie auch sehen, wie es live läuft).

+
+ +

Weitere Übungen

+ +

Versuchen Sie zunächst, ein paar weitere eigene Objekte hinzuzufügen, und versuchen Sie, die Eigenschaften bzw. Funktionen der daraus erzeugten Objektinstanzen zu nutzen bzw. zu verändern.

+ +

Außerdem gibt es einige Probleme mit unserer bio()-Methode - die Ausgabe enthält immer das Pronomen "He", egal ob Ihre Person weiblich ist oder einem anderen Geschlecht angehört. Und die bio()-Methode wird nur zwei Interessen enthalten, auch wenn mehr im Interessen-Array aufgelistet sind. Finden Sie heraus, wie man das in der Klassendefinition (Konstruktor) beheben kann? Sie können jeden beliebigen Code in einen Konstruktor einfügen (Sie werden wahrscheinlich ein paar Bedingungen und eine Schleife benötigen). Überlegen Sie sich, wie die Sätze je nach Geschlecht und je nachdem, ob die Anzahl der aufgelisteten Interessen 1, 2 oder mehr als 2 beträgt, unterschiedlich strukturiert werden sollten.

+ +
+

Hinweis: Wenn Sie nicht weiterkommen, haben wir eine Antwort bzw. Lösung in unserem GitHub-Repo bereitgestellt (Sehen Sie es sich hier an) - versuchen Sie bitte aber erst einmal, die Lösung selbst zu schreiben!

+
+ +

Andere Möglichkeiten, Objektinstanzen zu erzeugen

+ +

Bisher haben wir zwei verschiedene Wege gesehen, um eine Objektinstanz zu erzeugen - die Deklaration eines Objektes als Literal und die Verwendung einer Konstruktorfunktion (siehe oben).

+ +

Diese machen Sinn, aber es gibt auch andere Wege - wir möchten Sie mit diesen vertraut machen, falls Sie auf Ihren Reisen im Web auf sie stoßen sollten.

+ +

Der Object()-Konstruktor

+ +

Zuerst können Sie den Object() Konstruktor verwenden, um ein neues Objekt zu erstellen. Ja, sogar generische Objekte haben einen Konstruktor, der ein leeres Objekt erzeugt.

+ +
    +
  1. Geben Sie dies in die JavaScript-Konsole Ihres Browsers ein: +
    var person1 = new Object();
    +
  2. +
  3. Diese speichert ein leeres Objekt in der Variable person1. Sie können dann diesem Objekt Eigenschaften und Methoden mit Punkt- oder Klammer-Notation hinzufügen, wie gewünscht. Versuchen Sie diese Beispiele in Ihrer Konsole: +
    person1.name = 'Chris';
    +person1['age'] = 38;
    +person1.greeting = function() {
    +  alert('Hi! I\'m ' + this.name + '.');
    +};
    +
  4. +
  5. Sie können auch ein Objektliteral als Parameter an den Object() Konstruktor übergeben, um es mit Eigenschaften/Methoden vorzufüllen. Versuchen Sie folgendes in Ihrer JS-Konsole: +
    var person1 = new Object({
    +  name: 'Chris',
    +  age: 38,
    +  greeting: function() {
    +    alert('Hi! I\'m ' + this.name + '.');
    +  }
    +});
    +
  6. +
+ +

Verwendung der Methode create()

+ +

Konstruktoren können Ihnen helfen, Ihren Code zu ordnen - Sie können Konstruktoren an einer Stelle erstellen und dann bei Bedarf Instanzen davon erstellen - und es ist immer nachvollziehbar, woher sie kommen.

+ +

Einige Leute ziehen es jedoch vor, Objektinstanzen zu erstellen, ohne vorher Konstruktoren zu erstellen, insbesondere wenn sie nur wenige Instanzen eines Objekts erstellen müssen. JavaScript hat eine eingebaute Methode namens create(), die es Ihnen einfach ermöglicht, dies zu tun. Mit ihr können Sie ein neues Objekt auf Basis eines beliebigen vorhandenen Objekts erstellen.

+ +
    +
  1. Wenn Ihre fertige Übung aus den vorherigen Abschnitten im Browser geladen ist, versuchen Sie folgendes in Ihrer JavaScript-Konsole: +
    var person2 = Object.create(person1);
    +
  2. +
  3. Nun geben Sie bitte folgendes in die JavaScript-Konsole ein: +
    person2.name
    +person2.greeting()
    +
  4. +
+ +

Sie werden sehen, dass person2 auf der Basis von person1 erstellt wurde - es hat die gleichen Eigenschaften und die gleiche Methode, die ihm zur Verfügung stehen.

+ +

Eine Einschränkung von create() ist, dass der IE8 es nicht unterstützt. Daher können Konstruktoren effektiver sein, wenn Sie ältere Browser unterstützen wollen.

+ +

Wir werden die Auswirkungen von create() später noch genauer untersuchen.

+ +

Zusammenfassung

+ +

Dieser Artikel hat eine vereinfachte Sicht der objektorientierten Theorie geliefert - das ist noch lange nicht die ganze Geschichte, aber er gibt Ihnen eine Vorstellung davon, womit wir es hier zu tun haben. Darüber hinaus haben wir damit begonnen, verschiedene Möglichkeiten der Erzeugung von Objektinstanzen zu betrachten.

+ +

Im nächsten Artikel werden wir uns mit JavaScript-Objekt-Prototypen beschäftigen.

+ +

{{PreviousMenuNext("Learn/JavaScript/Objects/Basics", "Learn/JavaScript/Objects/Object_prototypes", "Learn/JavaScript/Objects")}}

+ +

In diesem Modul

+ + diff --git a/files/de/learn/javascript/objects/object-oriented_js/index.html b/files/de/learn/javascript/objects/object-oriented_js/index.html deleted file mode 100644 index b4229a8058..0000000000 --- a/files/de/learn/javascript/objects/object-oriented_js/index.html +++ /dev/null @@ -1,290 +0,0 @@ ---- -title: Objektorientiertes JavaScript für Beginner -slug: Learn/JavaScript/Objects/Object-oriented_JS -tags: - - Anfänger - - Artikel - - Erstellen - - Erzeugen - - Instanzen - - JavaScript - - Konstruktor - - Lernen - - OOJS - - OOP - - Objekt - - Objektorientiert - - codescripting -translation_of: Learn/JavaScript/Objects/Object-oriented_JS ---- -
{{LearnSidebar}}
- -
{{PreviousMenuNext("Learn/JavaScript/Objects/Basics", "Learn/JavaScript/Objects/Object_prototypes", "Learn/JavaScript/Objects")}}
- -

Mit den nun bereits erlangten Grundlagen werden wir uns jetzt auf objektorientiertes JavaScript (OOJS) konzentrieren - dieser Artikel vermittelt Grundlagen der Theorie der objektorientierten Programmierung (OOP). Anschließend wird näher betrachtet, wie JavaScript Objektklassen über Konstruktor-Funktionen emuliert und wie Objekt-Instanzen erzeugt werden.

- - - - - - - - - - - - -
Voraussetzungen: -

Grundlegende Computerkenntnisse, ein grundlegendes Verständnis von HTML und CSS, Vertrautheit mit den Grundlagen von JavaScript (siehe erste Schritte und Bausteine) und OOJS-Grundlagen (siehe Einführung in Objekte).

-
Ziel:Die grundlegende Theorie hinter der objektorientierten Programmierung und wie diese in JavaScript umgesetzt ist ("alles ist ein Objekt") zu verstehen, und wie man Konstruktoren und Objektinstanzen erstellt.
- -

Objektorientierte Programmierung - Grundlagen

- -

Um zu beginnen möchten wir Ihnen eine vereinfachende und umfangreiche Übersicht darüber geben, was objektorientierte Programmierung (OOP) ist. Wir sagen vereinfachend, weil OOP schnell sehr kompliziert werden kann und an dieser Stelle eine vollständige Einführung sehr wahrscheinlich mehr verwirren als helfen würde. Die Grundidee von OOP ist, dass wir Objekte verwenden, um Dinge aus der realen Welt zu modellieren, die wir in unseren Programmen abbilden wollen und/oder eine einfache Möglichkeit bieten möchten, auf Funktionen zuzugreifen, die sonst nur schwer oder gar nicht genutzt werden könnten.

- -

Objekte können in Beziehung stehende Daten und Code enthalten, die Informationen über die Sache darstellen, die Sie modellieren möchten, sowie Funktionalitäten bzw. Verhalten, die Sie erhalten bzw. bereitstellen möchten. Objektdaten (und oft auch Funktionen) können geordnet (das Fachwort dafür lautet "gekapselt") innerhalb eines Objektpakets gespeichert werden (dem ein bestimmter Name gegeben werden kann, auf den man sich beziehen kann, der manchmal auch "Namensraum" genannt wird), wodurch es leicht strukturiert und zugänglich wird. Objekte werden auch häufig als Datenspeicher verwendet, die einfach über das Netzwerk gesendet werden können.

- -

Definieren einer Objektvorlage

- -

Betrachten wir ein einfaches Programm, das Informationen über die Schüler und Lehrer einer Schule anzeigt. Hier betrachten wir die OOP-Theorie im Allgemeinen, nicht im Zusammenhang mit einer bestimmten Programmiersprache.

- -

Um damit zu beginnen, könnten wir zu unserem person-Objekt aus dem vorhergehenden Kapitel zurückkehren, in dem wir Informationen und Funktionalitäten einer Person definiert hatten. Es gibt viele Dinge, die man über eine Person wissen kann (ihre Adresse, Größe, Schuhgröße, DNA-Profil, Ausweisnummer, signifikante Persönlichkeitsmerkmale ...), aber in diesem Fall sind wir nur daran interessiert, ihren Namen, ihr Alter, ihr Geschlecht und ihre Interessen zu betrachten. Und wir wollen auch in der Lage sein, eine kurze Erläuterung über sie auf der Grundlage dieser Daten zu schreiben und sie dazu zu bringen, "Hallo" zu sagen. Dies wird als "Abstraktion" bezeichnet - ein einfaches Modell einer komplexeren Sache wird erstellt, das die wichtigsten Aspekte in einer Weise darstellt, die für die Zwecke unseres Programms leicht zu bearbeiten sind.

- -

- -

Erstellen von realen Objekten

- -

Von unserer Klasse können wir Objektinstanzen erstellen - Objekte die Informationen und Funktionalitäten enthalten, die in der Klasse definiert worden. Von unserer Klasse person können wir nun einige tatsächliche Personen erzeugen:

- -

- -

Wenn eine Objektinstanz aus einer Klasse erzeugt wurde, wird die Konstruktorfunktion der Klasse ausgeführt, um die Objektinstanz zu erzeugen. Dieser Vorgang der Erzeugung einer Objektinstanz aus einer Klasse wird als Instanziierung bezeichnet - die Objektinstanz wird von der Klasse aus instanziiert.

- -

Spezialisierte Klassen

- -

In diesem Fall wollen wir keine allgemeinen Leute - wir wollen Lehrer und Schüler, die beide spezifischere Typen von Menschen sind. In OOP können wir neue Klassen erstellen, die auf anderen Klassen basieren - diese neuen Unterklassen können die Daten- und Funktionalitäten ihrer Elternklasse erben, so dass Sie die Funktionalitäten, die allen Objekttypen gemeinsam ist, wiederverwenden können, anstatt sie duplizieren zu müssen.  Da wo sich Funktionalitäten zwischen den Klassen unterscheiden soll, können bei Bedarf spezialisierte Features direkt in den betroffenen Klassen entsprechend definieren.

- -

- -

Das ist wirklich sehr nützlich - Lehrer und Schüler haben viele gemeinsame Merkmale wie Name, Geschlecht und Alter, so dass es praktisch ist, diese Merkmale nur einmal zu definieren. Sie können dasselbe Merkmal auch separat in verschiedenen Klassen definieren, da jede Definition dieses Merkmals in einem anderen Namensraum liegt. Die Begrüßung eines Schülers könnte z.B. die Form "Yo, ich bin firstName" haben (z.B. Yo, ich bin Sam), während ein Lehrer etwas formelleres verwenden könnte, wie z.B. "Hallo, mein Name ist prefix lastName und ich unterrichte Subject". (z.B. Hallo, ich heiße Mr. Griffiths und unterrichte Chemie).

- -
-

Hinweis: Das Fachwort für die Fähigkeit, mehrere Objekttypen mit der gleichen Funktionalität zu implementieren, nennt man Polymorphismus. Nur für den Fall, dass Sie sich das fragen.

-
- -

Sie können nun Objektinstanzen aus Ihren Unterklassen erzeugen. Beispiel:

- -

- -

Im weiteren Verlauf dieses Kapitels werden wir uns damit beschäftigen, wie die OOP-Theorie in JavaScript in die Praxis umgesetzt werden kann.

- -

Konstruktoren und Objektinstanzen

- -

JavaScript verwendet spezielle Funktionen, die "Konstruktor-Funktionen" genannt werden, um Objekte und deren Eigenschaften zu definieren und zu initialisieren. Sie sind nützlich, weil Sie oft auf Situationen stoßen werden, in denen Sie nicht wissen, wie viele Objekte Sie erstellen werden müssen. Konstruktoren bieten die Möglichkeit, so viele Objekte wie nötig auf einfache und effektive Weise zu erstellen, indem sie alle erforderlichen Daten und Funktionen an diese Objekte anhängen.

- -

Lassen Sie uns nun das Erstellen von Klassen über Konstruktoren und das Erstellen von Objektinstanzen aus ihnen heraus speziell in JavaScript untersuchen. Zuerst möchten wir Sie bitten, eine neue lokale Kopie der oojs.html-Datei zu erstellen, die wir im vorhergehenden Kapitel bereits benutzt haben.

- -

Ein einfaches Beispiel

- -
    -
  1. Fangen wir damit an, wie man eine Person mit einer normalen Funktion definieren könnte. Fügen Sie diese Funktion innerhalb des Skript-Elements der oojs.html hinzu: -
    function createNewPerson(name) {
    -  var obj = {};
    -  obj.name = name;
    -  obj.greeting = function() {
    -    alert('Hi! I\'m ' + obj.name + '.');
    -  };
    -  return obj;
    -}
    -
  2. -
  3. Sie können nun eine neue Person erstellen, indem Sie diese Funktion aufrufen - bitte geben Sie die folgenden Zeilen in der JavaScript-Konsole Ihres Browsers ein: -
    var salva = createNewPerson('Salva');
    -salva.name;
    -salva.greeting();
    - Das funktioniert zwar ganz gut, aber es ist ein bisschen umständlich. Wenn wir wissen, dass wir ein Objekt erstellen wollen, warum müssen wir dann explizit ein neues leeres Objekt erstellen und es zurückgeben? Glücklicherweise bietet uns JavaScript eine praktische Vereinfachung in Form von Konstruktorfunktionen - nutzen wir jetzt eine!
  4. -
  5. Ersetzen Sie die vorher implementierte Funktion durch folgende: -
    function Person(name) {
    -  this.name = name;
    -  this.greeting = function() {
    -    alert('Hi! I\'m ' + this.name + '.');
    -  };
    -}
    -
  6. -
- -

Die Konstruktorfunktion ist die JavaScript-Version einer Klasse. Sie werden feststellen, dass sie alle Eigenschaften hat, die man in einer Funktion erwartet, obwohl sie weder etwas zurückgibt oder explizit ein Objekt erzeugt - sie definiert im Grunde nur Eigenschaften und Methoden. Sie werden sehen, dass dieses Schlüsselwort auch hier verwendet wird - es besagt im Grunde, dass immer dann, wenn eine dieser Objektinstanzen erzeugt wird, die Eigenschaft name des Objekts gleich dem Namenswert ist, der an den Konstruktoraufruf übergeben wird, und die Methode greeting() wird ebenfalls den Namenswert verwenden, der an den Konstruktoraufruf übergeben wird.

- -
-

Hinweis: Der Name einer Konstruktorfunktion beginnt normalerweise mit einem Großbuchstaben - diese Konvention wird verwendet, um Konstruktorfunktionen im Code leichter erkennbar zu machen.

-
- -

Wie rufen wir also einen Konstruktor auf, um einige Objekte zu erstellen?

- -
    -
  1. Fügen Sie die folgenden Zeilen unterhalb Ihres vorherigen Codezusatzes ein: -
    var person1 = new Person('Bob');
    -var person2 = new Person('Sarah');
    -
  2. -
  3. Speichern Sie Ihren Code, laden Sie ihn im Browser neu und geben Sie die folgenden Zeilen in Ihre JS-Konsole ein: -
    person1.name
    -person1.greeting()
    -person2.name
    -person2.greeting()
    -
  4. -
- -

Cool! Sie werden nun sehen, dass wir zwei neue Objekte auf der Seite haben, die jeweils unter einem anderen Namespace gespeichert sind - wenn Sie auf ihre Eigenschaften und Methoden zugreifen, müssen Sie Aufrufe mit person1 oder person2 starten; die darin enthaltene Funktionalität ist sauber verpackt, damit sie nicht mit anderen Funktionen kollidiert. Sie haben jedoch die gleiche Namenseigenschaft und die gleiche Methode greeting() zur Verfügung. Beachten Sie, dass Sie Ihren eigenen Namenswert verwenden, der Ihnen bei der Erstellung zugewiesen wurde. Das ist ein Grund, warum es sehr wichtig ist, diesen zu verwenden, so dass Sie Ihre eigenen Werte verwenden werden, und nicht irgendeinen anderen Wert.

- -

Sehen wir uns die Konstruktoraufrufe noch einmal genauer an:

- -
var person1 = new Person('Bob');
-var person2 = new Person('Sarah');
- -

In jedem Fall wird das neue Schlüsselwort verwendet, um dem Browser mitzuteilen, dass wir eine neue Objektinstanz erstellen wollen, gefolgt vom Funktionsnamen mit den erforderlichen Parametern in Klammern. Das Ergebnis wird in einer Variablen gespeichert - sehr ähnlich wie bei dem Aufruf einer Standardfunktion. Jede Instanz wird entsprechend dieser Definition erzeugt:

- -
function Person(name) {
-  this.name = name;
-  this.greeting = function() {
-    alert('Hi! I\'m ' + this.name + '.');
-  };
-}
- -

Nach dem Anlegen der neuen Objekte enthalten die Variablen person1 und person2 die folgenden Objekte:

- -
{
-  name: 'Bob',
-  greeting: function() {
-    alert('Hi! I\'m ' + this.name + '.');
-  }
-}
-
-{
-  name: 'Sarah',
-  greeting: function() {
-    alert('Hi! I\'m ' + this.name + '.');
-  }
-}
- -

Beachten Sie, dass wir beim Aufruf unserer Konstruktor-Funktion jedes Mal greeting() definieren, was nicht ideal ist. Um dies zu vermeiden, können wir stattdessen Funktionen auf dem Prototypen definieren, die wir uns später genauer ansehen werden.

- -

Erstellen unseres finalen Konstruktors

- -

Das Beispiel, das wir oben betrachtet haben, war nur ein einfaches Beispiel, um den Einstieg zu erleichtern. Lassen Sie uns nun weitermachen und unsere finale Konstruktor-Funktion Person() erstellen.

- -
    -
  1. Entfernen Sie den bisher eingefügten Code und fügen Sie nachfolgenden Konstruktor als Ersatz hinzu - dies ist im Prinzip genau dasselbe, wie das einfache Beispiel, nur etwas komplexer: -
    function Person(first, last, age, gender, interests) {
    -  this.name = {
    -     first : first,
    -     last : last
    -  };
    -  this.age = age;
    -  this.gender = gender;
    -  this.interests = interests;
    -  this.bio = function() {
    -    alert(this.name.first + ' ' + this.name.last + ' is ' + this.age + ' years old. He likes ' + this.interests[0] + ' and ' + this.interests[1] + '.');
    -  };
    -  this.greeting = function() {
    -    alert('Hi! I\'m ' + this.name.first + '.');
    -  };
    -}
    -
  2. -
  3. Fügen Sie nun unter dem Code von oben folgende Zeile ein, um eine Objektinstanz zu erzeugen: -
    var person1 = new Person('Bob', 'Smith', 32, 'male', ['music', 'skiing']);
    -
  4. -
- -

Sie werden nun sehen, dass Sie auf die Eigenschaften und Methoden zugreifen können, genau wie wir es zuvor getan haben - probieren Sie das in Ihrer JS-Konsole aus:

- -
person1['age']
-person1.interests[1]
-person1.bio()
-// etc.
- -
-

Hinweis: Wenn Sie Probleme haben, dies zum Laufen zu bringen, vergleichen Sie Ihren Code mit unserer Version - siehe oojs-class-finished.html (hier können Sie auch sehen, wie es live läuft).

-
- -

Weitere Übungen

- -

Versuchen Sie zunächst, ein paar weitere eigene Objekte hinzuzufügen, und versuchen Sie, die Eigenschaften bzw. Funktionen der daraus erzeugten Objektinstanzen zu nutzen bzw. zu verändern.

- -

Außerdem gibt es einige Probleme mit unserer bio()-Methode - die Ausgabe enthält immer das Pronomen "He", egal ob Ihre Person weiblich ist oder einem anderen Geschlecht angehört. Und die bio()-Methode wird nur zwei Interessen enthalten, auch wenn mehr im Interessen-Array aufgelistet sind. Finden Sie heraus, wie man das in der Klassendefinition (Konstruktor) beheben kann? Sie können jeden beliebigen Code in einen Konstruktor einfügen (Sie werden wahrscheinlich ein paar Bedingungen und eine Schleife benötigen). Überlegen Sie sich, wie die Sätze je nach Geschlecht und je nachdem, ob die Anzahl der aufgelisteten Interessen 1, 2 oder mehr als 2 beträgt, unterschiedlich strukturiert werden sollten.

- -
-

Hinweis: Wenn Sie nicht weiterkommen, haben wir eine Antwort bzw. Lösung in unserem GitHub-Repo bereitgestellt (Sehen Sie es sich hier an) - versuchen Sie bitte aber erst einmal, die Lösung selbst zu schreiben!

-
- -

Andere Möglichkeiten, Objektinstanzen zu erzeugen

- -

Bisher haben wir zwei verschiedene Wege gesehen, um eine Objektinstanz zu erzeugen - die Deklaration eines Objektes als Literal und die Verwendung einer Konstruktorfunktion (siehe oben).

- -

Diese machen Sinn, aber es gibt auch andere Wege - wir möchten Sie mit diesen vertraut machen, falls Sie auf Ihren Reisen im Web auf sie stoßen sollten.

- -

Der Object()-Konstruktor

- -

Zuerst können Sie den Object() Konstruktor verwenden, um ein neues Objekt zu erstellen. Ja, sogar generische Objekte haben einen Konstruktor, der ein leeres Objekt erzeugt.

- -
    -
  1. Geben Sie dies in die JavaScript-Konsole Ihres Browsers ein: -
    var person1 = new Object();
    -
  2. -
  3. Diese speichert ein leeres Objekt in der Variable person1. Sie können dann diesem Objekt Eigenschaften und Methoden mit Punkt- oder Klammer-Notation hinzufügen, wie gewünscht. Versuchen Sie diese Beispiele in Ihrer Konsole: -
    person1.name = 'Chris';
    -person1['age'] = 38;
    -person1.greeting = function() {
    -  alert('Hi! I\'m ' + this.name + '.');
    -};
    -
  4. -
  5. Sie können auch ein Objektliteral als Parameter an den Object() Konstruktor übergeben, um es mit Eigenschaften/Methoden vorzufüllen. Versuchen Sie folgendes in Ihrer JS-Konsole: -
    var person1 = new Object({
    -  name: 'Chris',
    -  age: 38,
    -  greeting: function() {
    -    alert('Hi! I\'m ' + this.name + '.');
    -  }
    -});
    -
  6. -
- -

Verwendung der Methode create()

- -

Konstruktoren können Ihnen helfen, Ihren Code zu ordnen - Sie können Konstruktoren an einer Stelle erstellen und dann bei Bedarf Instanzen davon erstellen - und es ist immer nachvollziehbar, woher sie kommen.

- -

Einige Leute ziehen es jedoch vor, Objektinstanzen zu erstellen, ohne vorher Konstruktoren zu erstellen, insbesondere wenn sie nur wenige Instanzen eines Objekts erstellen müssen. JavaScript hat eine eingebaute Methode namens create(), die es Ihnen einfach ermöglicht, dies zu tun. Mit ihr können Sie ein neues Objekt auf Basis eines beliebigen vorhandenen Objekts erstellen.

- -
    -
  1. Wenn Ihre fertige Übung aus den vorherigen Abschnitten im Browser geladen ist, versuchen Sie folgendes in Ihrer JavaScript-Konsole: -
    var person2 = Object.create(person1);
    -
  2. -
  3. Nun geben Sie bitte folgendes in die JavaScript-Konsole ein: -
    person2.name
    -person2.greeting()
    -
  4. -
- -

Sie werden sehen, dass person2 auf der Basis von person1 erstellt wurde - es hat die gleichen Eigenschaften und die gleiche Methode, die ihm zur Verfügung stehen.

- -

Eine Einschränkung von create() ist, dass der IE8 es nicht unterstützt. Daher können Konstruktoren effektiver sein, wenn Sie ältere Browser unterstützen wollen.

- -

Wir werden die Auswirkungen von create() später noch genauer untersuchen.

- -

Zusammenfassung

- -

Dieser Artikel hat eine vereinfachte Sicht der objektorientierten Theorie geliefert - das ist noch lange nicht die ganze Geschichte, aber er gibt Ihnen eine Vorstellung davon, womit wir es hier zu tun haben. Darüber hinaus haben wir damit begonnen, verschiedene Möglichkeiten der Erzeugung von Objektinstanzen zu betrachten.

- -

Im nächsten Artikel werden wir uns mit JavaScript-Objekt-Prototypen beschäftigen.

- -

{{PreviousMenuNext("Learn/JavaScript/Objects/Basics", "Learn/JavaScript/Objects/Object_prototypes", "Learn/JavaScript/Objects")}}

- -

In diesem Modul

- - diff --git a/files/es/_redirects.txt b/files/es/_redirects.txt index 2c0faa8c5e..bc65d530bf 100644 --- a/files/es/_redirects.txt +++ b/files/es/_redirects.txt @@ -1187,6 +1187,8 @@ /es/docs/Learn/JavaScript/First_steps/Prueba_tus_habilidades:_Strings /es/docs/Learn/JavaScript/First_steps/Test_your_skills:_Strings /es/docs/Learn/JavaScript/First_steps/Qué_es_JavaScript /es/docs/Learn/JavaScript/First_steps/What_is_JavaScript /es/docs/Learn/JavaScript/Objects/Ejercicio_práctico_de_construcción_de_objetos /es/docs/Learn/JavaScript/Objects/Object_building_practice +/es/docs/Learn/JavaScript/Objects/Inheritance /es/docs/Learn/JavaScript/Objects/Classes_in_JavaScript +/es/docs/Learn/JavaScript/Objects/Object-oriented_JS /es/docs/conflicting/Learn/JavaScript/Objects/Classes_in_JavaScript /es/docs/Learn/Server-side/Django/Introducción /es/docs/Learn/Server-side/Django/Introduction /es/docs/Learn/Server-side/Primeros_pasos /es/docs/Learn/Server-side/First_steps /es/docs/Learn/Server-side/Primeros_pasos/Introducción /es/docs/Learn/Server-side/First_steps/Introduction diff --git a/files/es/_wikihistory.json b/files/es/_wikihistory.json index dc0fdd2b06..889610fe22 100644 --- a/files/es/_wikihistory.json +++ b/files/es/_wikihistory.json @@ -3249,7 +3249,7 @@ "kevin-loal98" ] }, - "Learn/JavaScript/Objects/Inheritance": { + "Learn/JavaScript/Objects/Classes_in_JavaScript": { "modified": "2020-07-28T01:53:21.821Z", "contributors": [ "Fernando-Funes", @@ -3268,25 +3268,6 @@ "Enesimus" ] }, - "Learn/JavaScript/Objects/Object-oriented_JS": { - "modified": "2020-08-08T09:41:13.386Z", - "contributors": [ - "Nachec", - "andyesp", - "Fernando-Funes", - "jhonarielgj", - "rimbener", - "ReneAG", - "EnekoOdoo", - "ivanagui2", - "cristianmarquezp", - "djdouta", - "paulaco", - "martinGerez", - "anyruizd", - "Michelangeur" - ] - }, "Learn/JavaScript/Objects/Object_building_practice": { "modified": "2020-07-16T22:32:30.877Z", "contributors": [ @@ -21214,6 +21195,25 @@ "StripTM" ] }, + "conflicting/Learn/JavaScript/Objects/Classes_in_JavaScript": { + "modified": "2020-08-08T09:41:13.386Z", + "contributors": [ + "Nachec", + "andyesp", + "Fernando-Funes", + "jhonarielgj", + "rimbener", + "ReneAG", + "EnekoOdoo", + "ivanagui2", + "cristianmarquezp", + "djdouta", + "paulaco", + "martinGerez", + "anyruizd", + "Michelangeur" + ] + }, "conflicting/MDN/Contribute/Getting_started": { "modified": "2019-01-16T18:56:38.941Z", "contributors": [ diff --git a/files/es/conflicting/learn/javascript/objects/classes_in_javascript/index.html b/files/es/conflicting/learn/javascript/objects/classes_in_javascript/index.html new file mode 100644 index 0000000000..8cafe5c160 --- /dev/null +++ b/files/es/conflicting/learn/javascript/objects/classes_in_javascript/index.html @@ -0,0 +1,308 @@ +--- +title: JavaScript orientado a objetos para principiantes +slug: conflicting/Learn/JavaScript/Objects/Classes_in_JavaScript +tags: + - Aprender + - Artículo + - Constructor + - Crear + - Create + - JSOO + - JavaScript + - OOJS + - OOP + - Object + - Objeto + - Orientado a Objeto + - Principiante + - Programación orientada a objetos + - instance + - instanciar + - l10n:priority +translation_of: Learn/JavaScript/Objects/Object-oriented_JS +original_slug: Learn/JavaScript/Objects/Object-oriented_JS +--- +
{{LearnSidebar}}
+ +
{{PreviousMenuNext("Learn/JavaScript/Objects/Basics", "Learn/JavaScript/Objects/Object_prototypes", "Learn/JavaScript/Objects")}}
+ +

Con lo básico fuera del camino, nos enfocaremos en Javascript Orientado a Objetos (JSOO) — este artículo presenta una descripción básica de la teoría de la Programación Orientada a Objetos (POO), luego explora cómo Javascript emula classes de objetos via funciones constructoras, y cómo crea instancias de objetos.

+ + + + + + + + + + + + +
Prerequisitos:Conocimientos básicos de computación, entendimiento básico de HTML y CSS, familiaridad con las bases de Javascript (ver Primeros pasos con JavaScript y Bloques de construcción JavaScript) y las bases de JSOO (ver Introducción a objetos).
Objetivo: +

Entender la teoría base de la programación orientada a objetos, como se relaciona esta con JavaScript ("todo es un objeto"), y como crear constructores e instacias de objetos.

+
+ +

Programacion Orientada a Objetos— lo básico

+ +

Para empezar, daremos una descripción simple y de alto nivel acerca de lo que es la Programación Orientada a Objetos (POO). Decimos simple, porque la POO puede volverse complicada rápidamente, y darte un tratamiento completo ahora, probablemente podría confundirte más que ayudar. La idea básica de la POO es que usamos objetos para modelar cosas del mundo real que queremos representar en nuestros programas, y/o proveemos una simple manera para acceder a la funcionalidad que, de otra manera, sería difícil o imposible de usar.

+ +

Los objetos pueden contener información y código relacionados, los cuales representan información acerca de lo que estás tratando de modelar, y la funcionalidad o comportamiento que deseas que tenga.  Los datos de un Objeto (y frecuentemente, también las funciones) se pueden almacenar ordenadamente (la palabra oficial es encapsular) dentro del paquete de un objeto (al que se puede asignar un nombre específico, llamado a veces espacio de nombres), haciéndolo fácil de estructurar y acceder; los objetos también se usan comúnmente como almacenes de datos que se pueden enviar fácilmente a través de la red.

+ +

Definiendo una plantilla de objeto

+ +

Vamos a considerar un sencillo programa que muestra información sobre estudiantes y profesores en una escuela. Aquí daremos un vistazo a la POO (Programación Orientada a Objetos) en general, no en el contexto de algún lenguaje de programación específico.

+ +

Para empezar, podríamos volver a ver al objeto Persona de nuestro artículo de primeros objetos, que define los datos generales y funcionalidades de una persona. Hay muchas cosas que podrías saber acerca de una persona (su dirección, estatura, tamaño de calzado, perfil de ADN, número de pasaporte, rasgos significativos de su personalidad...), pero, en este caso, solo estamos interesados en mostrar su nombre, edad, género e intereses, además de una pequeña introducción sobre este individuo basada en los datos anteriores. También queremos que sea capaz de saludar. 

+ +

Esto es conocido como abstracción —  crear un modelo simple de algo complejo que represente sus aspectos más importantes y que sea fácil de manipular para el propósito de nuestro programa.

+ +

+ +

En algunos lenguajes de POO, esta definición de tipo de objeto se la llama class (JavaScript utiliza diferentes mecanismos y terminologías, como verás a continuación) — esto no es en realidad un objeto, en vez de esto es un modelo que define las características que un objeto debería tener.

+ +

Creando objetos

+ +

Partiendo de nuestra clase, podemos crear instancias de objetos — objetos que contienen los datos y funcionalidades definidas en la clase original. Teniendo a nuestra clase Persona, ahora podemos crear gente con características más específicas: 

+ +

+ +

Cuando una instancia del objeto es creada a partir de una clase, se ejecuta la función constructora (constructor en inglés) de la clase para crearla. El proceso de crear una instancia del objeto desde una clase se llama instanciación.

+ +

Clases especializadas

+ +

En este caso nosotros no queremos personas genericas — queremos docentes y estudiantes, que son los dos tipos más específicos de personas. En POO, podemos crear nuevas clases basadas en otras clases, estas nuevas clases secundarias se pueden hacer para  heredar los datos y código de su clase primaria, de modo que pueden reutilizar la funcionalidad común a todos los tipos de objetos en lugar de tener que duplicarla. Cuando la funcionalidad difiere entre clases, puedes definir funciones especializadas directamente en ellas según sea necesario.

+ +

+ +

Esto es realmente útil, los profesores y los estudiantes comparten muchas características comunes como el nombre, el género y la edad, por lo que es conveniente tener que definir esas características solo una vez. También puedes definir la misma característica por separado en diferentes clases, ya que cada definición de esa característica estará en un espacio de nombres diferente. Por ejemplo, el saludo de un estudiante puede tener la forma "Yo, soy [Nombre]" (por ejemplo, Yo, soy Sam), mientras que un profesor puede usar algo más formal, como "Hola, mi nombre es [Prefix] [lastName], y enseño [Asunto] ". (Por ejemplo, Hola, mi nombre es Sr. Griffiths, y yo enseño Química).

+ +
+

Nota:  la palabra elegante para la capacidad de múltiples tipos de objetos de implementar la misma funcionalidad es polimorfismo. Por si acaso te preguntabas.

+
+ +

Ahora puedes crear instancias de objetos de las clases "hijo". Por ejemplo:

+ +

+ +

En el resto del articulo, comenzaremos a ver como podemos practicar la teoría de POO en JavaScript.

+ +

Constructores e instancias de objetos

+ +

Algunas personas sostienen que JavaScript no es un verdadero lenguaje orientado a objetos — por ejemplo, su enunciado class es sólo azúcar sintáctica sobre la herencia prototípica existente y no es una class en el sentido tradicional. JavaScript, utiliza funciones especiales llamadas funciones constructoras para definir objetos y sus características. Son útiles porque a menudo te encontrarás con situaciones en las que no sabes cuántos objetos crearás; los constructores proporcionan los medios para crear tantos objetos como necesites de una manera efectiva, adjuntando datos y funciones a ellos según sea necesario.

+ +

Cuando se crea una nueva instancia del objeto a partir de una función constructora, su funcionalidad central (tal como se define en su prototipo, que exploraremos en el artículo Prototipos de objetos) no se copia en el nuevo objeto como lenguajes OO "clásicos", sino que la funcionalidad está vinculada a través de una cadena de referencia llamada cadena prototipo. Así que esto no es una verdadera instanciación, estrictamente hablando, JavaScript usa un mecanismo diferente para compartir funcionalidad entre objetos.

+ +
+

Nota: no ser "POO clásica" no es necesariamente algo malo; Como se mencionó anteriormente, la POO puede ser muy compleja muy rápidamente, y JavaScript tiene algunas agradables formas de aprovechar las características de la OO sin tener que profundizar demasiado en ello.

+
+ +

Exploremos la creación de clases a través de constructores y la creación de instancias de objetos a partir de ellas en JavaScript. En primer lugar, nos gustaría que hicieras una nueva copia local del archivo oojs.html que vimos en nuestro primer artículo de Objetos.

+ +

Un ejemplo simple

+ +
    +
  1. Comencemos por ver cómo puedes definir una persona con una funcion normal. Agrega esta funcion dentro del elemento script: + +
    function createNewPerson(name) {
    +  var obj = {};
    +  obj.name = name;
    +  obj.greeting = function() {
    +    alert('Hi! I\'m ' + this.name + '.');
    +  };
    +  return obj;
    +}
    +
  2. +
  3. Ahora puedes crear una nueva persona llamando a esta funcion — prueba con las siguientes lineas en la consola Javascript de tu navegador: +
    var salva = createNewPerson('Salva');
    +salva.name;
    +salva.greeting();
    + Esto funciona bastante bien, pero es un poco largo; si sabemos que queremos crear un objeto, ¿por qué necesitamos crear explícitamente un nuevo objeto vacío y devolverlo? Afortunadamente, JavaScript nos proporciona un práctico acceso directo, en forma de funciones constructoras — ¡hagamos una ahora!
  4. +
  5. Reemplaza tu función anterior por la siguiente: +
    function Person(name) {
    +  this.name = name;
    +  this.greeting = function() {
    +    alert('Hi! I\'m ' + this.name + '.');
    +  };
    +}
    +
  6. +
+ +

La función constructora es la versión de JavaScript de una clase. Notarás que tiene todas las características que esperas en una función, aunque no devuelve nada o crea explícitamente un objeto — básicamente sólo define propiedades y métodos. Verás que la palabra clave this se está usando aquí también — es básicamente decir que cuando se crea una de estas instancias de objeto, la propiedad name del objeto será igual al valor del nombre pasado a la llamada del constructor, y el método greeting() usará también el valor del nombre pasado a la llamada del constructor.

+ +
+

Nota: Un nombre de función constructora generalmente comienza con una letra mayúscula — esta convención se utiliza para hacer que las funciones constructoras sean más fáciles de reconocer en el código.

+
+ +

Entonces, ¿cómo llamamos a un constructor para crear algunos objetos?

+ +
    +
  1. Agrega las siguientes líneas debajo de tu código anterior: +
    var person1 = new Person('Bob');
    +var person2 = new Person('Sarah');
    +
  2. +
  3. Guarda el código y vuelve a cargarlo en el navegador, e intenta ingresar las siguientes líneas en la consola Javascript : +
    person1.name
    +person1.greeting()
    +person2.name
    +person2.greeting()
    +
  4. +
+ +

¡Guaw! Ahora veras que tenemos dos nuevos objetos, cada uno de los cuales está almacenado en un espacio de nombres diferente: para acceder a sus propiedades y métodos, debes llamarlos como person1 o  person2; están cuidadosamente empaquetados para que no entren en conflicto con otras funciones. Sin embargo, tienen disponible la misma propiedad name y el método greeting(). Ten en cuenta que están utilizando su propio name que se les asignó cuando se crearon; esta es una razón por la cual es muy importante usar this, para que usen sus propios valores, y no algún otro valor.

+ +

Veamos nuevamente las llamadas del constructor:

+ +
var person1 = new Person('Bob');
+var person2 = new Person('Sarah');
+ +

En cada caso, la  palabra clave new se usa para indicarle al navegador que queremos crear una nueva instancia del objeto, seguida del nombre de la función con sus parámetros requeridos entre paréntesis, y el resultado se almacena en una variable — muy similar a cómo se llama a una función estándar. Cada instancia se crea de acuerdo con esta definición:

+ +
function Person(name) {
+  this.name = name;
+  this.greeting = function() {
+    alert('Hi! I\'m ' + this.name + '.');
+  };
+}
+ +

Una vez creados los nuevos objetos, las variables person1 y person2 contienen los siguientes objetos:

+ +
{
+  name: 'Bob',
+  greeting: function() {
+    alert('Hi! I\'m ' + this.name + '.');
+  }
+}
+
+{
+  name: 'Sarah',
+  greeting: function() {
+    alert('Hi! I\'m ' + this.name + '.');
+  }
+}
+ +

Ten en cuenta que cuando llamamos a nuestra función constructora, estamos definiendo greeting() cada vez, lo cual no es lo ideal. Para evitar esto, podemos definir funciones en el prototipo, que veremos más adelante.

+ +

Creando nuestro constructor final

+ +

El ejercicio que vimos anteriormente fue solo un ejemplo simple para comenzar. Ahora crearemos nuestra función constructor Person() final.

+ +
    +
  1. Elimina el código que insertaste hasta ahora y agrega este constructor de reemplazo; este es exactamente el mismo que el ejemplo simple del principio, con un poco más de complejidad: +
    function Person(first, last, age, gender, interests) {
    +  this.name = {
    +    'first': first,
    +    'last' : last
    +  };
    +  this.age = age;
    +  this.gender = gender;
    +  this.interests = interests;
    +  this.bio = function() {
    +    alert(this.name.first + ' ' + this.name.last + ' is ' + this.age + ' years old. He likes ' + this.interests[0] + ' and ' + this.interests[1] + '.');
    +  };
    +  this.greeting = function() {
    +    alert('Hi! I\'m ' + this.name.first + '.');
    +  };
    +}
    +
  2. +
  3. Ahora, agrega la siguiente línea para crear una instancia del objeto: +
    var person1 = new Person('Bob', 'Smith', 32, 'male', ['music', 'skiing']);
    +
  4. +
+ +

Ahora verás que puedes acceder a las propiedades y métodos justo como lo hiciste anteriormente — intenta esto en tu consola JS:

+ +
person1['age']
+person1.interests[1]
+person1.bio()
+// etc.
+ +
+

Nota: Si tienes problemas para lograr que funcione, puedes comparar tu código con nuestra versión — ve oojs-class-finished.html (también lo puedes ver corriendo en vivo).

+
+ +

Ejercicios adicionales

+ +

Para empezar, intenta añadir un par de líneas de creación de objetos propias, y trata de obtener y asignar valores a los miembros de las instancias del objeto.

+ +

Además, hay un par de problemas con nuestro método bio() — la salida siempre incluye el pronombre "He", incluso para personas de otros géneros. Y bio solamente incluye dos intereses, sin importar la cantidad que hay en el arreglo interests. ¿Podrías corregir esto en la definición de la clase (constructor)? Puedes poner cualquier código dentro de un constructor (probablemente necesites algunos condicionales y un bucle). Piensa como se deben estructurar las declaraciones dependiendo del género, y de la cantidad de intereses.

+ +
+

Note: Si estás atascado, hay una respuesta en nuestro repositorio de GitHub (see it live) — igualmente ¡intentea resolverla primero!

+
+ +

Otras formas de crear instancias de objetos

+ +

Hasta ahora hemos visto dos diferentes formas de crear una instancia de objeto — declarando un objeto literal, y usando una función constructora (ver arriba).

+ +

Esto tiene sentido, pero hay otras formas — se muestran aquí para que te vayas familiarizando en caso de encontrarte con ellas.

+ +

El constructor Object()

+ +

Antes que nada, puedes usar el constructor Object() para crear un nuevo objeto. Si, incluso objetos genéricos tienen un constructor que genera un objeto vacío.

+ +
    +
  1. Intenta ingresar este código en la consola JavaScript de tu navegador: +
    var person1 = new Object();
    +
  2. +
  3. Esto guarda un objeto vacío en la variable person1. Luego pueded agregar propiedades y métodos a este objeto usando la notación de punto (.) o de corchetes (['']); prueba estos ejemplos en tu consola: +
    person1.name = 'Chris';
    +person1['age'] = 38;
    +person1.greeting = function() {
    +  alert('Hi! I\'m ' + this.name + '.');
    +};
    +
  4. +
  5. También puedes pasar un objeto literal como parámetro al constructor Object(), para precargarlo con propiedades/métodos. Prueba esto en tu consola: +
    var person1 = new Object({
    +  name: 'Chris',
    +  age: 38,
    +  greeting: function() {
    +    alert('Hi! I\'m ' + this.name + '.');
    +  }
    +});
    +
  6. +
+ +

Usando el método create()

+ +

Los constructores te pueden ayudar a ordenar tu código — puedes crear constructores en un lugar, y luego crear instancias cuando sean necesarias.

+ +

Sin embargo, algunas personas prefieren crear instancias de objetos sin crear antes constructores, especialmente si van a crear solamente pocas instancias de un objeto.

+ +

JavaScript tiene un método llamado create() que permite hacer esto. Con este método puedes crear un nuevo objeto basado en cualquier otro objeto existente.

+ +
    +
  1. Con tu ejercicio de la sección anterior cargado en el navegador, prueba esto en tu consola JavaScript +
    var person2 = Object.create(person1);
    +
  2. +
  3. Y ahora prueba esto: +
    person2.name
    +person2.greeting()
    +
  4. +
+ +

Verás que person2 fue creado basado en person1 — tiene las mismas propiedades y métodos.

+ +

Una limitación del método create() es que no está soportado por el navegador IE8. Por lo que los constructores serán más efectivos sin necesitas soportar navegadores antiguos.

+ +

Más tarde, exploraremos en detalle los efectos de create().

+ +

Resumen

+ +

Este artículo provee una visión simplificada de la teoría de la orientación a objetos — esta no es toda la historia, pero te da una idea de con que estamos lidiando aquí. Adicionalmente, empezamos a ver como JavaScript está relacionado y difiere de la orientación a objetos "clásica", cómo usamos funciones constructoras para implementar clases en JavaScript, y diferentes formas de generar instancias de objetos.

+ +

En el próximo artículo, exploraremos los prototipos de objeto JavaScript.

+ +

{{PreviousMenuNext("Learn/JavaScript/Objects/Basics", "Learn/JavaScript/Objects/Object_prototypes", "Learn/JavaScript/Objects")}}

+ +

En este modulo

+ + diff --git a/files/es/learn/javascript/objects/classes_in_javascript/index.html b/files/es/learn/javascript/objects/classes_in_javascript/index.html new file mode 100644 index 0000000000..2af15e98bf --- /dev/null +++ b/files/es/learn/javascript/objects/classes_in_javascript/index.html @@ -0,0 +1,401 @@ +--- +title: Herencia en JavaScript +slug: Learn/JavaScript/Objects/Classes_in_JavaScript +translation_of: Learn/JavaScript/Objects/Inheritance +original_slug: Learn/JavaScript/Objects/Inheritance +--- +
{{LearnSidebar}}
+ +
{{PreviousMenuNext("Learn/JavaScript/Objects/Object_prototypes", "Learn/JavaScript/Objects/JSON", "Learn/JavaScript/Objects")}}
+ +

Con la mayoría de los detalles internos de OOJS (JavaScript Orientado a Objetos) explicados, este artículo muestra cómo crear clases "hijo" (constructores) que heredan características de sus clases "padre". Además, presentamos algunos consejos sobre cuándo y dónde puedes usar OOJS y cómo se crean las clases con la sintaxis moderna de ECMAScript.

+ + + + + + + + + + + + +
Pre-requisitos:Conocimientos básicos de informática, una comprensión básica de HTML y CSS, familiaridad con los principios básicos de JavaScript (ver Primeros pasos y Construyendo bloques) y conceptos básicos de OOJS (ver Introduccion a objetos).
Objetivo:Entender cómo es posible implementar la herencia en JavaScript.
+ +

Herencia prototípica

+ +

Hasta ahora hemos visto algo de herencia en acción — hemos visto cómo funcionan las cadenas de prototipos, y cómo los miembros son heredados subiendo una cadena. Pero principalmente esto ha involucrado funciones integradas del navegador. ¿Cómo creamos un objeto en JavaScript que hereda de otro objeto?

+ +

Exploremos cómo hacer esto con un ejemplo concreto.

+ +

Primeros pasos

+ +

Primero que nada, hazte una copia local de nuestro archivo  oojs-class-inheritance-start.html (míralo corriendo en vivo también). Dentro de aquí encontrarás el mismo ejemplo de constructor de Persona() que hemos estado usando a través del módulo, con una ligera diferencia — hemos definido solo las propiedades dentro del constructor:

+ +
function Persona(nombrePila, apellido, edad, genero, intereses) {
+  this.nombre = {
+    nombrePila,
+    apellido
+  };
+  this.edad = edad;
+  this.genero = genero;
+  this.intereses = intereses;
+};
+ +

Todos los métodos están definidos en el prototipo del constructor. Por ejemplo:

+ +
Persona.prototype.saludo = function() {
+  alert('¡Hola! soy ' + this.nombre.nombrePila + '.');
+};
+ +
+

Nota: En el código fuente, también verá los métodos bio() y despedida() definidos. Más tarde verá cómo estos pueden ser heredados por otros constructores.

+
+ +

Digamos que quisieramos crear una clase de Profesor, como la que describimos en nuestra definición inicial orientada a objetos, que hereda todos los miembros de Persona, pero también incluye:

+ +
    +
  1. Una nueva propiedad, materia — esto contendrá la materia que el profesor enseña.
  2. +
  3. Un método actualizado de saludo(), que suena un poco más formal que el método estándar de saludo() — más adecuado para un profesor que se dirige a algunos estudiantes en la escuela.
  4. +
+ +

Definiendo un constructor Profesor()

+ +

Lo primero que tenemos que hacer es crear el constructor Profesor()  — añadimos lo siguiente tras el código existente:

+ +
function Profesor(nombrePila, apellido, edad, genero, intereses, materia) {
+  Person.call(this, nombrePila, apellido, edad, genero, intereses);
+
+  this.materia = materia;
+}
+ +

Esto es similar al constructor de Persona en muchos aspectos, pero hay algo extraño aquí que no hemos visto antes: la función call (). Esta función básicamente le permite llamar a una función definida en otro lugar, pero en el contexto actual.
+ El primer parámetro especifica el valor de this que desea utilizar al ejecutar la función, y los otros parámetros son aquellos que deben pasarse a la función cuando se invoca.

+ +

Queremos que el constructor Profesor() tome los mismos parámetros que el constructor Persona() del que está heredando, por lo que los especificamos todos como parámetros en la invocación call().

+ +

La última línea dentro del constructor simplemente define la nueva propiedad subject que los profesores tendrán y que las personas genéricas no tienen.

+ +

Como nota, podríamos haber simplemente hecho esto:

+ +
function Profesor(nombrePila, apellido, edad, genero, intereses, materia) {
+  this.nombre = {
+    nombrePila,
+    apellido
+  };
+  this.edad = edad;
+  this.genero = genero;
+  this.intereses = intereses;
+  this.materia = materia;
+}
+ +

Pero esto es solo definir las propiedades de nuevo, no heredarlas de Persona(), por lo que anula el punto de lo que estamos tratando de hacer. También lleva más líneas de código.

+ +

Heredando de un constructor sin parámetros

+ +

Nótese que si el constructor del cual se está heredando no toma los valores de sus propiedades de parámetros, no se necesita especificarlos como argumentos adicionales en call(). Por ejemplo, si se tuviera algo muy simple como esto:

+ +
function Brick() {
+  this.width = 10;
+  this.height = 20;
+}
+ +

Se podrían heredar las propiedades width y height haciendo esto (así como los otros pasos descritos a continuación, por supuesto):

+ +
function BlueGlassBrick() {
+  Brick.call(this);
+
+  this.opacity = 0.5;
+  this.color = 'blue';
+}
+ +

Nótese que solo especificamos this dentro de call() — no se requieren otros parámetros ya que no estamos heredando ninguna propiedad del padre que sea establecida por parámetros.

+ +

Estableciendo el prototipo de Profesor() y su referencia al constructor

+ +

Todo va bien hasta ahora, pero tenemos un problema. Definimos un nuevo constructor, y tiene una propiedad prototype, la cual por defecto solo contiene una referencia a la función constructor en sí misma. No contiene los métodos de la propiedad prototype del constructor Persona. Para ver esto, introduzca Object.getOwnPropertyNames(Profesor.prototype) ya sea en el campo de texto o en la consola de Javascript . Introdúzcalo nuevamente, reemplazando Profesor con Persona. El nuevo constructor tampoco hereda esos métodos. Para ver esto, compare los resultados de Persona.prototype.saludo and Profesor.prototype.saludo. Necesitamos obtener Profesor() para obtener los métodos definidos en el prototipo de Persona(). ¿Cómo lo hacemos?

+ +
    +
  1. Añade la siguiente línea debajo de tu adición anterior: +
    Profesor.prototype = Object.create(Persona.prototype);
    + Aquí es cuando nuestro amigo create() sale al rescate de nuevo. En este caso lo estamos usando para crear un nuevo objeto y hacerlo el valor de Profesor.prototype. El nuevo objeto tiene Persona.prototype como su prototipo y por lo tanto heredará, si y cuando lo necesite, todos los métodos disponibles en Persona.prototype.
  2. +
  3. Necesitamos hacer una cosa más antes de proseguir. Después de agregar la última línea, la propiedad constructor de  Profesor.prototype es ahora igual a Persona(), debido a que acabamos de asignar Profesor.prototype para que haga referencia a un objeto que hereda sus propiedades de Persona.prototype! Ahora prueba guardando tu código, carga la página en un explorador e intenta verificar en la consola el valor de  Profesor.prototype.constructor.
  4. +
  5. Esto puede volverse un problema, así que necesitamos corregirlo. Puedes hacerlo regresando a tu código y agregando la siguiente línea al final: +
    Profesor.prototype.constructor = Profesor;
    +
  6. +
  7. Ahora, si guardas y actualizas, el valor de Profesor.prototype.constructor debe regresar Profesor(), como se espera, además de que ahora estamos heredando de Persona()!
  8. +
+ +

Dándole a Profesor() un nuevo método saludo()

+ +

Para finalizar nuestro código, necesitamos definir un nuevo método saludo() en el constructor de Profesor().

+ +

La manera más fácil es definirlo en el prototipo de Profesor() — agrega lo siguiente al final de tu código:

+ +
Profesor.prototype.saludo = function() {
+  var prefijo;
+
+  if (this.genero === 'masculino' || this.genero === 'Masculino' || this.genero === 'm' || this.genero === 'M') {
+    prefijo = 'Sr.';
+  } else if (this.genero === 'female' || this.genero === 'Femenino' || this.genero === 'f' || this.genero === 'F') {
+    prefijo = 'Sra.';
+  } else {
+    prefijo = 'Sx.';
+  }
+
+  alert('Hola. Mi nombre es ' + prefijo + ' ' + this.nombre.apellido + ', y enseño ' + this.materia + '.');
+};
+ +

Esto muestra el saludo del profesor, el cual además utiliza un prefijo apropiado para su género, resuelto utilizando un bloque else-if.

+ +

Probando el ejemplo

+ +

Ahora que ha ingresado todo el código, intente creando una instancia de objeto desde Profesor() poniendo lo que sigue al final de su archivo (o algo similar a su elección):

+ +
var teacher1 = new Teacher('Dave', 'Griffiths', 31, 'male', ['football', 'cookery'], 'mathematics');
+ +

Ahora guarde y actualice, e intente accediendo a las propiedades y metodos de su nuevo teacher1 objecto, por ejemplo:

+ +
teacher1.name.first;
+teacher1.interests[0];
+teacher1.bio();
+teacher1.subject;
+teacher1.greeting();
+teacher1.farewell();
+ +

Esto deberia trabajar bien. Las consultas de las líneas 1, 2, 3, y 6 acceden a miembros heredados del genérico Person() constructor (clase). La consulta de la línea 4 accede un miembro que es disponible solamente en el mas especializado Teacher() constructor (clase). La consulta de la línea 5 accedería a un miembro desde Person(), excepto por el hecho que Teacher() tiene sus propios miembros con el mismo nombre, entonces la consulta accede a ese miembro.

+ +
+

Nota: Si tiene problemas con el funcionamiento, compare su código con nuestra versión final (vea corriendo en vivo también).

+
+ +

La técnica que mostramos aquí no es la única para crear herencia de clases en JavaScript, pero funciona OK, y le dá una buena idea acerca de cómo implementar herencia en JavaScript.

+ +

También estará interesado en verificar algo de las nuevas características de {{glossary("ECMAScript")}} que nos permiten hacer herencia mas claramente en JavaScript (véase Classes). No se cubrió todo aquí, como tampoco es soportado aún por todos los navegadores. Todo el otro código de constructores que se discutió aquí en estos artículos son soportados por IE9 o superior, y hay caminos para lograr superiores soportes que estos.

+ +

Un simple camino es usar una librería de JavaScript — la mayoría de las opciones mas populares tienen un facil ajuste de funcionalidad disponible para hacer herencia mas facil y rápido. CoffeeScript por ejemplo provee class, extends, etc.

+ +

Un ejercicio mas allá

+ +

En nuestra Sección teórica de POO, también incluimos una clase Student como un concepto, el cual hereda todas las características de Person, y también tiene un método diferende de greeting() que Person que es mas informal que el saludo de los profesores Teacher. Dele una mirada al saludo de los estudiantes, y trate de implementar su propio constructor de saludo Student() que herede todas las características de Person(), e implemente las diferentes funciones de saludo greeting().

+ +
+

Nota: Si tiene problemas resolviendo esto, dele una mirada a nuestra versión final (véala tambien funcionando ).

+
+ +

Resúmen de miembros objeto

+ +

To summarize, you've basically got three types of property/method to worry about:

+ +
    +
  1. Those defined inside a constructor function that are given to object instances. These are fairly easy to spot — in your own custom code, they are the members defined inside a constructor using the this.x = x type lines; in built in browser code, they are the members only available to object instances (usually created by calling a constructor using the new keyword, e.g. var myInstance = new myConstructor()).
  2. +
  3. Those defined directly on the constructor themselves, that are available only on the constructor. These are commonly only available on built-in browser objects, and are recognized by being chained directly onto a constructor, not an instance. For example, Object.keys().
  4. +
  5. Those defined on a constructor's prototype, which are inherited by all instances and inheriting object classes. These include any member defined on a Constructor's prototype property, e.g. myConstructor.prototype.x().
  6. +
+ +

If you are not sure which is which, don't worry about it just yet — you are still learning, and familiarity will come with practice.

+ +

ECMAScript 2015 Classes

+ +

ECMAScript 2015 introduces class syntax to JavaScript as a way to write reusable classes using easier, cleaner syntax, which is more similar to classes in C++ or Java. In this section we'll convert the Person and Teacher examples from prototypal inheritance to classes, to show you how it's done.

+ +
+

Note: This modern way of writing classes is supported in all modern browsers, but it is still worth knowing about how the underlying prototypal inheritance in case you work on a project that requires supporting a browser that doesn't support this syntax (most notably Internet Explorer).

+
+ +

Let's look at a rewritten version of the Person example, class-style:

+ +
class Person {
+  constructor(first, last, age, gender, interests) {
+    this.name = {
+      first,
+      last
+    };
+    this.age = age;
+    this.gender = gender;
+    this.interests = interests;
+  }
+
+  greeting() {
+    console.log(`Hi! I'm ${this.name.first}`);
+  };
+
+  farewell() {
+    console.log(`${this.name.first} has left the building. Bye for now!`);
+  };
+}
+
+ +

The class statement indicates that we are creating a new class. Inside this block, we define all the features of the class:

+ + + +

We can now instantiate object instances using the new operator, in just the same way as we did before:

+ +
let han = new Person('Han', 'Solo', 25, 'male', ['Smuggling']);
+han.greeting();
+// Hi! I'm Han
+
+let leia = new Person('Leia', 'Organa', 19, 'female' ['Government']]);
+leia.farewell();
+// Leia has left the building. Bye for now
+
+ +
+

Note: Under the hood, your classes are being converted into prototypal Inheritance models — this is just syntactic sugar. But I'm sure you'll agree that it's easier to write.

+
+ +

Inheritance with class syntax

+ +

Above we created a class to represent a person. They have a series of attributes that are common to all people; in this section we'll create our specialized Teacher class, making it inherit from Person using modern class syntax. This is called creating a subclass or subclassing.

+ +

To create a subclass we use the extends keyword to tell JavaScript the class we want to base our class on.

+ +
class Teacher extends Person {
+  constructor(first, last, age, gender, interests, subject, grade) {
+    this.name = {
+      first,
+      last
+    };
+
+  this.age = age;
+  this.gender = gender;
+  this.interests = interests;
+  // subject and grade are specific to Teacher
+  this.subject = subject;
+  this.grade = grade;
+  }
+}
+ +

We can make the code more readable by defining the super() operator as the first item inside the constructor(). This will call the parent class' constructor, and inherit the members we specify as parameters of super(), as long as they are defined there:

+ +
class Teacher extends Person {
+  constructor(first, last, age, gender, interests, subject, grade) {
+    super(first, last, age, gender, interests);
+
+    // subject and grade are specific to Teacher
+    this.subject = subject;
+    this.grade = grade;
+  }
+}
+
+ +

When we instantiate Teacher object instances, we can now call methods and properties defined on both Teacher and Person, as we'd expect:

+ +
let snape = new Teacher('Severus', 'Snape', 58, 'male', ['Potions'], 'Dark arts', 5);
+snape.greeting(); // Hi! I'm Severus.
+snape.farewell(); // Severus has left the building. Bye for now.
+snape.age // 58
+snape.subject; // Dark arts
+
+ +

Like we did with Teachers, we could create other subclasses of Person to make them more specialized without modifying the base class.

+ +
+

Note: You can find this example on GitHub as es2015-class-inheritance.html (see it live also).

+
+ +

Getters and Setters

+ +

There may be times when we want to change the values of an attribute in the classes we create or we don't know what the final value of an attribute will be. Using the Teacher example, we may not know what subject the teacher will teach before we create them, or their subject may change between terms.

+ +

We can handle such situations with getters and setters.

+ +

Let's enhance the Teacher class with getters and setters. The class starts the same as it was the last time we looked at it.

+ +

Getters and setters work in pairs. A getter returns the current value of the variable and its corresponding setter changes the value of the variable to the one it defines.

+ +

The modified Teacher class looks like this:

+ +
class Teacher extends Person {
+  constructor(first, last, age, gender, interests, subject, grade) {
+    super(first, last, age, gender, interests);
+    // subject and grade are specific to Teacher
+    this._subject = subject;
+    this.grade = grade;
+  }
+
+  get subject() {
+    return this._subject;
+  }
+
+  set subject(newSubject) {
+    this._subject = newSubject;
+  }
+}
+
+ +

In our class above we have a getter and setter for the subject property. We use _  to create a separate value in which to store our name property. Without using this convention, we would get errors every time we called get or set. At this point:

+ + + +

The example below shows the two features in action:

+ +
// Check the default value
+console.log(snape.subject) // Returns "Dark arts"
+
+// Change the value
+snape.subject="Balloon animals" // Sets _subject to "Balloon animals"
+
+// Check it again and see if it matches the new value
+console.log(snape.subject) // Returns "Balloon animals"
+
+ +
+

Note: You can find this example on GitHub as es2015-getters-setters.html (see it live also).

+
+ +

When would you use inheritance in JavaScript?

+ +

Particularly after this last article, you might be thinking "woo, this is complicated". Well, you are right. Prototypes and inheritance represent some of the most complex aspects of JavaScript, but a lot of JavaScript's power and flexibility comes from its object structure and inheritance, and it is worth understanding how it works.

+ +

In a way, you use inheritance all the time. Whenever you use various features of a Web API , or methods/properties defined on a built-in browser object that you call on your strings, arrays, etc., you are implicitly using inheritance.

+ +

In terms of using inheritance in your own code, you probably won't use it often, especially to begin with, and in small projects. It is a waste of time to use objects and inheritance just for the sake of it when you don't need them. But as your code bases get larger, you are more likely to find a need for it. If you find yourself starting to create a number of objects that have similar features, then creating a generic object type to contain all the shared functionality and inheriting those features in more specialized object types can be convenient and useful.

+ +
+

Note: Because of the way JavaScript works, with the prototype chain, etc., the sharing of functionality between objects is often called delegation. Specialized objects delegate functionality to a generic object type.

+
+ +

When using inheritance, you are advised to not have too many levels of inheritance, and to keep careful track of where you define your methods and properties. It is possible to start writing code that temporarily modifies the prototypes of built-in browser objects, but you should not do this unless you have a really good reason. Too much inheritance can lead to endless confusion, and endless pain when you try to debug such code.

+ +

Ultimately, objects are just another form of code reuse, like functions or loops, with their own specific roles and advantages. If you find yourself creating a bunch of related variables and functions and want to track them all together and package them neatly, an object is a good idea. Objects are also very useful when you want to pass a collection of data from one place to another. Both of these things can be achieved without use of constructors or inheritance. If you only need a single instance of an object, then you are probably better off just using an object literal, and you certainly don't need inheritance.

+ +

Alternativas para extender la cadena del prototipos

+ +

En JavaScript, hay varias maneras diferentes de extender el prototipo de un objeto aparte de lo que hemos mostrado anteriormente. Para saber más sobre las otras formas, visite nuestro artículo Herencia y la cadena de prototipos.

+ +

Resumen

+ +

Este artículo ha cubierto el resto de la teoría y sintaxis central de OOJS que creemos que debería conocer ahora. En este punto debe entender los conceptos básicos de objetos JavaScript y POO, prototipos y herencia de prototipos, cómo crear clases (constructores) e instancias de objetos, añadir características a las clases y crear subclases que heredan de otras clases.

+ +

En el siguiente artículo veremos cómo trabajar con JavaScript Object Notation (JSON), un formato común de intercambio de datos escrito con objetos JavaScript.

+ +

Véase también

+ + + +

{{PreviousMenuNext("Learn/JavaScript/Objects/Object_prototypes", "Learn/JavaScript/Objects/JSON", "Learn/JavaScript/Objects")}}

+ +

En éste módulo

+ + diff --git a/files/es/learn/javascript/objects/inheritance/index.html b/files/es/learn/javascript/objects/inheritance/index.html deleted file mode 100644 index e1cbba42da..0000000000 --- a/files/es/learn/javascript/objects/inheritance/index.html +++ /dev/null @@ -1,400 +0,0 @@ ---- -title: Herencia en JavaScript -slug: Learn/JavaScript/Objects/Inheritance -translation_of: Learn/JavaScript/Objects/Inheritance ---- -
{{LearnSidebar}}
- -
{{PreviousMenuNext("Learn/JavaScript/Objects/Object_prototypes", "Learn/JavaScript/Objects/JSON", "Learn/JavaScript/Objects")}}
- -

Con la mayoría de los detalles internos de OOJS (JavaScript Orientado a Objetos) explicados, este artículo muestra cómo crear clases "hijo" (constructores) que heredan características de sus clases "padre". Además, presentamos algunos consejos sobre cuándo y dónde puedes usar OOJS y cómo se crean las clases con la sintaxis moderna de ECMAScript.

- - - - - - - - - - - - -
Pre-requisitos:Conocimientos básicos de informática, una comprensión básica de HTML y CSS, familiaridad con los principios básicos de JavaScript (ver Primeros pasos y Construyendo bloques) y conceptos básicos de OOJS (ver Introduccion a objetos).
Objetivo:Entender cómo es posible implementar la herencia en JavaScript.
- -

Herencia prototípica

- -

Hasta ahora hemos visto algo de herencia en acción — hemos visto cómo funcionan las cadenas de prototipos, y cómo los miembros son heredados subiendo una cadena. Pero principalmente esto ha involucrado funciones integradas del navegador. ¿Cómo creamos un objeto en JavaScript que hereda de otro objeto?

- -

Exploremos cómo hacer esto con un ejemplo concreto.

- -

Primeros pasos

- -

Primero que nada, hazte una copia local de nuestro archivo  oojs-class-inheritance-start.html (míralo corriendo en vivo también). Dentro de aquí encontrarás el mismo ejemplo de constructor de Persona() que hemos estado usando a través del módulo, con una ligera diferencia — hemos definido solo las propiedades dentro del constructor:

- -
function Persona(nombrePila, apellido, edad, genero, intereses) {
-  this.nombre = {
-    nombrePila,
-    apellido
-  };
-  this.edad = edad;
-  this.genero = genero;
-  this.intereses = intereses;
-};
- -

Todos los métodos están definidos en el prototipo del constructor. Por ejemplo:

- -
Persona.prototype.saludo = function() {
-  alert('¡Hola! soy ' + this.nombre.nombrePila + '.');
-};
- -
-

Nota: En el código fuente, también verá los métodos bio() y despedida() definidos. Más tarde verá cómo estos pueden ser heredados por otros constructores.

-
- -

Digamos que quisieramos crear una clase de Profesor, como la que describimos en nuestra definición inicial orientada a objetos, que hereda todos los miembros de Persona, pero también incluye:

- -
    -
  1. Una nueva propiedad, materia — esto contendrá la materia que el profesor enseña.
  2. -
  3. Un método actualizado de saludo(), que suena un poco más formal que el método estándar de saludo() — más adecuado para un profesor que se dirige a algunos estudiantes en la escuela.
  4. -
- -

Definiendo un constructor Profesor()

- -

Lo primero que tenemos que hacer es crear el constructor Profesor()  — añadimos lo siguiente tras el código existente:

- -
function Profesor(nombrePila, apellido, edad, genero, intereses, materia) {
-  Person.call(this, nombrePila, apellido, edad, genero, intereses);
-
-  this.materia = materia;
-}
- -

Esto es similar al constructor de Persona en muchos aspectos, pero hay algo extraño aquí que no hemos visto antes: la función call (). Esta función básicamente le permite llamar a una función definida en otro lugar, pero en el contexto actual.
- El primer parámetro especifica el valor de this que desea utilizar al ejecutar la función, y los otros parámetros son aquellos que deben pasarse a la función cuando se invoca.

- -

Queremos que el constructor Profesor() tome los mismos parámetros que el constructor Persona() del que está heredando, por lo que los especificamos todos como parámetros en la invocación call().

- -

La última línea dentro del constructor simplemente define la nueva propiedad subject que los profesores tendrán y que las personas genéricas no tienen.

- -

Como nota, podríamos haber simplemente hecho esto:

- -
function Profesor(nombrePila, apellido, edad, genero, intereses, materia) {
-  this.nombre = {
-    nombrePila,
-    apellido
-  };
-  this.edad = edad;
-  this.genero = genero;
-  this.intereses = intereses;
-  this.materia = materia;
-}
- -

Pero esto es solo definir las propiedades de nuevo, no heredarlas de Persona(), por lo que anula el punto de lo que estamos tratando de hacer. También lleva más líneas de código.

- -

Heredando de un constructor sin parámetros

- -

Nótese que si el constructor del cual se está heredando no toma los valores de sus propiedades de parámetros, no se necesita especificarlos como argumentos adicionales en call(). Por ejemplo, si se tuviera algo muy simple como esto:

- -
function Brick() {
-  this.width = 10;
-  this.height = 20;
-}
- -

Se podrían heredar las propiedades width y height haciendo esto (así como los otros pasos descritos a continuación, por supuesto):

- -
function BlueGlassBrick() {
-  Brick.call(this);
-
-  this.opacity = 0.5;
-  this.color = 'blue';
-}
- -

Nótese que solo especificamos this dentro de call() — no se requieren otros parámetros ya que no estamos heredando ninguna propiedad del padre que sea establecida por parámetros.

- -

Estableciendo el prototipo de Profesor() y su referencia al constructor

- -

Todo va bien hasta ahora, pero tenemos un problema. Definimos un nuevo constructor, y tiene una propiedad prototype, la cual por defecto solo contiene una referencia a la función constructor en sí misma. No contiene los métodos de la propiedad prototype del constructor Persona. Para ver esto, introduzca Object.getOwnPropertyNames(Profesor.prototype) ya sea en el campo de texto o en la consola de Javascript . Introdúzcalo nuevamente, reemplazando Profesor con Persona. El nuevo constructor tampoco hereda esos métodos. Para ver esto, compare los resultados de Persona.prototype.saludo and Profesor.prototype.saludo. Necesitamos obtener Profesor() para obtener los métodos definidos en el prototipo de Persona(). ¿Cómo lo hacemos?

- -
    -
  1. Añade la siguiente línea debajo de tu adición anterior: -
    Profesor.prototype = Object.create(Persona.prototype);
    - Aquí es cuando nuestro amigo create() sale al rescate de nuevo. En este caso lo estamos usando para crear un nuevo objeto y hacerlo el valor de Profesor.prototype. El nuevo objeto tiene Persona.prototype como su prototipo y por lo tanto heredará, si y cuando lo necesite, todos los métodos disponibles en Persona.prototype.
  2. -
  3. Necesitamos hacer una cosa más antes de proseguir. Después de agregar la última línea, la propiedad constructor de  Profesor.prototype es ahora igual a Persona(), debido a que acabamos de asignar Profesor.prototype para que haga referencia a un objeto que hereda sus propiedades de Persona.prototype! Ahora prueba guardando tu código, carga la página en un explorador e intenta verificar en la consola el valor de  Profesor.prototype.constructor.
  4. -
  5. Esto puede volverse un problema, así que necesitamos corregirlo. Puedes hacerlo regresando a tu código y agregando la siguiente línea al final: -
    Profesor.prototype.constructor = Profesor;
    -
  6. -
  7. Ahora, si guardas y actualizas, el valor de Profesor.prototype.constructor debe regresar Profesor(), como se espera, además de que ahora estamos heredando de Persona()!
  8. -
- -

Dándole a Profesor() un nuevo método saludo()

- -

Para finalizar nuestro código, necesitamos definir un nuevo método saludo() en el constructor de Profesor().

- -

La manera más fácil es definirlo en el prototipo de Profesor() — agrega lo siguiente al final de tu código:

- -
Profesor.prototype.saludo = function() {
-  var prefijo;
-
-  if (this.genero === 'masculino' || this.genero === 'Masculino' || this.genero === 'm' || this.genero === 'M') {
-    prefijo = 'Sr.';
-  } else if (this.genero === 'female' || this.genero === 'Femenino' || this.genero === 'f' || this.genero === 'F') {
-    prefijo = 'Sra.';
-  } else {
-    prefijo = 'Sx.';
-  }
-
-  alert('Hola. Mi nombre es ' + prefijo + ' ' + this.nombre.apellido + ', y enseño ' + this.materia + '.');
-};
- -

Esto muestra el saludo del profesor, el cual además utiliza un prefijo apropiado para su género, resuelto utilizando un bloque else-if.

- -

Probando el ejemplo

- -

Ahora que ha ingresado todo el código, intente creando una instancia de objeto desde Profesor() poniendo lo que sigue al final de su archivo (o algo similar a su elección):

- -
var teacher1 = new Teacher('Dave', 'Griffiths', 31, 'male', ['football', 'cookery'], 'mathematics');
- -

Ahora guarde y actualice, e intente accediendo a las propiedades y metodos de su nuevo teacher1 objecto, por ejemplo:

- -
teacher1.name.first;
-teacher1.interests[0];
-teacher1.bio();
-teacher1.subject;
-teacher1.greeting();
-teacher1.farewell();
- -

Esto deberia trabajar bien. Las consultas de las líneas 1, 2, 3, y 6 acceden a miembros heredados del genérico Person() constructor (clase). La consulta de la línea 4 accede un miembro que es disponible solamente en el mas especializado Teacher() constructor (clase). La consulta de la línea 5 accedería a un miembro desde Person(), excepto por el hecho que Teacher() tiene sus propios miembros con el mismo nombre, entonces la consulta accede a ese miembro.

- -
-

Nota: Si tiene problemas con el funcionamiento, compare su código con nuestra versión final (vea corriendo en vivo también).

-
- -

La técnica que mostramos aquí no es la única para crear herencia de clases en JavaScript, pero funciona OK, y le dá una buena idea acerca de cómo implementar herencia en JavaScript.

- -

También estará interesado en verificar algo de las nuevas características de {{glossary("ECMAScript")}} que nos permiten hacer herencia mas claramente en JavaScript (véase Classes). No se cubrió todo aquí, como tampoco es soportado aún por todos los navegadores. Todo el otro código de constructores que se discutió aquí en estos artículos son soportados por IE9 o superior, y hay caminos para lograr superiores soportes que estos.

- -

Un simple camino es usar una librería de JavaScript — la mayoría de las opciones mas populares tienen un facil ajuste de funcionalidad disponible para hacer herencia mas facil y rápido. CoffeeScript por ejemplo provee class, extends, etc.

- -

Un ejercicio mas allá

- -

En nuestra Sección teórica de POO, también incluimos una clase Student como un concepto, el cual hereda todas las características de Person, y también tiene un método diferende de greeting() que Person que es mas informal que el saludo de los profesores Teacher. Dele una mirada al saludo de los estudiantes, y trate de implementar su propio constructor de saludo Student() que herede todas las características de Person(), e implemente las diferentes funciones de saludo greeting().

- -
-

Nota: Si tiene problemas resolviendo esto, dele una mirada a nuestra versión final (véala tambien funcionando ).

-
- -

Resúmen de miembros objeto

- -

To summarize, you've basically got three types of property/method to worry about:

- -
    -
  1. Those defined inside a constructor function that are given to object instances. These are fairly easy to spot — in your own custom code, they are the members defined inside a constructor using the this.x = x type lines; in built in browser code, they are the members only available to object instances (usually created by calling a constructor using the new keyword, e.g. var myInstance = new myConstructor()).
  2. -
  3. Those defined directly on the constructor themselves, that are available only on the constructor. These are commonly only available on built-in browser objects, and are recognized by being chained directly onto a constructor, not an instance. For example, Object.keys().
  4. -
  5. Those defined on a constructor's prototype, which are inherited by all instances and inheriting object classes. These include any member defined on a Constructor's prototype property, e.g. myConstructor.prototype.x().
  6. -
- -

If you are not sure which is which, don't worry about it just yet — you are still learning, and familiarity will come with practice.

- -

ECMAScript 2015 Classes

- -

ECMAScript 2015 introduces class syntax to JavaScript as a way to write reusable classes using easier, cleaner syntax, which is more similar to classes in C++ or Java. In this section we'll convert the Person and Teacher examples from prototypal inheritance to classes, to show you how it's done.

- -
-

Note: This modern way of writing classes is supported in all modern browsers, but it is still worth knowing about how the underlying prototypal inheritance in case you work on a project that requires supporting a browser that doesn't support this syntax (most notably Internet Explorer).

-
- -

Let's look at a rewritten version of the Person example, class-style:

- -
class Person {
-  constructor(first, last, age, gender, interests) {
-    this.name = {
-      first,
-      last
-    };
-    this.age = age;
-    this.gender = gender;
-    this.interests = interests;
-  }
-
-  greeting() {
-    console.log(`Hi! I'm ${this.name.first}`);
-  };
-
-  farewell() {
-    console.log(`${this.name.first} has left the building. Bye for now!`);
-  };
-}
-
- -

The class statement indicates that we are creating a new class. Inside this block, we define all the features of the class:

- - - -

We can now instantiate object instances using the new operator, in just the same way as we did before:

- -
let han = new Person('Han', 'Solo', 25, 'male', ['Smuggling']);
-han.greeting();
-// Hi! I'm Han
-
-let leia = new Person('Leia', 'Organa', 19, 'female' ['Government']]);
-leia.farewell();
-// Leia has left the building. Bye for now
-
- -
-

Note: Under the hood, your classes are being converted into prototypal Inheritance models — this is just syntactic sugar. But I'm sure you'll agree that it's easier to write.

-
- -

Inheritance with class syntax

- -

Above we created a class to represent a person. They have a series of attributes that are common to all people; in this section we'll create our specialized Teacher class, making it inherit from Person using modern class syntax. This is called creating a subclass or subclassing.

- -

To create a subclass we use the extends keyword to tell JavaScript the class we want to base our class on.

- -
class Teacher extends Person {
-  constructor(first, last, age, gender, interests, subject, grade) {
-    this.name = {
-      first,
-      last
-    };
-
-  this.age = age;
-  this.gender = gender;
-  this.interests = interests;
-  // subject and grade are specific to Teacher
-  this.subject = subject;
-  this.grade = grade;
-  }
-}
- -

We can make the code more readable by defining the super() operator as the first item inside the constructor(). This will call the parent class' constructor, and inherit the members we specify as parameters of super(), as long as they are defined there:

- -
class Teacher extends Person {
-  constructor(first, last, age, gender, interests, subject, grade) {
-    super(first, last, age, gender, interests);
-
-    // subject and grade are specific to Teacher
-    this.subject = subject;
-    this.grade = grade;
-  }
-}
-
- -

When we instantiate Teacher object instances, we can now call methods and properties defined on both Teacher and Person, as we'd expect:

- -
let snape = new Teacher('Severus', 'Snape', 58, 'male', ['Potions'], 'Dark arts', 5);
-snape.greeting(); // Hi! I'm Severus.
-snape.farewell(); // Severus has left the building. Bye for now.
-snape.age // 58
-snape.subject; // Dark arts
-
- -

Like we did with Teachers, we could create other subclasses of Person to make them more specialized without modifying the base class.

- -
-

Note: You can find this example on GitHub as es2015-class-inheritance.html (see it live also).

-
- -

Getters and Setters

- -

There may be times when we want to change the values of an attribute in the classes we create or we don't know what the final value of an attribute will be. Using the Teacher example, we may not know what subject the teacher will teach before we create them, or their subject may change between terms.

- -

We can handle such situations with getters and setters.

- -

Let's enhance the Teacher class with getters and setters. The class starts the same as it was the last time we looked at it.

- -

Getters and setters work in pairs. A getter returns the current value of the variable and its corresponding setter changes the value of the variable to the one it defines.

- -

The modified Teacher class looks like this:

- -
class Teacher extends Person {
-  constructor(first, last, age, gender, interests, subject, grade) {
-    super(first, last, age, gender, interests);
-    // subject and grade are specific to Teacher
-    this._subject = subject;
-    this.grade = grade;
-  }
-
-  get subject() {
-    return this._subject;
-  }
-
-  set subject(newSubject) {
-    this._subject = newSubject;
-  }
-}
-
- -

In our class above we have a getter and setter for the subject property. We use _  to create a separate value in which to store our name property. Without using this convention, we would get errors every time we called get or set. At this point:

- - - -

The example below shows the two features in action:

- -
// Check the default value
-console.log(snape.subject) // Returns "Dark arts"
-
-// Change the value
-snape.subject="Balloon animals" // Sets _subject to "Balloon animals"
-
-// Check it again and see if it matches the new value
-console.log(snape.subject) // Returns "Balloon animals"
-
- -
-

Note: You can find this example on GitHub as es2015-getters-setters.html (see it live also).

-
- -

When would you use inheritance in JavaScript?

- -

Particularly after this last article, you might be thinking "woo, this is complicated". Well, you are right. Prototypes and inheritance represent some of the most complex aspects of JavaScript, but a lot of JavaScript's power and flexibility comes from its object structure and inheritance, and it is worth understanding how it works.

- -

In a way, you use inheritance all the time. Whenever you use various features of a Web API , or methods/properties defined on a built-in browser object that you call on your strings, arrays, etc., you are implicitly using inheritance.

- -

In terms of using inheritance in your own code, you probably won't use it often, especially to begin with, and in small projects. It is a waste of time to use objects and inheritance just for the sake of it when you don't need them. But as your code bases get larger, you are more likely to find a need for it. If you find yourself starting to create a number of objects that have similar features, then creating a generic object type to contain all the shared functionality and inheriting those features in more specialized object types can be convenient and useful.

- -
-

Note: Because of the way JavaScript works, with the prototype chain, etc., the sharing of functionality between objects is often called delegation. Specialized objects delegate functionality to a generic object type.

-
- -

When using inheritance, you are advised to not have too many levels of inheritance, and to keep careful track of where you define your methods and properties. It is possible to start writing code that temporarily modifies the prototypes of built-in browser objects, but you should not do this unless you have a really good reason. Too much inheritance can lead to endless confusion, and endless pain when you try to debug such code.

- -

Ultimately, objects are just another form of code reuse, like functions or loops, with their own specific roles and advantages. If you find yourself creating a bunch of related variables and functions and want to track them all together and package them neatly, an object is a good idea. Objects are also very useful when you want to pass a collection of data from one place to another. Both of these things can be achieved without use of constructors or inheritance. If you only need a single instance of an object, then you are probably better off just using an object literal, and you certainly don't need inheritance.

- -

Alternativas para extender la cadena del prototipos

- -

En JavaScript, hay varias maneras diferentes de extender el prototipo de un objeto aparte de lo que hemos mostrado anteriormente. Para saber más sobre las otras formas, visite nuestro artículo Herencia y la cadena de prototipos.

- -

Resumen

- -

Este artículo ha cubierto el resto de la teoría y sintaxis central de OOJS que creemos que debería conocer ahora. En este punto debe entender los conceptos básicos de objetos JavaScript y POO, prototipos y herencia de prototipos, cómo crear clases (constructores) e instancias de objetos, añadir características a las clases y crear subclases que heredan de otras clases.

- -

En el siguiente artículo veremos cómo trabajar con JavaScript Object Notation (JSON), un formato común de intercambio de datos escrito con objetos JavaScript.

- -

Véase también

- - - -

{{PreviousMenuNext("Learn/JavaScript/Objects/Object_prototypes", "Learn/JavaScript/Objects/JSON", "Learn/JavaScript/Objects")}}

- -

En éste módulo

- - diff --git a/files/es/learn/javascript/objects/object-oriented_js/index.html b/files/es/learn/javascript/objects/object-oriented_js/index.html deleted file mode 100644 index 5eb023c685..0000000000 --- a/files/es/learn/javascript/objects/object-oriented_js/index.html +++ /dev/null @@ -1,307 +0,0 @@ ---- -title: JavaScript orientado a objetos para principiantes -slug: Learn/JavaScript/Objects/Object-oriented_JS -tags: - - Aprender - - Artículo - - Constructor - - Crear - - Create - - JSOO - - JavaScript - - OOJS - - OOP - - Object - - Objeto - - Orientado a Objeto - - Principiante - - Programación orientada a objetos - - instance - - instanciar - - 'l10n:priority' -translation_of: Learn/JavaScript/Objects/Object-oriented_JS ---- -
{{LearnSidebar}}
- -
{{PreviousMenuNext("Learn/JavaScript/Objects/Basics", "Learn/JavaScript/Objects/Object_prototypes", "Learn/JavaScript/Objects")}}
- -

Con lo básico fuera del camino, nos enfocaremos en Javascript Orientado a Objetos (JSOO) — este artículo presenta una descripción básica de la teoría de la Programación Orientada a Objetos (POO), luego explora cómo Javascript emula classes de objetos via funciones constructoras, y cómo crea instancias de objetos.

- - - - - - - - - - - - -
Prerequisitos:Conocimientos básicos de computación, entendimiento básico de HTML y CSS, familiaridad con las bases de Javascript (ver Primeros pasos con JavaScript y Bloques de construcción JavaScript) y las bases de JSOO (ver Introducción a objetos).
Objetivo: -

Entender la teoría base de la programación orientada a objetos, como se relaciona esta con JavaScript ("todo es un objeto"), y como crear constructores e instacias de objetos.

-
- -

Programacion Orientada a Objetos— lo básico

- -

Para empezar, daremos una descripción simple y de alto nivel acerca de lo que es la Programación Orientada a Objetos (POO). Decimos simple, porque la POO puede volverse complicada rápidamente, y darte un tratamiento completo ahora, probablemente podría confundirte más que ayudar. La idea básica de la POO es que usamos objetos para modelar cosas del mundo real que queremos representar en nuestros programas, y/o proveemos una simple manera para acceder a la funcionalidad que, de otra manera, sería difícil o imposible de usar.

- -

Los objetos pueden contener información y código relacionados, los cuales representan información acerca de lo que estás tratando de modelar, y la funcionalidad o comportamiento que deseas que tenga.  Los datos de un Objeto (y frecuentemente, también las funciones) se pueden almacenar ordenadamente (la palabra oficial es encapsular) dentro del paquete de un objeto (al que se puede asignar un nombre específico, llamado a veces espacio de nombres), haciéndolo fácil de estructurar y acceder; los objetos también se usan comúnmente como almacenes de datos que se pueden enviar fácilmente a través de la red.

- -

Definiendo una plantilla de objeto

- -

Vamos a considerar un sencillo programa que muestra información sobre estudiantes y profesores en una escuela. Aquí daremos un vistazo a la POO (Programación Orientada a Objetos) en general, no en el contexto de algún lenguaje de programación específico.

- -

Para empezar, podríamos volver a ver al objeto Persona de nuestro artículo de primeros objetos, que define los datos generales y funcionalidades de una persona. Hay muchas cosas que podrías saber acerca de una persona (su dirección, estatura, tamaño de calzado, perfil de ADN, número de pasaporte, rasgos significativos de su personalidad...), pero, en este caso, solo estamos interesados en mostrar su nombre, edad, género e intereses, además de una pequeña introducción sobre este individuo basada en los datos anteriores. También queremos que sea capaz de saludar. 

- -

Esto es conocido como abstracción —  crear un modelo simple de algo complejo que represente sus aspectos más importantes y que sea fácil de manipular para el propósito de nuestro programa.

- -

- -

En algunos lenguajes de POO, esta definición de tipo de objeto se la llama class (JavaScript utiliza diferentes mecanismos y terminologías, como verás a continuación) — esto no es en realidad un objeto, en vez de esto es un modelo que define las características que un objeto debería tener.

- -

Creando objetos

- -

Partiendo de nuestra clase, podemos crear instancias de objetos — objetos que contienen los datos y funcionalidades definidas en la clase original. Teniendo a nuestra clase Persona, ahora podemos crear gente con características más específicas: 

- -

- -

Cuando una instancia del objeto es creada a partir de una clase, se ejecuta la función constructora (constructor en inglés) de la clase para crearla. El proceso de crear una instancia del objeto desde una clase se llama instanciación.

- -

Clases especializadas

- -

En este caso nosotros no queremos personas genericas — queremos docentes y estudiantes, que son los dos tipos más específicos de personas. En POO, podemos crear nuevas clases basadas en otras clases, estas nuevas clases secundarias se pueden hacer para  heredar los datos y código de su clase primaria, de modo que pueden reutilizar la funcionalidad común a todos los tipos de objetos en lugar de tener que duplicarla. Cuando la funcionalidad difiere entre clases, puedes definir funciones especializadas directamente en ellas según sea necesario.

- -

- -

Esto es realmente útil, los profesores y los estudiantes comparten muchas características comunes como el nombre, el género y la edad, por lo que es conveniente tener que definir esas características solo una vez. También puedes definir la misma característica por separado en diferentes clases, ya que cada definición de esa característica estará en un espacio de nombres diferente. Por ejemplo, el saludo de un estudiante puede tener la forma "Yo, soy [Nombre]" (por ejemplo, Yo, soy Sam), mientras que un profesor puede usar algo más formal, como "Hola, mi nombre es [Prefix] [lastName], y enseño [Asunto] ". (Por ejemplo, Hola, mi nombre es Sr. Griffiths, y yo enseño Química).

- -
-

Nota:  la palabra elegante para la capacidad de múltiples tipos de objetos de implementar la misma funcionalidad es polimorfismo. Por si acaso te preguntabas.

-
- -

Ahora puedes crear instancias de objetos de las clases "hijo". Por ejemplo:

- -

- -

En el resto del articulo, comenzaremos a ver como podemos practicar la teoría de POO en JavaScript.

- -

Constructores e instancias de objetos

- -

Algunas personas sostienen que JavaScript no es un verdadero lenguaje orientado a objetos — por ejemplo, su enunciado class es sólo azúcar sintáctica sobre la herencia prototípica existente y no es una class en el sentido tradicional. JavaScript, utiliza funciones especiales llamadas funciones constructoras para definir objetos y sus características. Son útiles porque a menudo te encontrarás con situaciones en las que no sabes cuántos objetos crearás; los constructores proporcionan los medios para crear tantos objetos como necesites de una manera efectiva, adjuntando datos y funciones a ellos según sea necesario.

- -

Cuando se crea una nueva instancia del objeto a partir de una función constructora, su funcionalidad central (tal como se define en su prototipo, que exploraremos en el artículo Prototipos de objetos) no se copia en el nuevo objeto como lenguajes OO "clásicos", sino que la funcionalidad está vinculada a través de una cadena de referencia llamada cadena prototipo. Así que esto no es una verdadera instanciación, estrictamente hablando, JavaScript usa un mecanismo diferente para compartir funcionalidad entre objetos.

- -
-

Nota: no ser "POO clásica" no es necesariamente algo malo; Como se mencionó anteriormente, la POO puede ser muy compleja muy rápidamente, y JavaScript tiene algunas agradables formas de aprovechar las características de la OO sin tener que profundizar demasiado en ello.

-
- -

Exploremos la creación de clases a través de constructores y la creación de instancias de objetos a partir de ellas en JavaScript. En primer lugar, nos gustaría que hicieras una nueva copia local del archivo oojs.html que vimos en nuestro primer artículo de Objetos.

- -

Un ejemplo simple

- -
    -
  1. Comencemos por ver cómo puedes definir una persona con una funcion normal. Agrega esta funcion dentro del elemento script: - -
    function createNewPerson(name) {
    -  var obj = {};
    -  obj.name = name;
    -  obj.greeting = function() {
    -    alert('Hi! I\'m ' + this.name + '.');
    -  };
    -  return obj;
    -}
    -
  2. -
  3. Ahora puedes crear una nueva persona llamando a esta funcion — prueba con las siguientes lineas en la consola Javascript de tu navegador: -
    var salva = createNewPerson('Salva');
    -salva.name;
    -salva.greeting();
    - Esto funciona bastante bien, pero es un poco largo; si sabemos que queremos crear un objeto, ¿por qué necesitamos crear explícitamente un nuevo objeto vacío y devolverlo? Afortunadamente, JavaScript nos proporciona un práctico acceso directo, en forma de funciones constructoras — ¡hagamos una ahora!
  4. -
  5. Reemplaza tu función anterior por la siguiente: -
    function Person(name) {
    -  this.name = name;
    -  this.greeting = function() {
    -    alert('Hi! I\'m ' + this.name + '.');
    -  };
    -}
    -
  6. -
- -

La función constructora es la versión de JavaScript de una clase. Notarás que tiene todas las características que esperas en una función, aunque no devuelve nada o crea explícitamente un objeto — básicamente sólo define propiedades y métodos. Verás que la palabra clave this se está usando aquí también — es básicamente decir que cuando se crea una de estas instancias de objeto, la propiedad name del objeto será igual al valor del nombre pasado a la llamada del constructor, y el método greeting() usará también el valor del nombre pasado a la llamada del constructor.

- -
-

Nota: Un nombre de función constructora generalmente comienza con una letra mayúscula — esta convención se utiliza para hacer que las funciones constructoras sean más fáciles de reconocer en el código.

-
- -

Entonces, ¿cómo llamamos a un constructor para crear algunos objetos?

- -
    -
  1. Agrega las siguientes líneas debajo de tu código anterior: -
    var person1 = new Person('Bob');
    -var person2 = new Person('Sarah');
    -
  2. -
  3. Guarda el código y vuelve a cargarlo en el navegador, e intenta ingresar las siguientes líneas en la consola Javascript : -
    person1.name
    -person1.greeting()
    -person2.name
    -person2.greeting()
    -
  4. -
- -

¡Guaw! Ahora veras que tenemos dos nuevos objetos, cada uno de los cuales está almacenado en un espacio de nombres diferente: para acceder a sus propiedades y métodos, debes llamarlos como person1 o  person2; están cuidadosamente empaquetados para que no entren en conflicto con otras funciones. Sin embargo, tienen disponible la misma propiedad name y el método greeting(). Ten en cuenta que están utilizando su propio name que se les asignó cuando se crearon; esta es una razón por la cual es muy importante usar this, para que usen sus propios valores, y no algún otro valor.

- -

Veamos nuevamente las llamadas del constructor:

- -
var person1 = new Person('Bob');
-var person2 = new Person('Sarah');
- -

En cada caso, la  palabra clave new se usa para indicarle al navegador que queremos crear una nueva instancia del objeto, seguida del nombre de la función con sus parámetros requeridos entre paréntesis, y el resultado se almacena en una variable — muy similar a cómo se llama a una función estándar. Cada instancia se crea de acuerdo con esta definición:

- -
function Person(name) {
-  this.name = name;
-  this.greeting = function() {
-    alert('Hi! I\'m ' + this.name + '.');
-  };
-}
- -

Una vez creados los nuevos objetos, las variables person1 y person2 contienen los siguientes objetos:

- -
{
-  name: 'Bob',
-  greeting: function() {
-    alert('Hi! I\'m ' + this.name + '.');
-  }
-}
-
-{
-  name: 'Sarah',
-  greeting: function() {
-    alert('Hi! I\'m ' + this.name + '.');
-  }
-}
- -

Ten en cuenta que cuando llamamos a nuestra función constructora, estamos definiendo greeting() cada vez, lo cual no es lo ideal. Para evitar esto, podemos definir funciones en el prototipo, que veremos más adelante.

- -

Creando nuestro constructor final

- -

El ejercicio que vimos anteriormente fue solo un ejemplo simple para comenzar. Ahora crearemos nuestra función constructor Person() final.

- -
    -
  1. Elimina el código que insertaste hasta ahora y agrega este constructor de reemplazo; este es exactamente el mismo que el ejemplo simple del principio, con un poco más de complejidad: -
    function Person(first, last, age, gender, interests) {
    -  this.name = {
    -    'first': first,
    -    'last' : last
    -  };
    -  this.age = age;
    -  this.gender = gender;
    -  this.interests = interests;
    -  this.bio = function() {
    -    alert(this.name.first + ' ' + this.name.last + ' is ' + this.age + ' years old. He likes ' + this.interests[0] + ' and ' + this.interests[1] + '.');
    -  };
    -  this.greeting = function() {
    -    alert('Hi! I\'m ' + this.name.first + '.');
    -  };
    -}
    -
  2. -
  3. Ahora, agrega la siguiente línea para crear una instancia del objeto: -
    var person1 = new Person('Bob', 'Smith', 32, 'male', ['music', 'skiing']);
    -
  4. -
- -

Ahora verás que puedes acceder a las propiedades y métodos justo como lo hiciste anteriormente — intenta esto en tu consola JS:

- -
person1['age']
-person1.interests[1]
-person1.bio()
-// etc.
- -
-

Nota: Si tienes problemas para lograr que funcione, puedes comparar tu código con nuestra versión — ve oojs-class-finished.html (también lo puedes ver corriendo en vivo).

-
- -

Ejercicios adicionales

- -

Para empezar, intenta añadir un par de líneas de creación de objetos propias, y trata de obtener y asignar valores a los miembros de las instancias del objeto.

- -

Además, hay un par de problemas con nuestro método bio() — la salida siempre incluye el pronombre "He", incluso para personas de otros géneros. Y bio solamente incluye dos intereses, sin importar la cantidad que hay en el arreglo interests. ¿Podrías corregir esto en la definición de la clase (constructor)? Puedes poner cualquier código dentro de un constructor (probablemente necesites algunos condicionales y un bucle). Piensa como se deben estructurar las declaraciones dependiendo del género, y de la cantidad de intereses.

- -
-

Note: Si estás atascado, hay una respuesta en nuestro repositorio de GitHub (see it live) — igualmente ¡intentea resolverla primero!

-
- -

Otras formas de crear instancias de objetos

- -

Hasta ahora hemos visto dos diferentes formas de crear una instancia de objeto — declarando un objeto literal, y usando una función constructora (ver arriba).

- -

Esto tiene sentido, pero hay otras formas — se muestran aquí para que te vayas familiarizando en caso de encontrarte con ellas.

- -

El constructor Object()

- -

Antes que nada, puedes usar el constructor Object() para crear un nuevo objeto. Si, incluso objetos genéricos tienen un constructor que genera un objeto vacío.

- -
    -
  1. Intenta ingresar este código en la consola JavaScript de tu navegador: -
    var person1 = new Object();
    -
  2. -
  3. Esto guarda un objeto vacío en la variable person1. Luego pueded agregar propiedades y métodos a este objeto usando la notación de punto (.) o de corchetes (['']); prueba estos ejemplos en tu consola: -
    person1.name = 'Chris';
    -person1['age'] = 38;
    -person1.greeting = function() {
    -  alert('Hi! I\'m ' + this.name + '.');
    -};
    -
  4. -
  5. También puedes pasar un objeto literal como parámetro al constructor Object(), para precargarlo con propiedades/métodos. Prueba esto en tu consola: -
    var person1 = new Object({
    -  name: 'Chris',
    -  age: 38,
    -  greeting: function() {
    -    alert('Hi! I\'m ' + this.name + '.');
    -  }
    -});
    -
  6. -
- -

Usando el método create()

- -

Los constructores te pueden ayudar a ordenar tu código — puedes crear constructores en un lugar, y luego crear instancias cuando sean necesarias.

- -

Sin embargo, algunas personas prefieren crear instancias de objetos sin crear antes constructores, especialmente si van a crear solamente pocas instancias de un objeto.

- -

JavaScript tiene un método llamado create() que permite hacer esto. Con este método puedes crear un nuevo objeto basado en cualquier otro objeto existente.

- -
    -
  1. Con tu ejercicio de la sección anterior cargado en el navegador, prueba esto en tu consola JavaScript -
    var person2 = Object.create(person1);
    -
  2. -
  3. Y ahora prueba esto: -
    person2.name
    -person2.greeting()
    -
  4. -
- -

Verás que person2 fue creado basado en person1 — tiene las mismas propiedades y métodos.

- -

Una limitación del método create() es que no está soportado por el navegador IE8. Por lo que los constructores serán más efectivos sin necesitas soportar navegadores antiguos.

- -

Más tarde, exploraremos en detalle los efectos de create().

- -

Resumen

- -

Este artículo provee una visión simplificada de la teoría de la orientación a objetos — esta no es toda la historia, pero te da una idea de con que estamos lidiando aquí. Adicionalmente, empezamos a ver como JavaScript está relacionado y difiere de la orientación a objetos "clásica", cómo usamos funciones constructoras para implementar clases en JavaScript, y diferentes formas de generar instancias de objetos.

- -

En el próximo artículo, exploraremos los prototipos de objeto JavaScript.

- -

{{PreviousMenuNext("Learn/JavaScript/Objects/Basics", "Learn/JavaScript/Objects/Object_prototypes", "Learn/JavaScript/Objects")}}

- -

En este modulo

- - diff --git a/files/fr/_redirects.txt b/files/fr/_redirects.txt index 4f52dc802f..fd52f541c3 100644 --- a/files/fr/_redirects.txt +++ b/files/fr/_redirects.txt @@ -3001,8 +3001,10 @@ /fr/docs/Learn/JavaScript/First_steps/methode_chaine_utile /fr/docs/Learn/JavaScript/First_steps/Useful_string_methods /fr/docs/Learn/JavaScript/First_steps/tableaux /fr/docs/Learn/JavaScript/First_steps/Arrays /fr/docs/Learn/JavaScript/Objects/Ajouter_des_fonctionnalités_à_notre_démo_de_balles_rebondissantes /fr/docs/Learn/JavaScript/Objects/Adding_bouncing_balls_features -/fr/docs/Learn/JavaScript/Objects/Heritage /fr/docs/Learn/JavaScript/Objects/Inheritance -/fr/docs/Learn/JavaScript/Objects/JS_orienté-objet /fr/docs/Learn/JavaScript/Objects/Object-oriented_JS +/fr/docs/Learn/JavaScript/Objects/Heritage /fr/docs/Learn/JavaScript/Objects/Classes_in_JavaScript +/fr/docs/Learn/JavaScript/Objects/Inheritance /fr/docs/Learn/JavaScript/Objects/Classes_in_JavaScript +/fr/docs/Learn/JavaScript/Objects/JS_orienté-objet /fr/docs/conflicting/Learn/JavaScript/Objects/Classes_in_JavaScript +/fr/docs/Learn/JavaScript/Objects/Object-oriented_JS /fr/docs/conflicting/Learn/JavaScript/Objects/Classes_in_JavaScript /fr/docs/Learn/JavaScript/Objects/Prototypes_Objet /fr/docs/Learn/JavaScript/Objects/Object_prototypes /fr/docs/Learn/JavaScript/Objects/la_construction_d_objet_en_pratique /fr/docs/Learn/JavaScript/Objects/Object_building_practice /fr/docs/Learn/Performance/Populating_the_page:_how_browsers_work /fr/docs/Web/Performance/How_browsers_work diff --git a/files/fr/_wikihistory.json b/files/fr/_wikihistory.json index 14fc7b1eff..31db3f6c96 100644 --- a/files/fr/_wikihistory.json +++ b/files/fr/_wikihistory.json @@ -5125,7 +5125,7 @@ "mouffy" ] }, - "Learn/JavaScript/Objects/Inheritance": { + "Learn/JavaScript/Objects/Classes_in_JavaScript": { "modified": "2020-07-16T22:32:13.707Z", "contributors": [ "franssu", @@ -5151,22 +5151,6 @@ "darthyoh" ] }, - "Learn/JavaScript/Objects/Object-oriented_JS": { - "modified": "2020-07-16T22:32:05.419Z", - "contributors": [ - "grandoc", - "FloppyJunior", - "vacarme", - "zoora", - "Jetinho", - "elWombator", - "manuwhat", - "antoninha", - "LDCL", - "Alpha", - "SphinxKnight" - ] - }, "Learn/JavaScript/Objects/Object_building_practice": { "modified": "2020-07-16T22:32:31.265Z", "contributors": [ @@ -42753,6 +42737,22 @@ "Goofy" ] }, + "conflicting/Learn/JavaScript/Objects/Classes_in_JavaScript": { + "modified": "2020-07-16T22:32:05.419Z", + "contributors": [ + "grandoc", + "FloppyJunior", + "vacarme", + "zoora", + "Jetinho", + "elWombator", + "manuwhat", + "antoninha", + "LDCL", + "Alpha", + "SphinxKnight" + ] + }, "conflicting/Learn_370bfb0fa23e42f4384f010341852c43": { "modified": "2020-07-16T22:33:41.155Z", "contributors": [ diff --git a/files/fr/conflicting/learn/javascript/objects/classes_in_javascript/index.md b/files/fr/conflicting/learn/javascript/objects/classes_in_javascript/index.md new file mode 100644 index 0000000000..61923716ba --- /dev/null +++ b/files/fr/conflicting/learn/javascript/objects/classes_in_javascript/index.md @@ -0,0 +1,309 @@ +--- +title: Le JavaScript orienté objet pour débutants +slug: conflicting/Learn/JavaScript/Objects/Classes_in_JavaScript +tags: + - Apprendre + - Débutant + - Guide + - JavaScript + - OOJS + - OOP + - POO +translation_of: Learn/JavaScript/Objects/Object-oriented_JS +original_slug: Learn/JavaScript/Objects/Object-oriented_JS +--- +{{LearnSidebar}}{{PreviousMenuNext("Learn/JavaScript/Objects/Basics", "Learn/JavaScript/Objects/Object_prototypes", "Learn/JavaScript/Objects")}} + +Après avoir parcouru les fondamentaux, nous allons aborder en détail le JavaScript orienté objet (JSOO). Cet article présente une approche simple de la programmation orientée objet (POO) et détaille comment JavaScript émule des classes objet au travers des méthodes constructeur et comment instancier ces objets. + + + + + + + + + + + + +
Pré-requis : + Connaissances de base en informatique et compréhension des notions HTML + et CSS, notions de JavaScript (voir + Premiers pas et + Blocs de construction) +
Objectif : + Comprendre les concepts de base derrière la programmation orientée objet + et comment ils s'appliquent à JavaScript ( « tout est objet » ) et + comment créer des constructeurs et instancier des objets. +
+ +## La programmation orientée objet de loin + +Pour commencer, donnons une vue simplifiée et de haut niveau de ce qu'est la programmation orientée objet (POO). On parle d'une vision simplifiée étant donnée que la POO peut devenir très vite complexe et qu'être exhaustif rendrait probablement la découverte plus confuse et difficile qu'autre chose. L'idée de base de la POO consiste à utiliser des objets pour modéliser les objets du monde réel que l'on souhaite représenter dans nos programmes et/ou de fournir un moyen simple d'accéder à une fonctionnalité qu'il serait difficile d'utiliser autrement. + +Les objets peuvent contenir des données et du code représentant de l'information au sujet de la chose que l'on essaie de modéliser ainsi que des fonctionnalités ou un comportement que l'on souhaite lui appliquer. Les données (et bien souvent les fonctions) associées à un objet peuvent être stockées (le terme officiel est **encapsulé**) à l'intérieur d'un paquet objet. Il est possible de donner un nom spécifique à un paquet objet afin  d'y faire référence, on parle alors d'**espace de noms** ou _namespace_, il sera ainsi plus facile de le manipuler et d'y accéder. Les objets peuvent aussi servir pour stocker des données et les transférer facilement sur un réseau. + +### Définissons un modèle objet + +Nous allons voir un programme simple qui affiche des informations à propos des élèves et des professeurs d'une école. Nous allons aborder la théorie de la programmation orientée objet de manière générale sans l'appliquer à un langage particulier. + +Pour débuter, nous pouvons réutiliser l'objet Personne que nous avons créé dans notre [premier article](/fr/docs/Learn/JavaScript/Objects/Basics), il définit un ensemble de données et actions d'une personne. Il existe tout un tas de choses que nous pourrions savoir au sujet d'une personne (son adresse, sa taille, sa pointure, son ADN, son numéro de passeport, ses traits particuliers significatifs… ). En l'occurrence, nous souhaitons uniquement afficher son nom, son âge, ses passions, écrire une petite introduction à son sujet en utilisant ces données et lui apprendre à se présenter. On parle alors d'**abstraction** : créer un modèle simplifié de quelque chose de complexe mais qui ne contient que les aspects qui nous intéressent. Il sera alors plus simple de manipuler ce modèle objet simplifié dans le cadre de notre programme. + +![Classe Personne avec attributs élémentaires](ClassePersonne.png) + +Dans plusieurs langages de POO, la définition d'un objet est appelé une **classe** (comme on le verra ci-après, JavaScript se base sur un mécanisme et une terminologie différente). En réalité ce n'est pas vraiment un objet mais plutôt un modèle qui définit les propriétés que notre objet doit avoir. + +### Créons des objets + +À partir de notre classe, nous pouvons créer des objets, on parle alors d'**instancier des objets**, une classe objet a alors **une instance**. Il s'agit d'objets qui contiennent les données et attributs définis dans une classe. À partir de notre classe Personne, nous pouvons modéliser des personnes réelles : + +![Instantiation on a Personn Class for JS examples (fr)](InstancePersonne.png) + +Lorsque l'instance d'un objet est créée, on appelle la **fonction** **constructeur** de la classe pour la créer. On parle d'**instanciation** d'un objet — l'objet ainsi créé est **instancié** à partir de la classe. + +### Classes filles + +Pour notre exemple, nous n'allons pas nous contenter de personnes génériques — nous pourrions utiliser des professeurs, des étudiants, qui sont des types un peu plus spécifiques de personnes. En POO, il est possible de créer de nouvelles classes à partir d'autres classes — ces **classes filles** nouvellement créées peuvent **hériter** des propriétés et des attributs de leur **classe mère**. Il est donc possible d'avoir des attributs partagés à l'ensemble des classes plutôt que de les dupliquer. Si besoin, il est possible d'ajouter des fonctions et attributs spécifiques sur chaque classe fille. + +![Inheritance principle with French text for JS example](HeritageClasse.PNG) + +Cela s'avère très utile puisque les étudiants et les professeurs se ressemblent sur de nombreux aspects : ils ont un nom, un genre, un âge, il est donc utile de ne définir ces attributs qu'une seule fois. Il est aussi possible de redéfinir le même attribut dans différentes classes étant donné que l'attribut appartiendra à chaque fois à un nom d'espace différent. On pourra ainsi avoir différentes formes de salutations : « Hey, je m'appelle \[prénom] » pour les étudiants ( « Hey je m'appelle Sam » ) tandis que les professeurs pourront dire quelque chose d'un peu plus formel comme « Bonjour, mon nom est \[Titre]\[Nom] et j'enseigne \[matière] » par exemple « Bonjour mon nom est M. Griffiths et j'enseigne la chimie ». + +> **Note :** On parle de **polymorphisme**, lorsque des objets réutilisent la même propriété,  mais c'est juste pour info, vous embêtez pas. + +Une fois la classe fille créée il est alors possible de l'instancier et de créer des objets. Par exemple : + +![Professor instantiation example for JS fr](InstanceProf.png) + +Dans la suite de l'article, nous nous intéresserons à la mise en œuvre de la programmation orientée objet (POO) au sein de JavaScript. + +## Constructeurs et instances d'objet + +Certains disent que le JavaScript n'est pas vraiment un langage de programmation orienté objet — Il n'existe pas, en JavaScript d'élément `class` pour créer des classes alors que c'est le cas dans plusieurs langages orientés objet. JavaScript quant à lui, utilise des fonctions spéciales appelées **constructeurs** pour définir les objets et leurs propriétés. Ces constructeurs s'avèrent utiles, puisque bien souvent, nous ne savons pas combien d'objets nous allons définir, les constructeurs nous permettent de créer autant d'objets que nécessaire et d'y associer des données et des fonctions au fur et à mesure. + +Lorsqu'un objet est instancié à partir d'une fonction constructeur, les fonctions de la classe ne sont pas copiées directement dans l'objet comme dans la plupart des langages orientés objet (OO). En JavaScript, les fonctions sont liées grâce à une chaîne de référence appelée chaîne prototype (voir [Prototypes Objet](/fr/docs/Learn/JavaScript/Objects/Object_prototypes)). Il ne s'agit donc pas d'une véritable instanciation au sens strict puisque JavaScript utilise un mécanisme différent pour partager des fonctionnalités entre les objets. + +> **Note :** Ne pas être un "langage classique de POO" n'est pas nécessairement un défaut. Comme nous le mentionnions au début de l'article, la POO peut très vite devenir compliquée et JavaScript, grâce à ses différences parvient à utiliser certains concepts avancés tout en restant abordable. + +Voyons comment créer des classes via les constructeurs et les utiliser pour instancier des objets en JavaScript. Nous allons commencer par faire une copie locale du fichier [oojs.html](https://github.com/mdn/learning-area/blob/master/javascript/oojs/introduction/oojs.html) que nous avons vu dans notre premier article sur les objets. + +### Un exemple simple + +1. Tout d'abord ; voyons comment définir une personne au travers d'une fonction classique. Vous pouvez ajouter l'exemple ci-dessous dans votre code existant : + + ```js + function creerNouvellePersonne(nom) { + var obj = {}; + obj.nom = nom; + obj.salutation = function() { + alert('Salut ! Je m\'appelle ' + this.nom + '.'); + }; + return obj; + } + ``` + +2. Vous pouvez désormais créer une personne en appelant cette fonction, essayez en copiant les lignes suivantes dans la console JavaScript de votre navigateur : + + ```js + var salva = creerNouvellePersonne('Salva'); + salva.nom; + salva.salutation(); + ``` + + Ça fonctionne bien, mais on peut améliorer notre exemple. Si l'on sait que l'on va créer un objet, pourquoi créer un objet vide pour l'utiliser ensuite ? Heureusement, JavaScript est là et possède des fonctions adaptées comme les constructeurs. À l'abordage ! + +3. Remplacez la fonction précédente par celle-ci : + + ```js + function Personne(nom) { + this.nom = nom; + this.salutation = function() { + alert('Bonjour ! Je m\'appelle ' + this.nom + '.'); + }; + } + ``` + +Le constructeur est l'équivalent JavaScript d'une classe. Il possède l'ensemble des fonctionnalités d'une fonction, cependant il ne renvoie rien et ne crée pas d'objet explicitement. Il se contente de définir les propriétés et les méthodes associées. Il y a aussi l'utilisation du mot-clé `this`, ce mot-clé sert au sein d'une instance qui sera créée à y faire référence, ainsi l'attribut nom sera, pour l'instance, égal au nom passé en argument de la fonction constructrice, la méthode ` salutation``() ` retournera elle aussi le nom passé en argument de la fonction constructrice. + +> **Note :** Les fonctions de type constructeur commencent généralement par une majuscule. Cette convention d'écriture permet de repérer les constructeurs plus facilement dans le code. + +Comment pouvons-nous utiliser un constructeur ? + +1. Ajoutez les lignes suivantes au code déjà existant : + + ```js + var personne1 = new Personne('Bob'); + var personne2 = new Personne('Sarah'); + ``` + +2. Enregistrez votre code et relancez le dans votre navigateur puis essayez d'entrer les lignes suivantes dans la console : + + ```js + personne1.nom + personne1.salutation() + personne2.nom + personne2.salutation() + ``` + +Pas mal ! Vous voyez désormais que nous avons deux nouveaux objets sur cette page, chaque objet étant stocké dans un espace de nom différent, pour y accéder il faut utiliser `personne1` et `personne2` pour préfixer les fonctions et attributs. Ce rangement permet de ne pas tout casser et de ne pas rentrer en collision avec d'autres fonctionnalités. Cependant les objets disposent du même attribut `nom` et de la même méthode `salutation()`. Heureusement, les attributs et les méthodes utilisent `this` ce qui leur permet d'utiliser les valeurs propres à chaque instance et de ne pas les mélanger. + +Revoyons l'appel au constructeur : + +```js +var personne1 = new Personne('Bob'); +var personne2 = new Personne('Sarah'); +``` + +Dans chaque cas, le mot clé `new` est utilisé pour dire au navigateur que nous souhaitons définir une nouvelle instance, il est suivi du nom de la fonction que l'on utilise et de ses paramètres fournis entre parenthèses, le résultat est stocké dans une variable. Chaque instance est créée à partir de cette définition : + +```js +function Personne(nom) { + this.nom = nom; + this.salutation = function() { + alert('Bonjour ! Je m\'appelle ' + this.nom + '.'); + }; +} +``` + +Une fois les objets créés, les variables `personne1` et `personne2` contiennent les objets suivants : + +```js +{ + nom: 'Bob', + salutation: function() { + alert('Bonjour ! Je m\'appelle ' + this.nom + '.'); + } +} + +{ + nom: 'Sarah', + salutation: function() { + alert('Bonjour ! Je m\'appelle ' + this.nom + '.'); + } +} +``` + +On peut remarquer qu'à chaque appel de notre fonction constructrice nous définissons `salutation()` à chaque fois. Cela peut être évité via la définition de la fonction au sein du prototype, ce que nous verrons plus tard. + +### Créons une version finalisée de notre constructeur + +L'exemple que nous avons utilisé jusqu'à présent était destiné à aborder les notions de base des constructeurs. Créons un constructeur digne de ce nom pour notre fonction constructrice `Personne()`. + +1. Vous pouvez retirer le code que vous aviez ajouté précédemment pour le remplacer par le constructeur suivant, c'est la même fonction, ça reste un constructeur, nous avons juste ajouté quelques détails : + + ```js + function Personne(prenom, nom, age, genre, interets) { + this.nom = { + prenom, + nom + }; + this.age = age; + this.genre = genre; + this.interets = interets; + this.bio = function() { + alert(this.nom.prenom + ' ' + this.nom.nom + ' a ' + this.age + ' ans. Il aime ' + this.interets[0] + ' et ' + this.interets[1] + '.'); + }; + this.salutation = function() { + alert('Bonjour ! Je m\'appelle ' + this.nom.prenom + '.'); + }; + }; + ``` + +2. Vous pouvez ajouter la ligne ci-dessous pour créer une instance à partir du constructeur : + + ```js + var personne1 = new Personne('Bob', 'Smith', 32, 'homme', ['musique', 'ski']); + ``` + +Vous pouvez accéder aux fonctions des objets instanciés de la même manière qu'avant : + +```js +personne1['age'] +personne1.interets[1] +personne1.bio() +// etc. +``` + +> **Note :** Si vous avez du mal à faire fonctionner cet exemple, vous pouvez comparez votre travail avec notre version (voir [oojs-class-finished.html](https://github.com/mdn/learning-area/blob/master/javascript/oojs/introduction/oojs-class-finished.html) (vous pouvez aussi jeter un œil à la [démo](https://mdn.github.io/learning-area/javascript/oojs/introduction/oojs-class-finished.html)) + +### Exercices + +Vous pouvez démarrer en instanciant de nouveaux objets puis en essayant de modifier et d'accéder à leurs attributs respectifs. + +D'autre part, il y a quelques améliorations possibles pour notre méthode `bio().` En effet elle affiche systématiquement le pronom 'il', même si votre personne est une femme ou bien préfère se définir par un autre genre. De plus, la biographie n'inclut que deux passions, même s'il y en a plus dans la liste. Essayez d'améliorer cette méthode. Vous pourrez mettre votre code à l'intérieur du constructeur (vous aurez probablement besoin de quelques structures conditionnelles et d'une boucle). Réflechissez à la syntaxe des phrases qui devra s'adapter en fonction du genre et du nombre de passions listées. + +> **Note :** Si vous êtes bloqués, nous avons mis une réponse possible sur notre dépôt [GitHub ](https://github.com/mdn/learning-area/blob/master/javascript/oojs/introduction/oojs-class-further-exercises.html)([la démo](https://mdn.github.io/learning-area/javascript/oojs/introduction/oojs-class-further-exercises.html)) —tentez d'abord l'aventure avant d'aller regarder la réponse ! + +## D'autres manières d'instancier des objets + +Jusque là nous n'avons abordé que deux manières différentes pour créer une instance d'objet, la déclarer de manière explicite et en utilisant le constructeur. + +Elles sont toutes les deux valables, mais il en existe d'autres. Afin que vous les reconnaissiez lorsque vous vous baladez sur le Web, nous en avons listées quelques unes. + +### Le constructeur Object() + +Vous pouvez en premier lieu utiliser le constructeur [`Object()`](/fr/docs/Web/JavaScript/Reference/Global_Objects/Object) pour créer un nouvel objet. Oui, même les objets génériques ont leur propre constructeur, qui génère un objet vide. + +1. Essayez la commande suivante dans la console JavaScript de votre navigateur : + + ```js + var personne1 = new Object(); + ``` + +2. On stocke ainsi un objet vide dans la variable personne1. Vous pouvez ensuite ajouter des attributs et des méthodes à cet objet en utilisant la notation point ou parenthèses comme vous le souhaitez. + + ```js + personne1.nom = 'Chris'; + personne1['age'] = 38; + personne1.salutation = function() { + alert('Bonjour ! Je m\'appelle ' + this.nom + '.'); + }; + ``` + +3. Vous pouvez aussi passer un objet en paramètre du constructeur `Object()`, afin de prédéfinir certains attributs et méthodes. + + ```js + var personne1 = new Object({ + nom: 'Chris', + age: 38, + salutation: function() { + alert('Bonjour ! Je m\'appelle ' + this.nom + '.'); + } + }); + ``` + +### Via la méthode create() + +Les constructeurs permettent de structurer le code : vous pouvez avoir l'ensemble de vos constructeurs au même endroit et ensuite créer les instances suivant vos besoins, en identifiant clairement leur origine. + +Cependant, on peut vouloir créér des instances d'un objet, sans forcément définir un constructeur au préalable. (Particulierement si l'on a peu d'instances de cet object). JavaScript intègre directement une méthode appelée [`create()`](/fr/docs/Web/JavaScript/Reference/Global_Objects/Object/create) qui rend cela possible. Elle permet d'instancier un objet à partir d'un objet existant  . + +1. Essayez d'ajouter la ligne suivante dans votre console JavaScript : + + ```js + var personne2 = Object.create(personne1); + ``` + +2. Maintenant : + + ```js + personne2.nom + personne2.salutation() + ``` + +`personne2` a été créée à partir de `personne1` — et elle possède les mêmes propriétés. + +L'inconvénient de `create()` est qu'elle n'est pas supportée par IE8. Ainsi, utiliser les constructeurs peut s'avérer plus judicieux lorsqu'il s'agit de supporter les anciens navigateurs Web. + +Nous verrons les détails et les effets de `create()` plus tard. + +## Résumé + +Cet article vous a donné un aperçu simplifié de la programmation orientée objet. Tout n'y a pas été détaillé mais ça vous permet de vous faire une idée. Nous avons vu comment JavaScript s'appuyait sur un certain nombre de principes orienté objet tout en ayant un certain nombre de particularités. Nous avons aussi vu comment implémenter des classes en JavaScript via la fonction constructeur ainsi que les différentes manières de générer des instances d'objets. + +Dans le prochain article, nous explorerons le monde des objets prototypes en JavaScript. + +{{PreviousMenuNext("Learn/JavaScript/Objects/Basics", "Learn/JavaScript/Objects/Object_prototypes", "Learn/JavaScript/Objects")}} diff --git a/files/fr/learn/javascript/objects/classes_in_javascript/index.md b/files/fr/learn/javascript/objects/classes_in_javascript/index.md new file mode 100644 index 0000000000..29263128cc --- /dev/null +++ b/files/fr/learn/javascript/objects/classes_in_javascript/index.md @@ -0,0 +1,258 @@ +--- +title: L'héritage au sein de JavaScript +slug: Learn/JavaScript/Objects/Classes_in_JavaScript +tags: + - Apprendre + - Article + - Débutant + - Héritage + - JS Orienté Objet + - JavaScript + - Objet + - Programmation orientée objet + - Prototype +translation_of: Learn/JavaScript/Objects/Inheritance +original_slug: Learn/JavaScript/Objects/Inheritance +--- +{{LearnSidebar}} + +{{PreviousMenuNext("Learn/JavaScript/Objects/Object_prototypes", "Learn/JavaScript/Objects/JSON", "Learn/JavaScript/Objects")}} + +Les présentations ayant été faites pour les concepts du JavaScript orienté objet, cet article détaille comment il est possible de créer une classe fille qui hérite des propriétés de sa classe mère. Nous verrons ensuite quelques conseils quant à l'utilisation du JavaScript orienté objet. + + + + + + + + + + + + +
Pré-requis : + Une connaissance générale de l'informatique, des notions d'HTML et CSS, + une connaissance des bases en JavaScript (voir + Premiers pas et + Blocs de construction) ainsi que des notions de JavaScript orienté objet (JSOO) (voir + Introduction aux objets). +
Objectif :Comprendre comment implémenter l'héritage en JavaScript.
+ +## Héritage prototypique + +Nous avons déjà vu le concept d'héritage en action, nous avons vu comment la chaîne de prototypage fonctionnait, et comment les propriétés de cette chaîne sont lues de manière ascendante. En revanche,nous n'avons utilisé pratiquement que quelques fonctionnalités déjà intégrées dans le navigateur pour le faire. Comment créer un objet JavaScript qui hérite d'un autre objet ? + +Certains pensent que JavaScript n'est pas un véritable langage orienté objet. Dans les langages orientés objets classiques, on définit des classes objet et on peut ensuite définir laquelle hérite d'une autre (voir [C++ inheritance](http://www.tutorialspoint.com/cplusplus/cpp_inheritance.htm) en anglais pour des exemples simples). JavasScript utilise une approche différente : les objets héritant d'un autre n'ont pas de fonctionnalités copiées d'un autre objet, au lieu de ça, ils héritent des fonctionnalités via les liens de la chaîne de prototypage (on parle alors d'un **héritage prototypique**). + +Voyons comment cela se passe avec un exemple concret. + +## Pour commencer + +Tout d'abord, faites une copie du fichier [oojs-class-inheritance-start.html](https://github.com/mdn/learning-area/blob/master/javascript/oojs/advanced/oojs-class-inheritance-start.html) (voir la [démo](https://mdn.github.io/learning-area/javascript/oojs/advanced/oojs-class-inheritance-start.html)). Vous y trouverez le constructeur `Personne()` que nous avons utilisé jusque là dans l'ensemble des modules, néanmoins il y a un léger changement : nous n'avons défini que les attributs au sein du constructeur. + + function Personne(prenom, nom, age, genre, interets) { + this.nom = { + prenom, + nom + }; + this.age = age; + this.genre = genre; + this.interets = interets; + }; + +L'ensemble des méthodes est défini dans le prototype : + + Personne.prototype.saluer = function() { + alert('Salut! Je suis ' + this.nom.prenom + '.'); + }; + +Essayons de créer une classe `Professeur` similaire à celle que nous avons utilisée jusqu'ici dans les autres modules d'initiations à l'approche objet. Ainsi, cette classe hérite de `Personne` mais possède aussi : + +1. Un nouvel attribut `matière` — qui contiendra la matière que le professeur enseigne. +2. Une méthode `saluer` un peu plus élaborée, qui sera un peu plus formelle que la méthode de base, cela sera plus approprié, lorsque le professeur s'adrressera à des étudiants, par exemple. + +## Définissons le constructeur Professeur() + +La première chose à faire est de créer le constructeur `Professeur()` via l'ajout du code suivant : + + function Professeur(prenom, nom, age, genre, interets, matiere) { + Personne.call(this, prenom, nom, age, genre, interets); + + this.matiere = matiere; + } + +Cela ressemble beaucoup au constructeur `Personne` mais il y a quelque chose que nous n'avons pas encore vu : la fonction [`call()`](/fr/docs/Web/JavaScript/Reference/Global_Objects/Function/call). Cette fonction permet d'appeler une fonction définie ailleurs dans le contexte actuel. Le premier paramètre spécifie la valeur de `this` que l'on souhaite utiliser lors que l'on utilisera la fonction, les paramètres suivants seront les paramètres qui pourront être passés en arguments lorsqu'elle sera appelée. + +Nous voulons que le constructeur `Professeur()` aie les mêmes attributs que `Personne()`, nous les spécifions donc dans l'appel fait via la fonction `call()`. + +La dernière ligne au sein du constructeur sert simplement à définir l'attribut `matière` que les professeurs enseignent, ce qui n'est pas valable pour les personnes génériques. + +Notez que nous aurions très bien pu écrire tout simplement ceci : + + function Professeur(prenom, nom, age, genre, interets, matiere) { + this.nom_complet = { + prenom, + nom + }; + this.age = age; + this.genre = genre; + this.interets = interets; + this.matiere = matiere; + } + +Cependant cela aurait eu pour effet de redéfinir les attributs à nouveau, sans les hériter de `Personne()`, ce qui n'est pas vraiment le but que nous voulons atteindre lorsque l'on parle de l'héritage, cela rajoute aussi des lignes de code inutiles. + + + +### Hériter d'un constructeur sans paramètres + +Notez que si les valeurs des propriétés du constructeur dont vous héritez ne proviennent pas de paramètres, vous n'avez nullement besoin de les specifier comme arguments additionnels dans l'appel de la fonction `call()`. Donc, par exemple, si vous avez quelque chose d'aussi simple que ceci : + +```js +function Brick() { + this.width = 10; + this.height = 20; +} +``` + +Vous pouvez hériter des propriétés `width` et `height` en procédant comme ceci (Mais  également en suivant bien sûr les différentes étapes décrites ci dessous) : + +```js +function BlueGlassBrick() { + Brick.call(this); + + this.opacity = 0.5; + this.color = 'blue'; +} +``` + +Notez que nous n'avons spécifié que `this` au sein de `call()` — Aucun autre paramètre n'est requis puisque nous n'héritons ici d'aucune propriété provenant de la classe parente qui soit spécifiée via paramètres. + +## Définir le prototype de Professeur() et son constructeur référent. + +Pour le moment tout va bien, mais nous avons un petit problème. Nous avons défini un  nouveau constructeur et ce dernier possède une propriété `prototype`, qui par défaut ne contient qu'une référence à la fonction constructrice elle même. En revanche il ne contient pas les méthodes de la propriété `prototype` du constructeur `Personne()`. Pour le constater, vous pouvez par exemple entrer `Professeur.prototype.constructor` dans la console JavaScript pour voir ce qu'il en est. Le nouveau constructeur n'a en aucun cas hérité de ces méthodes. Pour le constater, comparez les sorties de `Personne.prototype.saluer` et de `Professeur.prototype.saluer` + +Notre classe `Professeur()` doit hériter des méthodes définies dans le prototype de `Personne()`. Aussi comment procéder pour obtenir ce résultat ? + +Ajoutez la ligne suivante à la suite du bloc de code que nous venons d'ajouter : + + Professeur.prototype = Object.create(Personne.prototype); + +1. Ici, notre ami [`create()`](/fr/docs/Web/JavaScript/Reference/Global_Objects/Object/create) vient nous aider à nouveau. Dans ce cas, on l'utilise afin de créer un nouvel objet que nous assignons à `Professeur.prototype`. Le nouvel objet possède `Personne.prototype` désormais comme son prototype et héritera ainsi, si et quand le besoin se fera sentir, de toutes les méthodes disponible sur `Personne.prototype`. +2. Nous avons également besoin de faire encore une chose avant de continuer. Après avoir ajouté la ligne précédente, le constructeur du prototype de `Professeur()` est désormais équivalent à celui de `Personne()`, parce que nous avons défini `Professeur.prototype` pour référencer un objet qui hérite ses propriétés de  `Personne.prototype` ! Essayez, après avoir sauvegardé votre code et rechargé la page, d'entrer `Professeur.prototype.constructor` dans la console pour vérifier. +3. Cela peut devenir problématique, autant le corriger dès maintenant. C'est possible via l'ajout de la ligne de code suivante à la fin : + + Professeur.prototype.constructor = Professeur; + +4. A présent, si vous sauvegardez et rafraichissez après avoir écrit `Professeur.prototype.constructor`, cela devrait retourner `Professeur()`, et en plus nous héritons maintenant de `Personne()` ! + +## Donner au prototype de Professeur() une nouvelle fonction saluer() + +Pour terminer notre code, nous devons définir une nouvelle fonction `saluer()` sur le constructeur de `Professeur()`. + +La façon la plus facile d'accomplir cela est de la définir sur le prototype de Professeur() — ajoutez ceci à la suite de votre code : + + Professeur.prototype.saluer = function() { + var prefix; + + if (this.genre === 'mâle' || this.genre === 'Mâle' || this.genre === 'm' || this.genre === 'M') { + prefix = 'M.'; + } else if (this.genre === 'femelle' || this.genre === 'Femelle' || this.genre === 'f' || this.genre === 'F') { + prefix = 'Mme'; + } else { + prefix = ''; + } + + alert('Bonjour. Mon nom est ' + prefix + ' ' + this.nom_complet.nom + ', et j\'enseigne ' + this.matiere + '.'); + }; + +Ceci affiche la salutation du professeur, qui utilise le titre de civilité approprié à son genre, au moyen d'une instruction conditionnelle. + + + +## Exécuter l'exemple + +Une fois tout le code saisi, essayez de créer une instance d'objet `Professeur()` en ajoutant à la fin de votre JavaScript (ou à l'endroit de votre choix) : + + var professeur1 = new Professeur('Cédric', 'Villani', 44, 'm', ['football', 'cuisine'], 'les mathématiques'); + +Sauvegardez et actualisez, et essayez d'accéder aux propriétés et méthodes de votre nouvel objet `professeur1`, par exemple : + + professeur1.nom_complet.nom; + professeur1.interets[0]; + professeur1.bio(); + professeur1.matiere; + professeur1.saluer();Ffa + +Tout cela devrait parfaitement fonctionner. Les instructions des lignes 1,2,3  et 6 accèdent à des membres hérités de la classe générique `Personne()` via son constructeur, tandis que la ligne 4 accède de façon plus spécifique à un membre qui n'est disponible que via le constructeur de la classe spécialisée `Professeur()`. + +**Note**: Si vous rencontrez un problème afin de faire fonctionner ce code comparez le à notre [version finalisée](http://mdn.github.io/learning-area/javascript/oojs/advanced/oojs-class-inheritance-finished.html) (Ou regarder tourner [notre demo en ligne](http://mdn.github.io/learning-area/javascript/oojs/advanced/oojs-class-inheritance-finished.html)). + +La méthode que nous avons détaillée ici n'est pas la seule permettant de mettre en place l'héritage de classes en JavaScript, mais elle fonctionne parfaitement et elle vous permet d'avoir une bonne idée de comment implémenter l'héritage en JavaScript. + +Vous pourriez également être intéressé par certaines des nouvelles fonctionnalités de {{glossary("ECMAScript")}} qui nous permettent de mettre en place l'héritage d'une façon beaucoup plus élégante en JavaScript (Voir [Classes](/fr/docs/Web/JavaScript/Reference/Classes)). Nous ne les avons pas développées ici parce qu'elles ne sont actuellement pas supportées par tous les navigateurs. Toutes les autres constructions dont nous avons discuté dans cette série d'articles sont supportées par IE9 et les versions moins récentes et il existe des méthodes qui prennent plus en  charge les navigateurs moins récents. + +Un moyen habituel est d'utiliser les librairies JavaScript — La plupart des options populaires ont une sélection de fonctionnalités disponibles pour réaliser l'héritage plus facilement et plus rapidement. + +[CoffeeScript](http://coffeescript.org/#classes) par exemple fournit les fonctionnalités `class`, `extends`, etc. + +## Un exercice plus complexe. + +Dans notre [section sur la programmation orientée objet](/fr/docs/Learn/JavaScript/Objects/Object-oriented_JS#Object-oriented_programming_from_10000_meters) nous avons également inclus  une classe `Etudiant` comme un concept qui hérite de toutes les fonctionnalités de la classe `Personne`, et qui a également une méthode `saluer()` differente de celle de `Personne` qui est beaucoup moins formelle que la méthode `saluer()` de `Professeur()`. Jetez un oeil à ce à quoi ressemble la méthode `saluer()` de la classe `Etudiant` dans cette section et essayez d'implémenter votre propre constructeur `Etudiant()` qui hérite de toutes les fonctionnalités de `Personne()` et la fonction `saluer()` différente. + +**Note**: Si vous rencontrez un problème afin de faire fonctionner ce code comparez le à notre [version finalisée](https://github.com/mdn/learning-area/blob/master/javascript/oojs/advanced/oojs-class-inheritance-student.html) (Ou regarder tourner [notre demo en ligne](http://mdn.github.io/learning-area/javascript/oojs/advanced/oojs-class-inheritance-student.html)). + +## Résumé sur les membres de l'Objet + +Pour résumer, vous avez de façon basique trois types de propriétés/méthodes à prendre en compte : + +1. Celles définies au sein d'un constructeur et passées en paramètres aux instances de l'objet. Celles là ne sont pas difficiles à repérer — Dans votre propre code personnalisé, elles sont les membres définis en utilisant les lignes comme `this.x = x` ; Dans les codes préconstruits propres aux navigateurs, ils sont les membres seulement accessibles aux instances d'objet (usuellement créés en appelant un constructeur via l'utilisation du mot clé `new`, exemple : `var myInstance = new myConstructor()`). +2. Celles définies directement sur les constructeurs eux mêmes et accessibles uniquement sur les constructeurs. Celles là sont communément présentes uniquement dans les objets préconstruits des navigateurs et sont reconnus par le fait d'être directement chaînées sur un constructeur et non sur une instance. Par exemple, [`Object.keys()`](/fr/docs/Web/JavaScript/Reference/Global_Objects/Object/keys). +3. Celles définies sur un prototype de constructeur qui sont héritées par toutes les instances des classes d'objet. Celles là incluent n'importe quel membre défini sur un prototype de constructeur, exemple : `myConstructor.prototype.x()`. + +Si vous êtes encore dans la confusion par rapport aux différents types ne vous inquiétez pas c'est normal — vous êtes encore entrain d'apprendre et la familiarité apparaîtra avec la pratique. + +## Quand devez-vous utiliser  l'héritage en JavaScript? + +Particulièrement après ce dernier article, vous pourriez penser "woa c'est compliqué". Bien, vous avez vu juste, prototypes et héritages représentent une partie des aspects les plus complexes de JavaScript, mais une bonne partie de la puissance et de la flexibilité de JavaScript vient de sa structure Objet et de l'héritage et il est vraiment très important de comprendre comment cela fonctionne. + +D'une certaine manière, vous utilisez l'héritage à plein temps — Que vous utilisiez différentes fonctionnalités d'une WebAPI , ou une méthode/propriété définie par défaut  sur un objet prédéfini du navigateur que vous invoquez sur vos chaînes de caractères, tableaux etc., vous utilisez de façon implicite l'héritage. + +En termes d'utilisation de l'héritage dans votre propre code, vous ne l'utiliserez probablement pas si souvent et spécialement pour débuter avec, et dans les petits projets — C'est une perte de temps d'utiliser les objets et l'héritage par amour pour cette pratique quand vous n'en avez pas besoin. Mais à mesure que les bases de votre code s'élargissent vous trouverez cette façon de faire probablement très utile. Si vous trouvez utile et plus pratique de commencer en créant un certain nombre d'objets spécialisés partageant les mêmes fonctionnalités, alors créer un objet générique qui contiendra toutes les fonctionnalités communes dont les objets spécialisés hériteront vous apparaîtra être une pratique peut être plus confortable et efficace par la suite. + +**Note**: A cause de la manière dont JavaScript fonctionne, avec la chaîne de prototype, etc., le partage de fonctionnalités entre objet est souvent appelée **délégation** — Les objets spécialisés délèguent cette fonctionnalité à l'objet de type générique. C'est certainement beaucoup plus précis que de l'appeler héritage, puisque la fonctionnalité "héritée" n'est pas copiée dans les objets qui "héritent". Au contraire, elle demeure dans l'objet générique. + +Lorsque vous utilisez l'héritage, il est conseillé de ne pas avoir trop de degrés d'héritage et de toujours garder minutieusement trace de l'endroit où vous définissez vos propriétés et méthodes. Il est possible de commencer à écrire un code qui modifie temporairement les prototypes des objets prédéfinis du navigateur mais vous ne devriez pas le faire à moins que n'ayiez une très bonne raison. Trop de degrés d'héritages peut conduire à une confusion sans fin et une peine sans fin quand vous essayez de déboguer un tel code. + +En définitive, les objets sont juste une autre forme de réutilisation de code comme les fonctions et les boucles avec leurs propres rôles et avantages. Si vous trouvez utile de créer un lot de variables et fonctions relatives et que vous voulez les retracer ensemble et les empaqueter de façon ordonnée, un objet est une bonne idée. Les objets sont également très utiles quand vous souhaitez passer une collection de données d'un endroit à un autre. Toutes ces choses peuvent être accomplies sans l'utilisation d'un constructeur ou de l'héritage. Si vous n'avez besoin que d'une seule instance, l'utilisation d'un simple objet littéral serait certainement un choix beaucoup plus judicieux et vous n'avez certainement pas besoin de l'héritage. + +## Résumé + +Cet article a couvert le reste du coeur de la théorie du JSOO et des syntaxes que nous pensons que vous devriez connaître maintenant. A cet stade vous devriez comprendre l'objet JavaScript et les bases de la POO, les prototypes et l'héritage par prototype, comment créer les classes (constructeurs) et les instances d'objet, ajouter des fonctionnalités aux classes, et créer des sous classes qui héritent d'autres classes. + +Dans le prochain article, nous jetterons un regard sur comment travailler avec le (JSON),  un format commun d'échange de données écrit en utilisant les objets JavaScript. + +## Voir aussi + +- [ObjectPlayground.com](http://www.objectplayground.com/) — Un site interactif d'appentissage très utile pour en savoir plus sur les Objets. +- [Secrets of the JavaScript Ninja](https://www.amazon.com/gp/product/193398869X/), Chapitre 6 — Un bon livre sur les concepts et techniques avancées du JavaScript par John Resig et Bear Bibeault. Le chapitre 6 couvre très bien les divers aspects des prototypes et de l'héritage ; vous trouverez sûrement facilement une version imprimée ou une version en ligne. +- [You Don't Know JS: this & Object Prototypes](https://github.com/getify/You-Dont-Know-JS/blob/master/this%20&%20object%20prototypes/README.md#you-dont-know-js-this--object-prototypes) — Une partie de l'excellente série de manuels sur le JavaScript de Kyle Simpson. Le chapitre 5 en particulier jette un regard beaucoup plus approfondi sur les prototypes que nous ne l'avons fait ici. Nous avons présenté ici une vue simplifiée dans cette série d'articles dédiée aux débutants tandis que Kyle est allé dans les détails les plus profonds et fournit une image beaucoup plus complexe et plus précise. + +{{PreviousMenuNext("Learn/JavaScript/Objects/Object_prototypes", "Learn/JavaScript/Objects/JSON", "Learn/JavaScript/Objects")}} + + + +## Dans ce module + +- [Les bases de l'Objet](/fr/docs/Learn/JavaScript/Objects/Basics) +- [JavaScript  Orienté Objet pour débutants](/fr/docs/Learn/JavaScript/Objects/Object-oriented_JS) +- [Prototypes d'Objet](/fr/docs/Learn/JavaScript/Objects/Object_prototypes) +- [L'héritage en JavaScript](/fr/docs/Learn/JavaScript/Objects/Inheritance) +- [Travailler avec les données JSON](/fr/docs/Learn/JavaScript/Objects/JSON) +- [Construire les Objets dans la pratique](/fr/docs/Learn/JavaScript/Objects/Object_building_practice) +- [Ajouter des fonctionnalités à la démo de nos balles bondissantes](/fr/docs/Learn/JavaScript/Objects/Adding_bouncing_balls_features) diff --git a/files/fr/learn/javascript/objects/inheritance/index.md b/files/fr/learn/javascript/objects/inheritance/index.md deleted file mode 100644 index 89668811fa..0000000000 --- a/files/fr/learn/javascript/objects/inheritance/index.md +++ /dev/null @@ -1,258 +0,0 @@ ---- -title: L'héritage au sein de JavaScript -slug: Learn/JavaScript/Objects/Inheritance -tags: - - Apprendre - - Article - - Débutant - - Héritage - - JS Orienté Objet - - JavaScript - - Objet - - Programmation orientée objet - - Prototype -translation_of: Learn/JavaScript/Objects/Inheritance -original_slug: Learn/JavaScript/Objects/Heritage ---- -{{LearnSidebar}} - -{{PreviousMenuNext("Learn/JavaScript/Objects/Object_prototypes", "Learn/JavaScript/Objects/JSON", "Learn/JavaScript/Objects")}} - -Les présentations ayant été faites pour les concepts du JavaScript orienté objet, cet article détaille comment il est possible de créer une classe fille qui hérite des propriétés de sa classe mère. Nous verrons ensuite quelques conseils quant à l'utilisation du JavaScript orienté objet. - - - - - - - - - - - - -
Pré-requis : - Une connaissance générale de l'informatique, des notions d'HTML et CSS, - une connaissance des bases en JavaScript (voir - Premiers pas et - Blocs de construction) ainsi que des notions de JavaScript orienté objet (JSOO) (voir - Introduction aux objets). -
Objectif :Comprendre comment implémenter l'héritage en JavaScript.
- -## Héritage prototypique - -Nous avons déjà vu le concept d'héritage en action, nous avons vu comment la chaîne de prototypage fonctionnait, et comment les propriétés de cette chaîne sont lues de manière ascendante. En revanche,nous n'avons utilisé pratiquement que quelques fonctionnalités déjà intégrées dans le navigateur pour le faire. Comment créer un objet JavaScript qui hérite d'un autre objet ? - -Certains pensent que JavaScript n'est pas un véritable langage orienté objet. Dans les langages orientés objets classiques, on définit des classes objet et on peut ensuite définir laquelle hérite d'une autre (voir [C++ inheritance](http://www.tutorialspoint.com/cplusplus/cpp_inheritance.htm) en anglais pour des exemples simples). JavasScript utilise une approche différente : les objets héritant d'un autre n'ont pas de fonctionnalités copiées d'un autre objet, au lieu de ça, ils héritent des fonctionnalités via les liens de la chaîne de prototypage (on parle alors d'un **héritage prototypique**). - -Voyons comment cela se passe avec un exemple concret. - -## Pour commencer - -Tout d'abord, faites une copie du fichier [oojs-class-inheritance-start.html](https://github.com/mdn/learning-area/blob/master/javascript/oojs/advanced/oojs-class-inheritance-start.html) (voir la [démo](https://mdn.github.io/learning-area/javascript/oojs/advanced/oojs-class-inheritance-start.html)). Vous y trouverez le constructeur `Personne()` que nous avons utilisé jusque là dans l'ensemble des modules, néanmoins il y a un léger changement : nous n'avons défini que les attributs au sein du constructeur. - - function Personne(prenom, nom, age, genre, interets) { - this.nom = { - prenom, - nom - }; - this.age = age; - this.genre = genre; - this.interets = interets; - }; - -L'ensemble des méthodes est défini dans le prototype : - - Personne.prototype.saluer = function() { - alert('Salut! Je suis ' + this.nom.prenom + '.'); - }; - -Essayons de créer une classe `Professeur` similaire à celle que nous avons utilisée jusqu'ici dans les autres modules d'initiations à l'approche objet. Ainsi, cette classe hérite de `Personne` mais possède aussi : - -1. Un nouvel attribut `matière` — qui contiendra la matière que le professeur enseigne. -2. Une méthode `saluer` un peu plus élaborée, qui sera un peu plus formelle que la méthode de base, cela sera plus approprié, lorsque le professeur s'adrressera à des étudiants, par exemple. - -## Définissons le constructeur Professeur() - -La première chose à faire est de créer le constructeur `Professeur()` via l'ajout du code suivant : - - function Professeur(prenom, nom, age, genre, interets, matiere) { - Personne.call(this, prenom, nom, age, genre, interets); - - this.matiere = matiere; - } - -Cela ressemble beaucoup au constructeur `Personne` mais il y a quelque chose que nous n'avons pas encore vu : la fonction [`call()`](/fr/docs/Web/JavaScript/Reference/Global_Objects/Function/call). Cette fonction permet d'appeler une fonction définie ailleurs dans le contexte actuel. Le premier paramètre spécifie la valeur de `this` que l'on souhaite utiliser lors que l'on utilisera la fonction, les paramètres suivants seront les paramètres qui pourront être passés en arguments lorsqu'elle sera appelée. - -Nous voulons que le constructeur `Professeur()` aie les mêmes attributs que `Personne()`, nous les spécifions donc dans l'appel fait via la fonction `call()`. - -La dernière ligne au sein du constructeur sert simplement à définir l'attribut `matière` que les professeurs enseignent, ce qui n'est pas valable pour les personnes génériques. - -Notez que nous aurions très bien pu écrire tout simplement ceci : - - function Professeur(prenom, nom, age, genre, interets, matiere) { - this.nom_complet = { - prenom, - nom - }; - this.age = age; - this.genre = genre; - this.interets = interets; - this.matiere = matiere; - } - -Cependant cela aurait eu pour effet de redéfinir les attributs à nouveau, sans les hériter de `Personne()`, ce qui n'est pas vraiment le but que nous voulons atteindre lorsque l'on parle de l'héritage, cela rajoute aussi des lignes de code inutiles. - - - -### Hériter d'un constructeur sans paramètres - -Notez que si les valeurs des propriétés du constructeur dont vous héritez ne proviennent pas de paramètres, vous n'avez nullement besoin de les specifier comme arguments additionnels dans l'appel de la fonction `call()`. Donc, par exemple, si vous avez quelque chose d'aussi simple que ceci : - -```js -function Brick() { - this.width = 10; - this.height = 20; -} -``` - -Vous pouvez hériter des propriétés `width` et `height` en procédant comme ceci (Mais  également en suivant bien sûr les différentes étapes décrites ci dessous) : - -```js -function BlueGlassBrick() { - Brick.call(this); - - this.opacity = 0.5; - this.color = 'blue'; -} -``` - -Notez que nous n'avons spécifié que `this` au sein de `call()` — Aucun autre paramètre n'est requis puisque nous n'héritons ici d'aucune propriété provenant de la classe parente qui soit spécifiée via paramètres. - -## Définir le prototype de Professeur() et son constructeur référent. - -Pour le moment tout va bien, mais nous avons un petit problème. Nous avons défini un  nouveau constructeur et ce dernier possède une propriété `prototype`, qui par défaut ne contient qu'une référence à la fonction constructrice elle même. En revanche il ne contient pas les méthodes de la propriété `prototype` du constructeur `Personne()`. Pour le constater, vous pouvez par exemple entrer `Professeur.prototype.constructor` dans la console JavaScript pour voir ce qu'il en est. Le nouveau constructeur n'a en aucun cas hérité de ces méthodes. Pour le constater, comparez les sorties de `Personne.prototype.saluer` et de `Professeur.prototype.saluer` - -Notre classe `Professeur()` doit hériter des méthodes définies dans le prototype de `Personne()`. Aussi comment procéder pour obtenir ce résultat ? - -Ajoutez la ligne suivante à la suite du bloc de code que nous venons d'ajouter : - - Professeur.prototype = Object.create(Personne.prototype); - -1. Ici, notre ami [`create()`](/fr/docs/Web/JavaScript/Reference/Global_Objects/Object/create) vient nous aider à nouveau. Dans ce cas, on l'utilise afin de créer un nouvel objet que nous assignons à `Professeur.prototype`. Le nouvel objet possède `Personne.prototype` désormais comme son prototype et héritera ainsi, si et quand le besoin se fera sentir, de toutes les méthodes disponible sur `Personne.prototype`. -2. Nous avons également besoin de faire encore une chose avant de continuer. Après avoir ajouté la ligne précédente, le constructeur du prototype de `Professeur()` est désormais équivalent à celui de `Personne()`, parce que nous avons défini `Professeur.prototype` pour référencer un objet qui hérite ses propriétés de  `Personne.prototype` ! Essayez, après avoir sauvegardé votre code et rechargé la page, d'entrer `Professeur.prototype.constructor` dans la console pour vérifier. -3. Cela peut devenir problématique, autant le corriger dès maintenant. C'est possible via l'ajout de la ligne de code suivante à la fin : - - Professeur.prototype.constructor = Professeur; - -4. A présent, si vous sauvegardez et rafraichissez après avoir écrit `Professeur.prototype.constructor`, cela devrait retourner `Professeur()`, et en plus nous héritons maintenant de `Personne()` ! - -## Donner au prototype de Professeur() une nouvelle fonction saluer() - -Pour terminer notre code, nous devons définir une nouvelle fonction `saluer()` sur le constructeur de `Professeur()`. - -La façon la plus facile d'accomplir cela est de la définir sur le prototype de Professeur() — ajoutez ceci à la suite de votre code : - - Professeur.prototype.saluer = function() { - var prefix; - - if (this.genre === 'mâle' || this.genre === 'Mâle' || this.genre === 'm' || this.genre === 'M') { - prefix = 'M.'; - } else if (this.genre === 'femelle' || this.genre === 'Femelle' || this.genre === 'f' || this.genre === 'F') { - prefix = 'Mme'; - } else { - prefix = ''; - } - - alert('Bonjour. Mon nom est ' + prefix + ' ' + this.nom_complet.nom + ', et j\'enseigne ' + this.matiere + '.'); - }; - -Ceci affiche la salutation du professeur, qui utilise le titre de civilité approprié à son genre, au moyen d'une instruction conditionnelle. - - - -## Exécuter l'exemple - -Une fois tout le code saisi, essayez de créer une instance d'objet `Professeur()` en ajoutant à la fin de votre JavaScript (ou à l'endroit de votre choix) : - - var professeur1 = new Professeur('Cédric', 'Villani', 44, 'm', ['football', 'cuisine'], 'les mathématiques'); - -Sauvegardez et actualisez, et essayez d'accéder aux propriétés et méthodes de votre nouvel objet `professeur1`, par exemple : - - professeur1.nom_complet.nom; - professeur1.interets[0]; - professeur1.bio(); - professeur1.matiere; - professeur1.saluer();Ffa - -Tout cela devrait parfaitement fonctionner. Les instructions des lignes 1,2,3  et 6 accèdent à des membres hérités de la classe générique `Personne()` via son constructeur, tandis que la ligne 4 accède de façon plus spécifique à un membre qui n'est disponible que via le constructeur de la classe spécialisée `Professeur()`. - -**Note**: Si vous rencontrez un problème afin de faire fonctionner ce code comparez le à notre [version finalisée](http://mdn.github.io/learning-area/javascript/oojs/advanced/oojs-class-inheritance-finished.html) (Ou regarder tourner [notre demo en ligne](http://mdn.github.io/learning-area/javascript/oojs/advanced/oojs-class-inheritance-finished.html)). - -La méthode que nous avons détaillée ici n'est pas la seule permettant de mettre en place l'héritage de classes en JavaScript, mais elle fonctionne parfaitement et elle vous permet d'avoir une bonne idée de comment implémenter l'héritage en JavaScript. - -Vous pourriez également être intéressé par certaines des nouvelles fonctionnalités de {{glossary("ECMAScript")}} qui nous permettent de mettre en place l'héritage d'une façon beaucoup plus élégante en JavaScript (Voir [Classes](/fr/docs/Web/JavaScript/Reference/Classes)). Nous ne les avons pas développées ici parce qu'elles ne sont actuellement pas supportées par tous les navigateurs. Toutes les autres constructions dont nous avons discuté dans cette série d'articles sont supportées par IE9 et les versions moins récentes et il existe des méthodes qui prennent plus en  charge les navigateurs moins récents. - -Un moyen habituel est d'utiliser les librairies JavaScript — La plupart des options populaires ont une sélection de fonctionnalités disponibles pour réaliser l'héritage plus facilement et plus rapidement. - -[CoffeeScript](http://coffeescript.org/#classes) par exemple fournit les fonctionnalités `class`, `extends`, etc. - -## Un exercice plus complexe. - -Dans notre [section sur la programmation orientée objet](/fr/docs/Learn/JavaScript/Objects/Object-oriented_JS#Object-oriented_programming_from_10000_meters) nous avons également inclus  une classe `Etudiant` comme un concept qui hérite de toutes les fonctionnalités de la classe `Personne`, et qui a également une méthode `saluer()` differente de celle de `Personne` qui est beaucoup moins formelle que la méthode `saluer()` de `Professeur()`. Jetez un oeil à ce à quoi ressemble la méthode `saluer()` de la classe `Etudiant` dans cette section et essayez d'implémenter votre propre constructeur `Etudiant()` qui hérite de toutes les fonctionnalités de `Personne()` et la fonction `saluer()` différente. - -**Note**: Si vous rencontrez un problème afin de faire fonctionner ce code comparez le à notre [version finalisée](https://github.com/mdn/learning-area/blob/master/javascript/oojs/advanced/oojs-class-inheritance-student.html) (Ou regarder tourner [notre demo en ligne](http://mdn.github.io/learning-area/javascript/oojs/advanced/oojs-class-inheritance-student.html)). - -## Résumé sur les membres de l'Objet - -Pour résumer, vous avez de façon basique trois types de propriétés/méthodes à prendre en compte : - -1. Celles définies au sein d'un constructeur et passées en paramètres aux instances de l'objet. Celles là ne sont pas difficiles à repérer — Dans votre propre code personnalisé, elles sont les membres définis en utilisant les lignes comme `this.x = x` ; Dans les codes préconstruits propres aux navigateurs, ils sont les membres seulement accessibles aux instances d'objet (usuellement créés en appelant un constructeur via l'utilisation du mot clé `new`, exemple : `var myInstance = new myConstructor()`). -2. Celles définies directement sur les constructeurs eux mêmes et accessibles uniquement sur les constructeurs. Celles là sont communément présentes uniquement dans les objets préconstruits des navigateurs et sont reconnus par le fait d'être directement chaînées sur un constructeur et non sur une instance. Par exemple, [`Object.keys()`](/fr/docs/Web/JavaScript/Reference/Global_Objects/Object/keys). -3. Celles définies sur un prototype de constructeur qui sont héritées par toutes les instances des classes d'objet. Celles là incluent n'importe quel membre défini sur un prototype de constructeur, exemple : `myConstructor.prototype.x()`. - -Si vous êtes encore dans la confusion par rapport aux différents types ne vous inquiétez pas c'est normal — vous êtes encore entrain d'apprendre et la familiarité apparaîtra avec la pratique. - -## Quand devez-vous utiliser  l'héritage en JavaScript? - -Particulièrement après ce dernier article, vous pourriez penser "woa c'est compliqué". Bien, vous avez vu juste, prototypes et héritages représentent une partie des aspects les plus complexes de JavaScript, mais une bonne partie de la puissance et de la flexibilité de JavaScript vient de sa structure Objet et de l'héritage et il est vraiment très important de comprendre comment cela fonctionne. - -D'une certaine manière, vous utilisez l'héritage à plein temps — Que vous utilisiez différentes fonctionnalités d'une WebAPI , ou une méthode/propriété définie par défaut  sur un objet prédéfini du navigateur que vous invoquez sur vos chaînes de caractères, tableaux etc., vous utilisez de façon implicite l'héritage. - -En termes d'utilisation de l'héritage dans votre propre code, vous ne l'utiliserez probablement pas si souvent et spécialement pour débuter avec, et dans les petits projets — C'est une perte de temps d'utiliser les objets et l'héritage par amour pour cette pratique quand vous n'en avez pas besoin. Mais à mesure que les bases de votre code s'élargissent vous trouverez cette façon de faire probablement très utile. Si vous trouvez utile et plus pratique de commencer en créant un certain nombre d'objets spécialisés partageant les mêmes fonctionnalités, alors créer un objet générique qui contiendra toutes les fonctionnalités communes dont les objets spécialisés hériteront vous apparaîtra être une pratique peut être plus confortable et efficace par la suite. - -**Note**: A cause de la manière dont JavaScript fonctionne, avec la chaîne de prototype, etc., le partage de fonctionnalités entre objet est souvent appelée **délégation** — Les objets spécialisés délèguent cette fonctionnalité à l'objet de type générique. C'est certainement beaucoup plus précis que de l'appeler héritage, puisque la fonctionnalité "héritée" n'est pas copiée dans les objets qui "héritent". Au contraire, elle demeure dans l'objet générique. - -Lorsque vous utilisez l'héritage, il est conseillé de ne pas avoir trop de degrés d'héritage et de toujours garder minutieusement trace de l'endroit où vous définissez vos propriétés et méthodes. Il est possible de commencer à écrire un code qui modifie temporairement les prototypes des objets prédéfinis du navigateur mais vous ne devriez pas le faire à moins que n'ayiez une très bonne raison. Trop de degrés d'héritages peut conduire à une confusion sans fin et une peine sans fin quand vous essayez de déboguer un tel code. - -En définitive, les objets sont juste une autre forme de réutilisation de code comme les fonctions et les boucles avec leurs propres rôles et avantages. Si vous trouvez utile de créer un lot de variables et fonctions relatives et que vous voulez les retracer ensemble et les empaqueter de façon ordonnée, un objet est une bonne idée. Les objets sont également très utiles quand vous souhaitez passer une collection de données d'un endroit à un autre. Toutes ces choses peuvent être accomplies sans l'utilisation d'un constructeur ou de l'héritage. Si vous n'avez besoin que d'une seule instance, l'utilisation d'un simple objet littéral serait certainement un choix beaucoup plus judicieux et vous n'avez certainement pas besoin de l'héritage. - -## Résumé - -Cet article a couvert le reste du coeur de la théorie du JSOO et des syntaxes que nous pensons que vous devriez connaître maintenant. A cet stade vous devriez comprendre l'objet JavaScript et les bases de la POO, les prototypes et l'héritage par prototype, comment créer les classes (constructeurs) et les instances d'objet, ajouter des fonctionnalités aux classes, et créer des sous classes qui héritent d'autres classes. - -Dans le prochain article, nous jetterons un regard sur comment travailler avec le (JSON),  un format commun d'échange de données écrit en utilisant les objets JavaScript. - -## Voir aussi - -- [ObjectPlayground.com](http://www.objectplayground.com/) — Un site interactif d'appentissage très utile pour en savoir plus sur les Objets. -- [Secrets of the JavaScript Ninja](https://www.amazon.com/gp/product/193398869X/), Chapitre 6 — Un bon livre sur les concepts et techniques avancées du JavaScript par John Resig et Bear Bibeault. Le chapitre 6 couvre très bien les divers aspects des prototypes et de l'héritage ; vous trouverez sûrement facilement une version imprimée ou une version en ligne. -- [You Don't Know JS: this & Object Prototypes](https://github.com/getify/You-Dont-Know-JS/blob/master/this%20&%20object%20prototypes/README.md#you-dont-know-js-this--object-prototypes) — Une partie de l'excellente série de manuels sur le JavaScript de Kyle Simpson. Le chapitre 5 en particulier jette un regard beaucoup plus approfondi sur les prototypes que nous ne l'avons fait ici. Nous avons présenté ici une vue simplifiée dans cette série d'articles dédiée aux débutants tandis que Kyle est allé dans les détails les plus profonds et fournit une image beaucoup plus complexe et plus précise. - -{{PreviousMenuNext("Learn/JavaScript/Objects/Object_prototypes", "Learn/JavaScript/Objects/JSON", "Learn/JavaScript/Objects")}} - - - -## Dans ce module - -- [Les bases de l'Objet](/fr/docs/Learn/JavaScript/Objects/Basics) -- [JavaScript  Orienté Objet pour débutants](/fr/docs/Learn/JavaScript/Objects/Object-oriented_JS) -- [Prototypes d'Objet](/fr/docs/Learn/JavaScript/Objects/Object_prototypes) -- [L'héritage en JavaScript](/fr/docs/Learn/JavaScript/Objects/Inheritance) -- [Travailler avec les données JSON](/fr/docs/Learn/JavaScript/Objects/JSON) -- [Construire les Objets dans la pratique](/fr/docs/Learn/JavaScript/Objects/Object_building_practice) -- [Ajouter des fonctionnalités à la démo de nos balles bondissantes](/fr/docs/Learn/JavaScript/Objects/Adding_bouncing_balls_features) diff --git a/files/fr/learn/javascript/objects/object-oriented_js/index.md b/files/fr/learn/javascript/objects/object-oriented_js/index.md deleted file mode 100644 index be3d5ffacd..0000000000 --- a/files/fr/learn/javascript/objects/object-oriented_js/index.md +++ /dev/null @@ -1,309 +0,0 @@ ---- -title: Le JavaScript orienté objet pour débutants -slug: Learn/JavaScript/Objects/Object-oriented_JS -tags: - - Apprendre - - Débutant - - Guide - - JavaScript - - OOJS - - OOP - - POO -translation_of: Learn/JavaScript/Objects/Object-oriented_JS -original_slug: Learn/JavaScript/Objects/JS_orienté-objet ---- -{{LearnSidebar}}{{PreviousMenuNext("Learn/JavaScript/Objects/Basics", "Learn/JavaScript/Objects/Object_prototypes", "Learn/JavaScript/Objects")}} - -Après avoir parcouru les fondamentaux, nous allons aborder en détail le JavaScript orienté objet (JSOO). Cet article présente une approche simple de la programmation orientée objet (POO) et détaille comment JavaScript émule des classes objet au travers des méthodes constructeur et comment instancier ces objets. - - - - - - - - - - - - -
Pré-requis : - Connaissances de base en informatique et compréhension des notions HTML - et CSS, notions de JavaScript (voir - Premiers pas et - Blocs de construction) -
Objectif : - Comprendre les concepts de base derrière la programmation orientée objet - et comment ils s'appliquent à JavaScript ( « tout est objet » ) et - comment créer des constructeurs et instancier des objets. -
- -## La programmation orientée objet de loin - -Pour commencer, donnons une vue simplifiée et de haut niveau de ce qu'est la programmation orientée objet (POO). On parle d'une vision simplifiée étant donnée que la POO peut devenir très vite complexe et qu'être exhaustif rendrait probablement la découverte plus confuse et difficile qu'autre chose. L'idée de base de la POO consiste à utiliser des objets pour modéliser les objets du monde réel que l'on souhaite représenter dans nos programmes et/ou de fournir un moyen simple d'accéder à une fonctionnalité qu'il serait difficile d'utiliser autrement. - -Les objets peuvent contenir des données et du code représentant de l'information au sujet de la chose que l'on essaie de modéliser ainsi que des fonctionnalités ou un comportement que l'on souhaite lui appliquer. Les données (et bien souvent les fonctions) associées à un objet peuvent être stockées (le terme officiel est **encapsulé**) à l'intérieur d'un paquet objet. Il est possible de donner un nom spécifique à un paquet objet afin  d'y faire référence, on parle alors d'**espace de noms** ou _namespace_, il sera ainsi plus facile de le manipuler et d'y accéder. Les objets peuvent aussi servir pour stocker des données et les transférer facilement sur un réseau. - -### Définissons un modèle objet - -Nous allons voir un programme simple qui affiche des informations à propos des élèves et des professeurs d'une école. Nous allons aborder la théorie de la programmation orientée objet de manière générale sans l'appliquer à un langage particulier. - -Pour débuter, nous pouvons réutiliser l'objet Personne que nous avons créé dans notre [premier article](/fr/docs/Learn/JavaScript/Objects/Basics), il définit un ensemble de données et actions d'une personne. Il existe tout un tas de choses que nous pourrions savoir au sujet d'une personne (son adresse, sa taille, sa pointure, son ADN, son numéro de passeport, ses traits particuliers significatifs… ). En l'occurrence, nous souhaitons uniquement afficher son nom, son âge, ses passions, écrire une petite introduction à son sujet en utilisant ces données et lui apprendre à se présenter. On parle alors d'**abstraction** : créer un modèle simplifié de quelque chose de complexe mais qui ne contient que les aspects qui nous intéressent. Il sera alors plus simple de manipuler ce modèle objet simplifié dans le cadre de notre programme. - -![Classe Personne avec attributs élémentaires](ClassePersonne.png) - -Dans plusieurs langages de POO, la définition d'un objet est appelé une **classe** (comme on le verra ci-après, JavaScript se base sur un mécanisme et une terminologie différente). En réalité ce n'est pas vraiment un objet mais plutôt un modèle qui définit les propriétés que notre objet doit avoir. - -### Créons des objets - -À partir de notre classe, nous pouvons créer des objets, on parle alors d'**instancier des objets**, une classe objet a alors **une instance**. Il s'agit d'objets qui contiennent les données et attributs définis dans une classe. À partir de notre classe Personne, nous pouvons modéliser des personnes réelles : - -![Instantiation on a Personn Class for JS examples (fr)](InstancePersonne.png) - -Lorsque l'instance d'un objet est créée, on appelle la **fonction** **constructeur** de la classe pour la créer. On parle d'**instanciation** d'un objet — l'objet ainsi créé est **instancié** à partir de la classe. - -### Classes filles - -Pour notre exemple, nous n'allons pas nous contenter de personnes génériques — nous pourrions utiliser des professeurs, des étudiants, qui sont des types un peu plus spécifiques de personnes. En POO, il est possible de créer de nouvelles classes à partir d'autres classes — ces **classes filles** nouvellement créées peuvent **hériter** des propriétés et des attributs de leur **classe mère**. Il est donc possible d'avoir des attributs partagés à l'ensemble des classes plutôt que de les dupliquer. Si besoin, il est possible d'ajouter des fonctions et attributs spécifiques sur chaque classe fille. - -![Inheritance principle with French text for JS example](HeritageClasse.PNG) - -Cela s'avère très utile puisque les étudiants et les professeurs se ressemblent sur de nombreux aspects : ils ont un nom, un genre, un âge, il est donc utile de ne définir ces attributs qu'une seule fois. Il est aussi possible de redéfinir le même attribut dans différentes classes étant donné que l'attribut appartiendra à chaque fois à un nom d'espace différent. On pourra ainsi avoir différentes formes de salutations : « Hey, je m'appelle \[prénom] » pour les étudiants ( « Hey je m'appelle Sam » ) tandis que les professeurs pourront dire quelque chose d'un peu plus formel comme « Bonjour, mon nom est \[Titre]\[Nom] et j'enseigne \[matière] » par exemple « Bonjour mon nom est M. Griffiths et j'enseigne la chimie ». - -> **Note :** On parle de **polymorphisme**, lorsque des objets réutilisent la même propriété,  mais c'est juste pour info, vous embêtez pas. - -Une fois la classe fille créée il est alors possible de l'instancier et de créer des objets. Par exemple : - -![Professor instantiation example for JS fr](InstanceProf.png) - -Dans la suite de l'article, nous nous intéresserons à la mise en œuvre de la programmation orientée objet (POO) au sein de JavaScript. - -## Constructeurs et instances d'objet - -Certains disent que le JavaScript n'est pas vraiment un langage de programmation orienté objet — Il n'existe pas, en JavaScript d'élément `class` pour créer des classes alors que c'est le cas dans plusieurs langages orientés objet. JavaScript quant à lui, utilise des fonctions spéciales appelées **constructeurs** pour définir les objets et leurs propriétés. Ces constructeurs s'avèrent utiles, puisque bien souvent, nous ne savons pas combien d'objets nous allons définir, les constructeurs nous permettent de créer autant d'objets que nécessaire et d'y associer des données et des fonctions au fur et à mesure. - -Lorsqu'un objet est instancié à partir d'une fonction constructeur, les fonctions de la classe ne sont pas copiées directement dans l'objet comme dans la plupart des langages orientés objet (OO). En JavaScript, les fonctions sont liées grâce à une chaîne de référence appelée chaîne prototype (voir [Prototypes Objet](/fr/docs/Learn/JavaScript/Objects/Object_prototypes)). Il ne s'agit donc pas d'une véritable instanciation au sens strict puisque JavaScript utilise un mécanisme différent pour partager des fonctionnalités entre les objets. - -> **Note :** Ne pas être un "langage classique de POO" n'est pas nécessairement un défaut. Comme nous le mentionnions au début de l'article, la POO peut très vite devenir compliquée et JavaScript, grâce à ses différences parvient à utiliser certains concepts avancés tout en restant abordable. - -Voyons comment créer des classes via les constructeurs et les utiliser pour instancier des objets en JavaScript. Nous allons commencer par faire une copie locale du fichier [oojs.html](https://github.com/mdn/learning-area/blob/master/javascript/oojs/introduction/oojs.html) que nous avons vu dans notre premier article sur les objets. - -### Un exemple simple - -1. Tout d'abord ; voyons comment définir une personne au travers d'une fonction classique. Vous pouvez ajouter l'exemple ci-dessous dans votre code existant : - - ```js - function creerNouvellePersonne(nom) { - var obj = {}; - obj.nom = nom; - obj.salutation = function() { - alert('Salut ! Je m\'appelle ' + this.nom + '.'); - }; - return obj; - } - ``` - -2. Vous pouvez désormais créer une personne en appelant cette fonction, essayez en copiant les lignes suivantes dans la console JavaScript de votre navigateur : - - ```js - var salva = creerNouvellePersonne('Salva'); - salva.nom; - salva.salutation(); - ``` - - Ça fonctionne bien, mais on peut améliorer notre exemple. Si l'on sait que l'on va créer un objet, pourquoi créer un objet vide pour l'utiliser ensuite ? Heureusement, JavaScript est là et possède des fonctions adaptées comme les constructeurs. À l'abordage ! - -3. Remplacez la fonction précédente par celle-ci : - - ```js - function Personne(nom) { - this.nom = nom; - this.salutation = function() { - alert('Bonjour ! Je m\'appelle ' + this.nom + '.'); - }; - } - ``` - -Le constructeur est l'équivalent JavaScript d'une classe. Il possède l'ensemble des fonctionnalités d'une fonction, cependant il ne renvoie rien et ne crée pas d'objet explicitement. Il se contente de définir les propriétés et les méthodes associées. Il y a aussi l'utilisation du mot-clé `this`, ce mot-clé sert au sein d'une instance qui sera créée à y faire référence, ainsi l'attribut nom sera, pour l'instance, égal au nom passé en argument de la fonction constructrice, la méthode ` salutation``() ` retournera elle aussi le nom passé en argument de la fonction constructrice. - -> **Note :** Les fonctions de type constructeur commencent généralement par une majuscule. Cette convention d'écriture permet de repérer les constructeurs plus facilement dans le code. - -Comment pouvons-nous utiliser un constructeur ? - -1. Ajoutez les lignes suivantes au code déjà existant : - - ```js - var personne1 = new Personne('Bob'); - var personne2 = new Personne('Sarah'); - ``` - -2. Enregistrez votre code et relancez le dans votre navigateur puis essayez d'entrer les lignes suivantes dans la console : - - ```js - personne1.nom - personne1.salutation() - personne2.nom - personne2.salutation() - ``` - -Pas mal ! Vous voyez désormais que nous avons deux nouveaux objets sur cette page, chaque objet étant stocké dans un espace de nom différent, pour y accéder il faut utiliser `personne1` et `personne2` pour préfixer les fonctions et attributs. Ce rangement permet de ne pas tout casser et de ne pas rentrer en collision avec d'autres fonctionnalités. Cependant les objets disposent du même attribut `nom` et de la même méthode `salutation()`. Heureusement, les attributs et les méthodes utilisent `this` ce qui leur permet d'utiliser les valeurs propres à chaque instance et de ne pas les mélanger. - -Revoyons l'appel au constructeur : - -```js -var personne1 = new Personne('Bob'); -var personne2 = new Personne('Sarah'); -``` - -Dans chaque cas, le mot clé `new` est utilisé pour dire au navigateur que nous souhaitons définir une nouvelle instance, il est suivi du nom de la fonction que l'on utilise et de ses paramètres fournis entre parenthèses, le résultat est stocké dans une variable. Chaque instance est créée à partir de cette définition : - -```js -function Personne(nom) { - this.nom = nom; - this.salutation = function() { - alert('Bonjour ! Je m\'appelle ' + this.nom + '.'); - }; -} -``` - -Une fois les objets créés, les variables `personne1` et `personne2` contiennent les objets suivants : - -```js -{ - nom: 'Bob', - salutation: function() { - alert('Bonjour ! Je m\'appelle ' + this.nom + '.'); - } -} - -{ - nom: 'Sarah', - salutation: function() { - alert('Bonjour ! Je m\'appelle ' + this.nom + '.'); - } -} -``` - -On peut remarquer qu'à chaque appel de notre fonction constructrice nous définissons `salutation()` à chaque fois. Cela peut être évité via la définition de la fonction au sein du prototype, ce que nous verrons plus tard. - -### Créons une version finalisée de notre constructeur - -L'exemple que nous avons utilisé jusqu'à présent était destiné à aborder les notions de base des constructeurs. Créons un constructeur digne de ce nom pour notre fonction constructrice `Personne()`. - -1. Vous pouvez retirer le code que vous aviez ajouté précédemment pour le remplacer par le constructeur suivant, c'est la même fonction, ça reste un constructeur, nous avons juste ajouté quelques détails : - - ```js - function Personne(prenom, nom, age, genre, interets) { - this.nom = { - prenom, - nom - }; - this.age = age; - this.genre = genre; - this.interets = interets; - this.bio = function() { - alert(this.nom.prenom + ' ' + this.nom.nom + ' a ' + this.age + ' ans. Il aime ' + this.interets[0] + ' et ' + this.interets[1] + '.'); - }; - this.salutation = function() { - alert('Bonjour ! Je m\'appelle ' + this.nom.prenom + '.'); - }; - }; - ``` - -2. Vous pouvez ajouter la ligne ci-dessous pour créer une instance à partir du constructeur : - - ```js - var personne1 = new Personne('Bob', 'Smith', 32, 'homme', ['musique', 'ski']); - ``` - -Vous pouvez accéder aux fonctions des objets instanciés de la même manière qu'avant : - -```js -personne1['age'] -personne1.interets[1] -personne1.bio() -// etc. -``` - -> **Note :** Si vous avez du mal à faire fonctionner cet exemple, vous pouvez comparez votre travail avec notre version (voir [oojs-class-finished.html](https://github.com/mdn/learning-area/blob/master/javascript/oojs/introduction/oojs-class-finished.html) (vous pouvez aussi jeter un œil à la [démo](https://mdn.github.io/learning-area/javascript/oojs/introduction/oojs-class-finished.html)) - -### Exercices - -Vous pouvez démarrer en instanciant de nouveaux objets puis en essayant de modifier et d'accéder à leurs attributs respectifs. - -D'autre part, il y a quelques améliorations possibles pour notre méthode `bio().` En effet elle affiche systématiquement le pronom 'il', même si votre personne est une femme ou bien préfère se définir par un autre genre. De plus, la biographie n'inclut que deux passions, même s'il y en a plus dans la liste. Essayez d'améliorer cette méthode. Vous pourrez mettre votre code à l'intérieur du constructeur (vous aurez probablement besoin de quelques structures conditionnelles et d'une boucle). Réflechissez à la syntaxe des phrases qui devra s'adapter en fonction du genre et du nombre de passions listées. - -> **Note :** Si vous êtes bloqués, nous avons mis une réponse possible sur notre dépôt [GitHub ](https://github.com/mdn/learning-area/blob/master/javascript/oojs/introduction/oojs-class-further-exercises.html)([la démo](https://mdn.github.io/learning-area/javascript/oojs/introduction/oojs-class-further-exercises.html)) —tentez d'abord l'aventure avant d'aller regarder la réponse ! - -## D'autres manières d'instancier des objets - -Jusque là nous n'avons abordé que deux manières différentes pour créer une instance d'objet, la déclarer de manière explicite et en utilisant le constructeur. - -Elles sont toutes les deux valables, mais il en existe d'autres. Afin que vous les reconnaissiez lorsque vous vous baladez sur le Web, nous en avons listées quelques unes. - -### Le constructeur Object() - -Vous pouvez en premier lieu utiliser le constructeur [`Object()`](/fr/docs/Web/JavaScript/Reference/Global_Objects/Object) pour créer un nouvel objet. Oui, même les objets génériques ont leur propre constructeur, qui génère un objet vide. - -1. Essayez la commande suivante dans la console JavaScript de votre navigateur : - - ```js - var personne1 = new Object(); - ``` - -2. On stocke ainsi un objet vide dans la variable personne1. Vous pouvez ensuite ajouter des attributs et des méthodes à cet objet en utilisant la notation point ou parenthèses comme vous le souhaitez. - - ```js - personne1.nom = 'Chris'; - personne1['age'] = 38; - personne1.salutation = function() { - alert('Bonjour ! Je m\'appelle ' + this.nom + '.'); - }; - ``` - -3. Vous pouvez aussi passer un objet en paramètre du constructeur `Object()`, afin de prédéfinir certains attributs et méthodes. - - ```js - var personne1 = new Object({ - nom: 'Chris', - age: 38, - salutation: function() { - alert('Bonjour ! Je m\'appelle ' + this.nom + '.'); - } - }); - ``` - -### Via la méthode create() - -Les constructeurs permettent de structurer le code : vous pouvez avoir l'ensemble de vos constructeurs au même endroit et ensuite créer les instances suivant vos besoins, en identifiant clairement leur origine. - -Cependant, on peut vouloir créér des instances d'un objet, sans forcément définir un constructeur au préalable. (Particulierement si l'on a peu d'instances de cet object). JavaScript intègre directement une méthode appelée [`create()`](/fr/docs/Web/JavaScript/Reference/Global_Objects/Object/create) qui rend cela possible. Elle permet d'instancier un objet à partir d'un objet existant  . - -1. Essayez d'ajouter la ligne suivante dans votre console JavaScript : - - ```js - var personne2 = Object.create(personne1); - ``` - -2. Maintenant : - - ```js - personne2.nom - personne2.salutation() - ``` - -`personne2` a été créée à partir de `personne1` — et elle possède les mêmes propriétés. - -L'inconvénient de `create()` est qu'elle n'est pas supportée par IE8. Ainsi, utiliser les constructeurs peut s'avérer plus judicieux lorsqu'il s'agit de supporter les anciens navigateurs Web. - -Nous verrons les détails et les effets de `create()` plus tard. - -## Résumé - -Cet article vous a donné un aperçu simplifié de la programmation orientée objet. Tout n'y a pas été détaillé mais ça vous permet de vous faire une idée. Nous avons vu comment JavaScript s'appuyait sur un certain nombre de principes orienté objet tout en ayant un certain nombre de particularités. Nous avons aussi vu comment implémenter des classes en JavaScript via la fonction constructeur ainsi que les différentes manières de générer des instances d'objets. - -Dans le prochain article, nous explorerons le monde des objets prototypes en JavaScript. - -{{PreviousMenuNext("Learn/JavaScript/Objects/Basics", "Learn/JavaScript/Objects/Object_prototypes", "Learn/JavaScript/Objects")}} diff --git a/files/ja/_redirects.txt b/files/ja/_redirects.txt index 40ffd7a36e..e511d50334 100644 --- a/files/ja/_redirects.txt +++ b/files/ja/_redirects.txt @@ -2693,7 +2693,9 @@ /ja/docs/Learn/HTML/Forms/Your_first_HTML_form /ja/docs/Learn/Forms/Your_first_form /ja/docs/Learn/HTML/Forms/Your_first_HTML_form/Example /ja/docs/Learn/Forms/Your_first_form/Example /ja/docs/Learn/HTML/Introduction_to_HTML/高度なテキスト成型 /ja/docs/Learn/HTML/Introduction_to_HTML/Advanced_text_formatting -/ja/docs/Learn/JavaScript/Objects/継承 /ja/docs/Learn/JavaScript/Objects/Inheritance +/ja/docs/Learn/JavaScript/Objects/Inheritance /ja/docs/Learn/JavaScript/Objects/Classes_in_JavaScript +/ja/docs/Learn/JavaScript/Objects/Object-oriented_JS /ja/docs/conflicting/Learn/JavaScript/Objects/Classes_in_JavaScript +/ja/docs/Learn/JavaScript/Objects/継承 /ja/docs/Learn/JavaScript/Objects/Classes_in_JavaScript /ja/docs/Learn/JavaScript/非同期 /ja/docs/Learn/JavaScript/Asynchronous /ja/docs/Learn/Performance/Populating_the_page:_how_browsers_work /ja/docs/Web/Performance/How_browsers_work /ja/docs/Learn/Server-side/Express_Nodejs/Installing_on_PWS_Cloud_Foundry /ja/docs/orphaned/Learn/Server-side/Express_Nodejs/Installing_on_PWS_Cloud_Foundry diff --git a/files/ja/_wikihistory.json b/files/ja/_wikihistory.json index b55beced95..673d001d2e 100644 --- a/files/ja/_wikihistory.json +++ b/files/ja/_wikihistory.json @@ -5531,7 +5531,7 @@ "chameleonhead" ] }, - "Learn/JavaScript/Objects/Inheritance": { + "Learn/JavaScript/Objects/Classes_in_JavaScript": { "modified": "2020-12-12T19:47:14.522Z", "contributors": [ "eldesh", @@ -5554,19 +5554,6 @@ "r-tamura" ] }, - "Learn/JavaScript/Objects/Object-oriented_JS": { - "modified": "2020-12-06T14:17:48.495Z", - "contributors": [ - "eldesh", - "silverskyvicto", - "mfuji09", - "JuthaDDA", - "Uemmra3", - "hamasaki", - "kenji-yamasaki", - "sii" - ] - }, "Learn/JavaScript/Objects/Object_building_practice": { "modified": "2020-12-07T11:55:33.524Z", "contributors": [ @@ -48449,6 +48436,19 @@ "momdo" ] }, + "conflicting/Learn/JavaScript/Objects/Classes_in_JavaScript": { + "modified": "2020-12-06T14:17:48.495Z", + "contributors": [ + "eldesh", + "silverskyvicto", + "mfuji09", + "JuthaDDA", + "Uemmra3", + "hamasaki", + "kenji-yamasaki", + "sii" + ] + }, "conflicting/MDN/Contribute/Markdown_in_MDN": { "modified": "2020-09-30T15:30:25.061Z", "contributors": [ diff --git a/files/ja/conflicting/learn/javascript/objects/classes_in_javascript/index.html b/files/ja/conflicting/learn/javascript/objects/classes_in_javascript/index.html new file mode 100644 index 0000000000..66e8b522d0 --- /dev/null +++ b/files/ja/conflicting/learn/javascript/objects/classes_in_javascript/index.html @@ -0,0 +1,292 @@ +--- +title: 初心者のためのオブジェクト指向 JavaScript +slug: conflicting/Learn/JavaScript/Objects/Classes_in_JavaScript +tags: + - Beginner + - Create + - JavaScript + - OOJS + - OOP + - オブジェクト + - オブジェクト指向 + - 学習 + - 記事 +translation_of: Learn/JavaScript/Objects/Object-oriented_JS +original_slug: Learn/JavaScript/Objects/Object-oriented_JS +--- +
{{LearnSidebar}}
+ +
{{PreviousMenuNext("Learn/JavaScript/Objects/Basics", "Learn/JavaScript/Objects/Object_prototypes", "Learn/JavaScript/Objects")}}
+ +

基礎が片付いたところで、オブジェクト指向 JavaScript (OOJS) について取り上げます。この記事ではオブジェクト指向プログラミング (OOP) の基本的な視点を説明し、 JavaScript がどのようにコンストラクター関数を通じてオブジェクトクラスをエミュレートしているか、またどのようにオブジェクトインスタンスを生成しているかを紹介します。

+ + + + + + + + + + + + +
前提知識:基礎的なコンピュータリテラシー、基礎的な HTML と CSS の理解、JavaScript (JavaScript の第一歩JavaScript の構成要素を参照) および OOJS (JavaScript オブジェクトの基本を参照)の基礎知識。
目標:オブジェクト指向プログラミングの基本理論、どのようにそれが JavaScript (「すべてはオブジェクトである」) に関連しているか、どのようにコンストラクターがオブジェクトインスタンスを生成しているかを理解する。
+ +

オブジェクト指向プログラミング - その基本

+ +

はじめに、オブジェクト指向プログラミング (OOP) とは何か、シンプルかつ高レベルな視点を提示します。シンプルと述べたのは、OOP はあっという間にひどく複雑になり得るためで、現時点でそのすべてを論じてしまうと、助けとなるよりもむしろ混乱を生んでしまうことでしょう。OOP の基本的な考え方は、プログラムの中で扱いたい、現実世界の事物を模るためにオブジェクトを使用すること、またはそうしなければ使うことが難しいあるいは不可能だった機能にアクセスするための、シンプルな方法を提供することです。

+ +

オブジェクトは、モデル化しようとしている事物に関する情報および、持たせたい機能や動作を表現する、関連したデータとコードを持つことができます。オブジェクトのデータ (しばしば関数も含む) はオブジェクトのパッケージの中 (名前空間と呼ばれることがある) に適切に格納されます (カプセル化)。オブジェクトは一般に、ネットワークを通じて容易に送信することが可能な、データストアとしても使われます。

+ +

オブジェクトのテンプレートを定義する

+ +

学校の生徒と教師の情報を表示する、シンプルなプログラムを考えてみましょう。特定のプログラミング言語の文脈ではなく、OOP 一般の理論を眺めていきます。

+ +

はじめに、オブジェクト入門の最初の記事にある、人物の包括的なデータや機能を定義した、Person オブジェクトに戻りましょう。ある人物について知り得る事柄は数多くあります (住所、身長、靴のサイズ、DNA 情報、パスポート番号、顕著な人格特性など) が、このケースでは名前、年齢、性別、趣味を表示することに興味があるだけです。また、このデータに基づいた短い自己紹介や、挨拶をさせられるようにもしましょう。これは抽象化 — より複雑な事物を、プログラムの目的に沿って簡単に操作できるように、その最も重要な側面を表現する、シンプルなモデルを作ること — として知られています。

+ +

+ +

実際のオブジェクトの生成

+ +

このクラスから、オブジェクトインスタンスを生成することができます。オブジェクトインスタンスは、クラスで定義されたデータや機能を持ったオブジェクトです。 Person クラスから、何名かの実際の人物を生成します。

+ +

+ +

クラスからオブジェクトインスタンスが生成されるとき、クラスのコンストラクター関数が生成のために実行されます。クラスからオブジェクトインスタンスが生成される過程をインスタンス化と呼びます。オブジェクトインスタンスは、クラスをインスタンス化したものです。

+ +

専門のクラス

+ +

このケースで求めているのは、包括的な人物ではなく、より特定のタイプである、教師と生徒です。OOP では、他のクラスを元にした新しいクラスを作ることができます。これらの新しい子クラスは、親クラスからデータやコード機能を継承することができ、すべてのオブジェクトタイプに共通する機能を、重複させるのではなく、再利用することができます。クラス間で機能が異なる場合は、必要に応じて特殊化された機能を直接定義することができます。

+ +

+ +

これは実に役立ちます。教師と生徒は名前、性別、年齢のように多数の共通機能を共有しており、これらの機能を一度だけ定義すればいいので便利です。異なるクラスで、同じ機能を分けて定義することもでき、その機能の各定義は異なる名前空間に置かれます。例えば、生徒の挨拶は "Yo, I'm [firstName]" (例:Yo, I'm Sam) という形式とし、一方の教師の挨拶は、より形式的に "Hello, my name is [Prefix] [lastName], and I teach [Subject]." (例:Hello, My name is Mr Griffiths, and I teach Chemistry) のように。

+ +
+

: 同じ機能を複数のオブジェクトタイプが実装する能力のことを示す用語に、ポリモーフィズムがあります。不思議に感じているかも知れないので念のため。

+
+ +

子クラスのオブジェクトインスタンスを生成しましょう。例:

+ +

+ +

記事の続きでは、OOP 理論が JavaScript でどのように実践されているかを見ていきます。

+ +

コンストラクターとオブジェクトインスタンス

+ +

JavaScript では、オブジェクトやその機能を定義し初期化するためにコンストラクター関数と呼ばれる特殊な関数を使用します。これは便利です。なぜならオブジェクトをいくつ作成することになるか分からない状況に出くわすでしょうから。コンストラクターは必要な数のオブジェクトを効率的な方法で作成し、必要に応じてデータや関数を付加する手段を提供します。

+ +

JavaScript でコンストラクターを通じてクラスを作り、そこからオブジェクトのインスタンスを生成するところを見ていきましょう。まずは、最初のオブジェクトの記事で見た oojs.html ファイルの新しいコピーを、ローカルに作成しておいてください。

+ +

シンプルな例

+ +
    +
  1. どのように通常の関数で人物を定義できるかを見てみるところから始めましょう。この関数を script 要素の中に加えてください。 + +
    function createNewPerson(name) {
    +  const obj = {};
    +  obj.name = name;
    +  obj.greeting = function() {
    +    alert('Hi! I\'m ' + obj.name + '.');
    +  };
    +  return obj;
    +}
    +
  2. +
  3. この関数を呼び出すことで、新しい人物を生成することができます。次の 3 行をブラウザーの JavaScript コンソールで試してみてください。 +
    const salva = createNewPerson('Salva');
    +salva.name;
    +salva.greeting();
    + これも十分上手くいっていますが、やや長ったらしいです。オブジェクトを生成したいと知っているなら、なぜ明示的に空のオブジェクトを生成し、返すことが必要なのでしょうか?幸いにも、 JavaScript はコンストラクター関数という形で、便利なショートカットを提供してくれます。早速作ってみましょう!
  4. +
  5. 前の関数を、以下のもので置き換えてください。 +
    function Person(name) {
    +  this.name = name;
    +  this.greeting = function() {
    +    alert('Hi! I\'m ' + this.name + '.');
    +  };
    +}
    +
  6. +
+ +

コンストラクター関数は、 JavaScript 版のクラスです。それは関数に期待される全ての機能を持っていますが、何も返さないし、明示的にオブジェクトを生成しもしないという点に注意してください。基本的には、プロパティとメソッドを定義するだけです。加えて、 this キーワードが使われていることにも注意してください。基本、オブジェクトインスタンスの 1 つが作成されるときにはいつでも、オブジェクトの name プロパティはコンストラクター呼び出しに渡される name 値と等しくなり、 greeting() メソッドもコンストラクター呼び出しに渡される name 値を使用します。

+ +
+

メモ: 通常、コンストラクター関数の名前は大文字で始まります。コードの中で、コンストラクター関数がより容易に認識されるようにするための慣習です。

+
+ +

では、オブジェクトを生成するために、どのようにコンストラクターを呼び出したらよいでしょうか?

+ +
    +
  1. 次の 2 行を、前のコードの続きに加えてください。 +
    let person1 = new Person('Bob');
    +let person2 = new Person('Sarah');
    +
  2. +
  3. コードを保存し、ブラウザーをリロードして、以下の 4 行を JavaScript コンソールに入れて試してみてください。 +
    person1.name
    +person1.greeting()
    +person2.name
    +person2.greeting()
    +
  4. +
+ +

素晴らしい!2 つの新しいオブジェクトが、異なる名前空間の下でページに格納されていることが確認できます。それらのプロパティとメソッドにアクセスするときには、 person1 または person2 を呼び出すことから始めなければなりません。中に含まれている機能は適切にパッケージ化されており、他の機能と衝突しないようになっています。しかしながら、それらは同じように name プロパティと greeting() メソッドが利用可能です。 2 つのオブジェクトはそれぞれ、生成されたときに割り当てられた、自身の name 値を使っていることに注意してください。これが this を使うことがとても重要である理由の 1 つであり、他の値ではなく、自身の値を使っているのです。

+ +

コンストラクターをもう一度呼び出してみましょう。

+ +
let person1 = new Person('Bob');
+let person2 = new Person('Sarah');
+ +

いずれのケースでも、新しいオブジェクトインスタンスを生成したいとブラウザーに伝えるために new キーワードが使われており、その後に、括弧に必要なパラメーターを入れた関数名が続き、その結果が変数に格納されていて、一般的な関数の呼ばれ方とよく似ています。どちらのインスタンスも、次の定義によって生成されています。

+ +
function Person(name) {
+  this.name = name;
+  this.greeting = function() {
+    alert('Hi! I\'m ' + this.name + '.');
+  };
+}
+ +

新しいオブジェクトが生成された後、 person1 および person2 変数は、次のオブジェクトを格納しています。

+ +
{
+  name: 'Bob',
+  greeting: function() {
+    alert('Hi! I\'m ' + this.name + '.');
+  }
+}
+
+{
+  name: 'Sarah',
+  greeting: function() {
+    alert('Hi! I\'m ' + this.name + '.');
+  }
+}
+ +

コンストラクター関数を呼び出すとき、毎回 greeting() メソッドを定義していることに注意してください。これは理想的ではありません。これを回避するために、代わりにプロトタイプに関数を定義することができます。後で見てみましょう。

+ +

最終的なコンストラクターの作成

+ +

上で見てきた例は、スタートのためのシンプルな例に過ぎません。次は最終的な Person() コンストラクター関数を作りましょう。

+ +
    +
  1. ここまでに挿入したコードを削除し、代わりとなるコンストラクターを追加してください。これはシンプルな例とほぼ同じもので、ほんのわずか複雑になっているだけです。 +
    function Person(first, last, age, gender, interests) {
    +  this.name = {
    +     first : first,
    +     last : last
    +  };
    +  this.age = age;
    +  this.gender = gender;
    +  this.interests = interests;
    +  this.bio = function() {
    +    alert(this.name.first + ' ' + this.name.last + ' is ' + this.age + ' years old. He likes ' + this.interests[0] + ' and ' + this.interests[1] + '.');
    +  };
    +  this.greeting = function() {
    +    alert('Hi! I\'m ' + this.name.first + '.');
    +  };
    +}
    +
  2. +
  3. ではその下に、コンストラクターからオブジェクトインスタンスを生成するため、次の行を追加してください。 +
    let person1 = new Person('Bob', 'Smith', 32, 'male', ['music', 'skiing']);
    +
  4. +
+ +

ちょうど以前行ったように、プロパティやメソッドにアクセスできることを確認できます。 JavaScript コンソールの中でやってみましょう。

+ +
person1['age']
+person1.interests[1]
+person1.bio()
+// etc.
+ +
+

メモ: もしこの工程で何らかのトラブルがあった場合は、あなたのコードを我々のバージョン (oojs-class-finished.htmlライブサンプルも) と比べてみてください。

+
+ +

さらなる練習

+ +

まずはじめに、さらにいくつかのオブジェクトを生成する独自の行を追加し、オブジェクトインスタンスのメンバーの取得や設定をしてみてください。

+ +

加えて、 bio() メソッドにはいくつかの問題点があります。人物が女性である、あるいは他の優先される性別分類の場合でも、その出力には常に "He" という代名詞が含まれています。また、 bio は interests 配列により多くのものが列挙されていても、2 つの趣味しか含みません。このクラス定義 (コンストラクター) の問題を、あなたはどのように修正することができますか?コンストラクター内に任意のコード (恐らく、いくつかの条件分岐やループが必要となるでしょう) を入れてみてください。性別や、趣味の数が 1、2、あるいは 2 よりも多いかどうかによって、文がどのように構築されるべきか考えてみてください。

+ +
+

: もし行き詰まってしまった場合は、GitHub に答えとなるリポジトリ (ライブ) があります。最初はあなた自身で書いてみてください!

+
+ +

オブジェクトインスタンスを生成する他の方法

+ +

ここまで、オブジェクトインスタンスを生成する 2 つの異なる方法を見てきました。オブジェクトリテラルの宣言と、上で見たコンストラクター関数の使用です。

+ +

これで十分かもしれませんが、他にも方法はあります。ウェブを巡る際に遭遇したときに備えて、よく知っておいてください。

+ +

Object() コンストラクター

+ +

まず最初に、 Object() コンストラクターを新しいオブジェクトの生成のために使うことができます。はい、一般的なオブジェクトにも、空のオブジェクトを生成するコンストラクターがあります。

+ +
    +
  1. このコードを JavaScript コンソールに入力してみましょう。 +
    let person1 = new Object();
    +
  2. +
  3. person1 変数に空のオブジェクトが格納されました。このオブジェクトに、ドット記法とブラケット記法を使ってプロパティを追加することができます。次の例を JavaScript コンソールで試してみましょう。 +
    person1.name = 'Chris';
    +person1['age'] = 38;
    +person1.greeting = function() {
    +  alert('Hi! I\'m ' + this.name + '.');
    +};
    +
  4. +
  5. あらかじめプロパティやメソッドを設定するため、Object() コンストラクターに引数としてオブジェクトリテラルを渡すことも可能です。次のコードを JavaScript コンソールで試してみてください。 +
    let person1 = new Object({
    +  name: 'Chris',
    +  age: 38,
    +  greeting: function() {
    +    alert('Hi! I\'m ' + this.name + '.');
    +  }
    +});
    +
  6. +
+ +

create() メソッドの使用

+ +

コードの順序についてもコンストラクターが助けとなります。コンストラクターを一箇所で作っておくと、必要に応じてインスタンスを生成することができ、それらがどこから来たものであるか、明瞭です。

+ +

しかしながら、特に少数のインスタンスのみを生成する場合に、最初にコンストラクターを作らずにインスタンスを生成することを好む人もいます。JavaScript にはそれを可能とする、create() と呼ばれる組み込みメソッドがあります。それにより、既存のオブジェクトを基にして、新しいオブジェクトを生成することができます。

+ +
    +
  1. 前のセクションの練習をブラウザーで終えた状態で、こちらを JavaScript コンソールで試してみてください。 +
    let person2 = Object.create(person1);
    +
  2. +
  3. 次は以下のコードです。 +
    person2.name;
    +person2.greeting();
    +
  4. +
+ +

person1 を基に person2 が生成され、person2 では同じプロパティとメソッドが利用可能であることを確認することができます。

+ +

create() には、IE8 が対応していないという制限があります。つまり、コンストラクターは古いブラウザーに対応したい場合により効果的かもしれません。

+ +

いずれ、create() の効果についてより詳細に紹介するつもりです。

+ +

あなたのスキルをテストしてみましょう!

+ +

この記事はここまでですが、最も重要な情報を覚えていますか?先に進む前に、この情報を保持しているかどうかを確認するために、さらにいくつかのテストを見つけることができます。あなたのスキルをテストする: オブジェクト指向 JavaScript を参照してください。

+ +

この一連のテストは次の記事で紹介する知識に依存していることに注意してください。なので、試してみる前に、まずそれを読んでみるといいかもしれません。

+ +

まとめ

+ +

この記事はオブジェクト指向の理論の概略を見てきました。これですべてではありませんが、ここで扱っていることに関する考えを提示しました。加えて、オブジェクトのインスタンスを生成する様々な方法を見始めたところです。

+ +

次の記事では、 JavaScript オブジェクトのプロトタイプについて紹介します。

+ +

{{PreviousMenuNext("Learn/JavaScript/Objects/Basics", "Learn/JavaScript/Objects/Object_prototypes", "Learn/JavaScript/Objects")}}

+ +

このモジュール内の文書

+ + diff --git a/files/ja/learn/javascript/objects/classes_in_javascript/index.html b/files/ja/learn/javascript/objects/classes_in_javascript/index.html new file mode 100644 index 0000000000..b0802b7072 --- /dev/null +++ b/files/ja/learn/javascript/objects/classes_in_javascript/index.html @@ -0,0 +1,413 @@ +--- +title: JavaScript での継承 +slug: Learn/JavaScript/Objects/Classes_in_JavaScript +tags: + - Article + - CodingScripting + - Inheritance + - JavaScript + - OOJS + - OOP + - Object + - Prototype + - l10n:priority + - 初心者 + - 学習 +translation_of: Learn/JavaScript/Objects/Inheritance +original_slug: Learn/JavaScript/Objects/Inheritance +--- +
{{LearnSidebar}}
+ +
{{PreviousMenuNext("Learn/JavaScript/Objects/Object_prototypes", "Learn/JavaScript/Objects/JSON", "Learn/JavaScript/Objects")}}
+ +

OOJS のぞっとするような細部はほとんど説明されたので、ここでは”親”クラスからの機能を継承する”子供”のオブジェクトクラス (コンストラクタ) の生成方法について解説します。さらに、いつ、どこで OOJS を使うかについてのアドバイスを提示し、最新の ECMAScript の構文でクラスがどのように扱われるかを見ていきます。

+ + + + + + + + + + + + +
前提知識基本的なコンピュータの知識および利用能力、HTML と CSS への基本的な理解、JavaScript の基本 (第一歩構成要素を参照) と OOJS の基本 (オブジェクト入門) に慣れている。
目的:JavaScript でどのように継承ができるようになっているかを理解していること。
+ +

プロトタイプでの継承

+ +

ここまで動作している継承 ー プロトタイプチェーンがどのように動作するか、どのようにメンバーが繋がるチェーンから継承されるのかを見てきました。しかし、これらの大半はブラウザーの組み込み関数で実行されています。我々が他のオブジェクトから継承したオブジェクトを作成するには JavaScript でどのようにするのでしょうか。

+ +

具体的な例を使ってどのようの継承が行われているかを見てゆきましょう。

+ +

さあ始めてみよう

+ +

まず、oojs-class-inheritance-start.html ファイルをローカルにコピーしましょう (あるいはライブ版の実行でも確認できます)。ここでこのモジュールで幅広く使用されてきた Person() というコンストラクタの例を見つけることができます。わずかな違いがあって、コンストラクタ内部にプロパティのみが定義されています。

+ +
function Person(first, last, age, gender, interests) {
+  this.name = {
+    first,
+    last
+  };
+  this.age = age;
+  this.gender = gender;
+  this.interests = interests;
+};
+ +

メソッドはすべてコンストラクタのプロトタイプとして定義されています。例えば、

+ +
Person.prototype.greeting = function() {
+  alert('Hi! I\'m ' + this.name.first + '.');
+};
+ +
+

注意: ソースコードに、bio() と farewell() が定義されています。後ほどこれらのメソッドがどのようにほかのコンストラクタで継承されるのかを確認します。

+
+ +

Teacher クラスを作成したい場合を考えましょう。これは最初のオブジェクト指向の特徴にて述べたもののようなクラスで、Person からすべてのメンバーを継承しますが、次のものも内包しています。

+ +
    +
  1. 新しいプロパティの subject — これはその先生の教える科目を格納します。
  2. +
  3. 上書きされた greeting() メソッド、標準の greeting() メソッドよりわずかに固く感じられる — 学校で生徒に語りかける先生により相応しい。
  4. +
+ +

Teacher() コンストラクタの機能を定義しよう

+ +

われわれのまずすべき事は Teacher() コンストラクタを作成する事です — 以下に続くコードを既存コードの下に追加してください。

+ +
function Teacher(first, last, age, gender, interests, subject) {
+  Person.call(this, first, last, age, gender, interests);
+
+  this.subject = subject;
+}
+ +

これは多くの点で Person コンストラクタと似ていますが、これまでに見てきたものと異なったものがあります—  call() 関数です。この関数は基本的に別の場所で定義された関数を、しかし現在のコンテキストで呼び出すことができます。最初の引数は関数を実行するときに使用したい this の値を指定します、また他の引数は実行される関数に渡されるべき値です。

+ +

Teacher() コンストラクタは継承元の Person() コンストラクタと同じ引数を取りたいため、 call() を呼び出して、すべての引き数を引数として渡します。

+ +

コンストラクタの最後の行は、先生が行うべきであり、一般の人が持たない新たな subject(授業) のプロパティを定義しています。

+ +

注意として、下記のソースのように、このようにシンプルにも書けます。

+ +
function Teacher(first, last, age, gender, interests, subject) {
+  this.name = {
+    first,
+    last
+  };
+  this.age = age;
+  this.gender = gender;
+  this.interests = interests;
+  this.subject = subject;
+}
+ +

しかしながらこれはただ改めてプロパティを再定義しているだけで、 Person() から継承していません、そのため、説明しようとしたポイントが伝わりません。またコード行数が多くもなります。

+ +

引数なしのコンストラクタからの継承

+ +

もし継承したコンストラクタがパラメータからプロパティの値を取得しない場合、 call() の呼び出しで追加の引数を指定する必要がないことを示しておきます。そのため、例えば、このような本当にシンプルなものがある場合、

+ +
function Brick() {
+  this.width = 10;
+  this.height = 20;
+}
+ +

このように書くことで width と height プロパティを継承することができます(もちろん、下に挙げる数行のステップの様にすることもできます)。

+ +
function BlueGlassBrick() {
+  Brick.call(this);
+
+  this.opacity = 0.5;
+  this.color = 'blue';
+}
+ +

 call() の中に this だけを記載していることに注意して下さい— 引数を介して親より設定されるどのプロパティも継承しないので他の引数は不要です。

+ +

Teacher()のプロトタイプ とコンストラクタの参照への設定方法

+ +

今まではすべて順調でしたが、1点問題があります。新しいコンストラクタを定義して、その中に 1 つの prototype プロパティを持たせ、これはデフォルトでただ自身のコンストラクタ関数への参照を保持しています。Person のコンストラクタの prototype プロパティへのメソッド群は持っていません。このことを見てみたいのならば Object.getOwnPropertyNames(Teacher.prototype)  をテキスト入力フィールドや JavaScript コンソールへ入力を試してみてください。そして再度入力する時には、Teacher を Person で置き換えてみてください。新しいコンストラクタもそれらのメソッドを継承していません。このことを確認するために、Person.prototype.greeting と Teacher.prototype.greeting の出力結果を比べてみてください。Person()  のプロトタイプに定義されたメソッドを継承するために Teacher() を生成する必要があります。ではどのようにすればよいのでしょうか。

+ +
    +
  1. 前回追加した部分の下に以下の行を追加してみましょう: +
    Teacher.prototype = Object.create(Person.prototype);
    + ここで我々に馴染み深い create() に再度助けてもらいましょう。この場合に新しいオブジェクトを作ってそれを Teacher.prototype の値とするのに使います。新しいオブジェクトは Person.prototype を自身のプロトタイプとして保持し、それがゆえに(必要となる時には) Person.prototype 上で利用できるすべてのメソッドを継承します。
  2. +
  3. 先に進む前にもう一つやることがあります。
    + 最後の行を追加した後、Teacher.prototypeconstructor プロパティは Person() と同じになりました。なぜなら、Person.prototype からプロパティを継承するオブジェクトを参照するように Teacher.prototype を設定しただけだからです。コードを保存し、ブラウザでページを読み込み、コンソールに Teacher.prototype.constructor と入力して確認してみてください。
  4. +
  5. これは問題になるかもしれません、なので以下の内容をすぐに設定しましょう。 ソースコードにまた戻って最後に以下の行を追加しましょう。 +
    Object.defineProperty(Teacher.prototype, 'constructor', {
    +    value: Teacher,
    +    enumerable: false, // 'for in'ループで現れないようにする
    +    writable: true });
    +
  6. +
  7. ソースコードを保存およびページの再読み込みを行って、 Teacher.prototype.constructor と入力したならば Teacher() と返すでしょう、希望した通りに Person() から継承することができました!
  8. +
+ +

Teacher() に greeting() ファンクションを付け加える

+ +

コードを完成させる前に、Teacher() コンストラクタに新たに greeting() 関数を追加する必要があります。

+ +

このようにする一番簡単な方法は Teacher() のプロトタイプに定義することです — コードの最後に以下のコードを追加します。

+ +
Teacher.prototype.greeting = function() {
+  let prefix;
+
+  if (this.gender === 'male' || this.gender === 'Male' || this.gender === 'm' || this.gender === 'M') {
+    prefix = 'Mr.';
+  } else if (this.gender === 'female' || this.gender === 'Female' || this.gender === 'f' || this.gender === 'F') {
+    prefix = 'Mrs.';
+  } else {
+    prefix = 'Mx.';
+  }
+
+  alert('Hello. My name is ' + prefix + ' ' + this.name.last + ', and I teach ' + this.subject + '.');
+};
+ +

これは性別を基にした適切な敬称を使う教師の挨拶を通知します。条件文を使うことで実現します。

+ +

例を試してみよう

+ +

これまでのコードをすべて入力し終えているなら、ソースコード(もしくはあなたの用意した同じようなコードに)の最後に続けて Teacher() からオブジェクトインスタンスを生成してみましょう。

+ +
let teacher1 = new Teacher('Dave', 'Griffiths', 31, 'male', ['football', 'cookery'], 'mathematics');
+ +

保存し、再読み込みをしたなら、新たな teacher1 オブジェクトのプロパティとメソッドにアクセスしてみましょう、例えば。

+ +
teacher1.name.first;
+teacher1.interests[0];
+teacher1.bio();
+teacher1.subject;
+teacher1.greeting();
+teacher1.farewell();
+ +

これらはすべて正常に動作するはずです。1, 2, 3, 6 行目のクエリは、ジェネリックな Person() コンストラクタ (クラス) から継承されたメンバにアクセスします。4 行目のクエリは、より特殊な Teacher() コンストラクタ (クラス) でのみ利用可能なメンバにアクセスしています。5 行目のクエリは Person() から継承したメンバにアクセスしていますが、Teacher() には同じ名前の独自のメンバがあるため、そのメンバにアクセスしています。

+ +
+

注記: もしここまでの例がうまく動作しないなら、あなたのコードと完成版(ライブ版も参照)を比較してみてください。

+
+ +

ここで述べている手法は JavaScript でクラスを継承する唯一の方法ではなく、問題なく動作し、JavaScript でのどのように実装するかの良いアイデアを提示しています。

+ +

また JavaScript でより明瞭に継承を行えるようにした新しい{{glossary("ECMAScript")}}の機能(Classes を参照)にも興味を持つかもしれません。ここではそれらについて言及はしませんでした、それはまだブラウザー間で幅広くサポートされていないためです。一連の記事で検討してきた他のコード構造はすべて、IE9 やそれ以前のバージョンといった、はるか以前よりサポートされており、それより早くからのサポートを確認する方法となります。

+ +

一般的な方法は JavaScript ライブラリを使用することです — よく知られた選択肢のうちの大部分は、よりたやすく素早く利用できる簡易な機能セットを持っています。例えば CoffeeScript は class, extends などを提供します。

+ +

追加の特訓

+ +

OOP 理論のセクションでは、概念として Student クラスも取り上げました。このクラスは Person のすべての機能を継承しており、また Person とは異なる Teacher の greeting よりもはるかにくだけた greeting() メソッドを持っています。このセクションで生徒の挨拶がどのように見えるかを見て、Person() のすべての機能を継承し、異なる greeting() 関数を実装した独自の Student() コンストラクタを実装してみてください。

+ +
+

注記: もしここまでの例がうまく動作しないなら、あなたのコードと完成版(動作するライブ版も参照)を比較してみてください。

+
+ +

Object メンバーの概要

+ +

要約すると、気になるプロパティ/メソッドは4種類あります。

+ +
    +
  1. コンストラクタ関数の内部で定義され、オブジェクトインスタンスに与えられるもの。独自のカスタムコードでは、コンストラクタの内部で this.x = x 型の行を使用して定義されたメンバです。組み込みのブラウザコードでは、オブジェクトインスタンス (通常は new キーワードを使用してコンストラクタを呼び出すことで作成されます。例:  let myInstance = new myConstructor()) のみが利用可能なメンバです
  2. +
  3. コンストラクタ自身で直接定義されたもので、コンストラクタ上でのみ利用可能なもの。これらは一般的に組み込みのブラウザオブジェクトでのみ利用可能であり、インスタンスではなくコンストラクタに直接連結されていることで認識されます。たとえば Object.keys() などです。これらは静的プロパティ/メソッドとしても知られています
  4. +
  5. コンストラクタのプロトタイプに定義されているもので、すべてのインスタンスに継承され、オブジェクトクラスを継承しているもの。これらには、コンストラクタの prototype プロパティに定義されている任意のメンバ (例: myConstructor.prototype.x()) が含まれます
  6. +
  7. これらは、上で見たようにコンストラクタがインスタンス化されたときに作成されるオブジェクト (例えば let teacher1 = new Teacher('Chris'); の後に teacher1.name)、またはオブジェクトリテラル (let teacher1 = { name : 'Chris' } の後に teacher1.name) のいずれかであることができます
  8. +
+ +

もしどれがどれを指すかを区別できないのであれば、まだ気にしないでください — あなたはまだ学習中で、実践を通じて精通することでしょう。

+ +

ECMAScript 2015 のクラス

+ +

ECMAScript 2015では、C++ や Java のクラスに似た、より簡単で洗練された構文を使用して再利用可能なクラスを記述する方法として、JavaScript にクラス構文を導入しています。このセクションでは、Person と Teacher の例をプロトタイプの継承からクラスに変更して、どのようにして行うかを示します。

+ +
+

メモ: この近代的なクラスの作成方法は現在のすべてのブラウザでサポートされていますが、この構文をサポートしていないブラウザ (特に Internet Explorer) をサポートする必要があるプロジェクトで作業する場合に備えて、基本的なプロトタイプの継承の仕組みについて知っておくことはまだ価値があります。

+
+ +

Person の例を class-style で書き直したバージョンを見てみましょう:

+ +
class Person {
+  constructor(first, last, age, gender, interests) {
+    this.name = {
+      first,
+      last
+    };
+    this.age = age;
+    this.gender = gender;
+    this.interests = interests;
+  }
+
+  greeting() {
+    console.log(`Hi! I'm ${this.name.first}`);
+  };
+
+  farewell() {
+    console.log(`${this.name.first} has left the building. Bye for now!`);
+  };
+}
+ +

classステートメントは、新しいクラスを作成していることを示します。 このブロックの中で、クラスのすべての機能を定義します。

+ + + +

以前と同じようにnew演算子を使用してオブジェクトインスタンスをインスタンス化できるようになりました。

+ +
let han = new Person('Han', 'Solo', 25, 'male', ['Smuggling']);
+han.greeting();
+// Hi! I'm Han
+
+let leia = new Person('Leia', 'Organa', 19, 'female', ['Government']);
+leia.farewell();
+// Leia has left the building. Bye for now
+ +
+

メモ: 内部ではクラスはプロトタイプの継承モデルに変換されています。これはシンタックスシュガーです。しかし、私たちはあなたがそれを書く方が簡単だと考えるだろうと確信しています。

+
+ +

クラス構文による継承

+ +

上記では人を表すクラスを作成しました。彼らはすべての人々に共通の一連の属性を持っています。このセクションでは、特殊なTeacherクラスを作成し、現代のクラス構文を使用してPersonから継承します。これはサブクラス、またはサブクラスの作成と呼ばれます。
+
+ サブクラスを作成するには extends キーワードを使用して、クラスの基底となるクラスをJavaScriptに通知します。

+ +
constructor(first, last, age, gender, interests) {
+   this.name = {
+     first,
+     last
+   };
+   this.age = age;
+   this.gender = gender;
+   this.interests = interests;
+} 
+ +

super() 演算子を constructor() 内の最初の項目として定義することで、コードをより読みやすくすることができます。これは親クラスのコンストラクタを呼び出し、そこに定義されている限り、指定したメンバーをsuper() のパラメータとして継承します。

+ +
class Teacher extends Person {
+  constructor(first, last, age, gender, interests, subject, grade) {
+    super(first, last, age, gender, interests);
+
+    // 科目と学年は教師によって決まっている
+    this.subject = subject;
+    this.grade = grade;
+  }
+}
+ +

Teacherのオブジェクトをインスタンス化するときには、TeacherPersonの両方で定義されたメソッドとプロパティを呼び出すことができます。

+ +
let snape = new Teacher('Severus', 'Snape', 58, 'male', ['Potions'], 'Dark arts', 5);
+snape.greeting(); // Hi! I'm Severus.
+snape.farewell(); // Severus has left the building. Bye for now.
+snape.age // 58
+snape.subject; // Dark arts
+ +

Teachers と同じように、基本クラスを変更せずに Person をさらに特化したものにするために、他のサブクラスを作成できます。

+ +
+

メモ: この例は、GitHub で es2015-class-inheritance.html として見つけることができます(これも実際に参照してください)。

+
+ +

Getter と Setter

+ +

作成するクラスの属性の値を変更したい場合や、属性の最終値がわからない場合があります。Teacher の例を使用すると、教師が教材を作成する前にどの教科を教えるか分からないことがあります。
+
+ getter や setter でこのような状況を処理できます。
+
+ getter と setter で Teacher クラスを強化しましょう。私たちが最後に見たときと同じようにクラスが始まります。
+
+ getter と setter はペアで動作します。getter は変数の現在の値を返し、対応する setter は変数の値をひとつの値に変更します。
+
+ 変更された Teacher クラスは次のようになります。

+ +
class Teacher extends Person {
+  constructor(first, last, age, gender, interests, subject, grade) {
+    super(first, last, age, gender, interests);
+    // 科目と学年は教師によって決まっている
+    this._subject = subject;
+    this.grade = grade;
+  }
+
+  get subject() {
+    return this._subject;
+  }
+
+  set subject(newSubject) {
+    this._subject = newSubject;
+  }
+}
+ +

上のクラスでは、subject プロパティの getter と setter があります。 Nameプロパティを格納するために _ を使用して別の値を作成します。この規約を使用しないと、get または set を呼び出すたびにエラーが発生します。 この時点で:

+ + + +

以下の例は、動作している2つの機能を示しています。

+ +
// デフォルトの値をチェックする
+console.log(snape.subject) // Returns "Dark arts"
+
+// 値を変更する
+snape.subject = "Balloon animals" // Sets _subject to "Balloon animals"
+
+// 新しい値と一致しているか再度チェックする
+console.log(snape.subject) // Returns "Balloon animals"
+ +
+

メモ: この例は、GitHub で es2015-class-inheritance.html として見つけることができます(これも実際に参照してください)。

+
+ +
+

メモ: ゲッターやセッターは、プロパティが要求されたり設定されたりするたびにコードを実行したい場合など、非常に便利な場合があります。しかし、単純なケースでは、ゲッターやセッターを使用しないプレーンなプロパティアクセスで十分です。

+
+ +

JavaScript でいつ継承を使用するのでしょうか?

+ +

特にこの最後の記事を読み終えた後、「うーん、これはややこしいな。」と考えることでしょう。ええ、それは正しい感想です。プロトタイプと継承は JavaScript のもっとも複雑な面のいくつかに当たります、しかし多くの JavaScript の能力と柔軟性はそのオブジェクトの構造と継承に由来します、そしてそれがどのように動作するかは理解するに値します。

+ +

ある意味では、常に継承を使用しています。Web API の様々な機能、文字列や配列といったブラウザーに組み込まれたオブジェクトで定義されているメソッド/プロパティを使用するときはいつも、暗黙の内に継承を使用しています。

+ +

コードに継承を使用していることに関して、特に開始時には、そして小さなプロジェクトでは多分頻繁には使っていないでしょう。不要にも関わらず、継承のためだけにオブジェクトおよび継承を使用するのは時間の浪費です。しかしコードの母体が大きくなればなるほど、継承についての必要性が目に付きやすくなってきます。同じような機能を持ついくつものオブジェクトを作成していることに気付いた場合は、共有機能を持つ汎化オブジェクトタイプを作成し、特化オブジェクトタイプでそれらの機能を継承させるのがお手軽であり、便利です。

+ +
+

注記: プロトタイプチェーンなどを使って JavaScript が動作する方法のために、オブジェクト間での機能の共有をしばしば 移譲 と呼ぶ事があります。特化オブジェクトは汎化オブジェクトタイプから機能的に移譲されています。

+
+ +

継承を使用している時、継承をやたら多いレベルに行わないように、メソッドとプロパティをどこに定義したかを注意深く追跡し続けるようにアドバイスされるでしょう。組み込みブラウザーのオブジェクトのプロトタイプを一時的に変更するコードを書き始めることは可能ですが、実際に良い理由がないのであれば、そうすべきではありません。過剰な継承は終わりない混乱や、そんなコードをデバッグする時は終わりない痛みに繋がりかねません。

+ +

究極的には、オブジェクトは関数やループのような、自身の固有の役割や長所を活かした、コードの再利用の単なる別の形でもあります。もし関連のある変数や関数の一団を作成していることに気付き、それらすべてを追跡して適切にパッケージ化したいのであれば、オブジェクトは良いアイデアです。オブジェクトはまた、ある所から別の所にデータの集合を渡したい場合にも大変便利です。これらの事柄の両方がコンストラクタや継承を使用する事なく達成できます。もしオブジェクトの単一のインスタンスが必要なだけであれば、オブジェクトリテラルを使用するのが多分より良く、確実に継承は必要ないでしょう。

+ +

プロトタイプチェーンを拡張するための代替案

+ +

JavaScript では、上で示したものとは別に、オブジェクトのプロトタイプを拡張する方法がいくつかあります。その他の方法についての詳細は、継承とプロトタイプチェーンの記事を参照してください。

+ +

あなたのスキルをテストしてみましょう!

+ +

この記事はここまでですが、最も重要な情報を覚えていますか?先に進む前に、この情報を保持しているかどうかを確認するためのテストがいくつかあります - あなたのスキルをテストする: オブジェクト指向 JavaScript を参照してください。

+ +

まとめ

+ +

この記事は今知っておくべき考えられる OOJS の核となる理論および文法の残りの部分をカバーしています。この時点で、 JavaScript オブジェクトおよび オブジェクト指向プログラミングの基本、プロトタイプとプロトタイプにおける継承、クラス(コンストラクタ)とオブジェクトのインスタンスの生成、クラスへの機能の追加、他のクラスから継承されたサブクラスの生成をどのように行うか、を理解しているでしょう。

+ +

次の記事では JavaScript Object Notaion (JSON) 、つまり  JavaScript オブジェクトを使用して書かれた共通データ交換フォーマット、がどのように動作するかをを見て行きましょう。

+ +

あわせて参照

+ + + +

{{PreviousMenuNext("Learn/JavaScript/Objects/Object_prototypes", "Learn/JavaScript/Objects/JSON", "Learn/JavaScript/Objects")}}

+ +

このモジュール

+ + diff --git a/files/ja/learn/javascript/objects/inheritance/index.html b/files/ja/learn/javascript/objects/inheritance/index.html deleted file mode 100644 index 2964fbe786..0000000000 --- a/files/ja/learn/javascript/objects/inheritance/index.html +++ /dev/null @@ -1,412 +0,0 @@ ---- -title: JavaScript での継承 -slug: Learn/JavaScript/Objects/Inheritance -tags: - - Article - - CodingScripting - - Inheritance - - JavaScript - - OOJS - - OOP - - Object - - Prototype - - 'l10n:priority' - - 初心者 - - 学習 -translation_of: Learn/JavaScript/Objects/Inheritance ---- -
{{LearnSidebar}}
- -
{{PreviousMenuNext("Learn/JavaScript/Objects/Object_prototypes", "Learn/JavaScript/Objects/JSON", "Learn/JavaScript/Objects")}}
- -

OOJS のぞっとするような細部はほとんど説明されたので、ここでは”親”クラスからの機能を継承する”子供”のオブジェクトクラス (コンストラクタ) の生成方法について解説します。さらに、いつ、どこで OOJS を使うかについてのアドバイスを提示し、最新の ECMAScript の構文でクラスがどのように扱われるかを見ていきます。

- - - - - - - - - - - - -
前提知識基本的なコンピュータの知識および利用能力、HTML と CSS への基本的な理解、JavaScript の基本 (第一歩構成要素を参照) と OOJS の基本 (オブジェクト入門) に慣れている。
目的:JavaScript でどのように継承ができるようになっているかを理解していること。
- -

プロトタイプでの継承

- -

ここまで動作している継承 ー プロトタイプチェーンがどのように動作するか、どのようにメンバーが繋がるチェーンから継承されるのかを見てきました。しかし、これらの大半はブラウザーの組み込み関数で実行されています。我々が他のオブジェクトから継承したオブジェクトを作成するには JavaScript でどのようにするのでしょうか。

- -

具体的な例を使ってどのようの継承が行われているかを見てゆきましょう。

- -

さあ始めてみよう

- -

まず、oojs-class-inheritance-start.html ファイルをローカルにコピーしましょう (あるいはライブ版の実行でも確認できます)。ここでこのモジュールで幅広く使用されてきた Person() というコンストラクタの例を見つけることができます。わずかな違いがあって、コンストラクタ内部にプロパティのみが定義されています。

- -
function Person(first, last, age, gender, interests) {
-  this.name = {
-    first,
-    last
-  };
-  this.age = age;
-  this.gender = gender;
-  this.interests = interests;
-};
- -

メソッドはすべてコンストラクタのプロトタイプとして定義されています。例えば、

- -
Person.prototype.greeting = function() {
-  alert('Hi! I\'m ' + this.name.first + '.');
-};
- -
-

注意: ソースコードに、bio() と farewell() が定義されています。後ほどこれらのメソッドがどのようにほかのコンストラクタで継承されるのかを確認します。

-
- -

Teacher クラスを作成したい場合を考えましょう。これは最初のオブジェクト指向の特徴にて述べたもののようなクラスで、Person からすべてのメンバーを継承しますが、次のものも内包しています。

- -
    -
  1. 新しいプロパティの subject — これはその先生の教える科目を格納します。
  2. -
  3. 上書きされた greeting() メソッド、標準の greeting() メソッドよりわずかに固く感じられる — 学校で生徒に語りかける先生により相応しい。
  4. -
- -

Teacher() コンストラクタの機能を定義しよう

- -

われわれのまずすべき事は Teacher() コンストラクタを作成する事です — 以下に続くコードを既存コードの下に追加してください。

- -
function Teacher(first, last, age, gender, interests, subject) {
-  Person.call(this, first, last, age, gender, interests);
-
-  this.subject = subject;
-}
- -

これは多くの点で Person コンストラクタと似ていますが、これまでに見てきたものと異なったものがあります—  call() 関数です。この関数は基本的に別の場所で定義された関数を、しかし現在のコンテキストで呼び出すことができます。最初の引数は関数を実行するときに使用したい this の値を指定します、また他の引数は実行される関数に渡されるべき値です。

- -

Teacher() コンストラクタは継承元の Person() コンストラクタと同じ引数を取りたいため、 call() を呼び出して、すべての引き数を引数として渡します。

- -

コンストラクタの最後の行は、先生が行うべきであり、一般の人が持たない新たな subject(授業) のプロパティを定義しています。

- -

注意として、下記のソースのように、このようにシンプルにも書けます。

- -
function Teacher(first, last, age, gender, interests, subject) {
-  this.name = {
-    first,
-    last
-  };
-  this.age = age;
-  this.gender = gender;
-  this.interests = interests;
-  this.subject = subject;
-}
- -

しかしながらこれはただ改めてプロパティを再定義しているだけで、 Person() から継承していません、そのため、説明しようとしたポイントが伝わりません。またコード行数が多くもなります。

- -

引数なしのコンストラクタからの継承

- -

もし継承したコンストラクタがパラメータからプロパティの値を取得しない場合、 call() の呼び出しで追加の引数を指定する必要がないことを示しておきます。そのため、例えば、このような本当にシンプルなものがある場合、

- -
function Brick() {
-  this.width = 10;
-  this.height = 20;
-}
- -

このように書くことで width と height プロパティを継承することができます(もちろん、下に挙げる数行のステップの様にすることもできます)。

- -
function BlueGlassBrick() {
-  Brick.call(this);
-
-  this.opacity = 0.5;
-  this.color = 'blue';
-}
- -

 call() の中に this だけを記載していることに注意して下さい— 引数を介して親より設定されるどのプロパティも継承しないので他の引数は不要です。

- -

Teacher()のプロトタイプ とコンストラクタの参照への設定方法

- -

今まではすべて順調でしたが、1点問題があります。新しいコンストラクタを定義して、その中に 1 つの prototype プロパティを持たせ、これはデフォルトでただ自身のコンストラクタ関数への参照を保持しています。Person のコンストラクタの prototype プロパティへのメソッド群は持っていません。このことを見てみたいのならば Object.getOwnPropertyNames(Teacher.prototype)  をテキスト入力フィールドや JavaScript コンソールへ入力を試してみてください。そして再度入力する時には、Teacher を Person で置き換えてみてください。新しいコンストラクタもそれらのメソッドを継承していません。このことを確認するために、Person.prototype.greeting と Teacher.prototype.greeting の出力結果を比べてみてください。Person()  のプロトタイプに定義されたメソッドを継承するために Teacher() を生成する必要があります。ではどのようにすればよいのでしょうか。

- -
    -
  1. 前回追加した部分の下に以下の行を追加してみましょう: -
    Teacher.prototype = Object.create(Person.prototype);
    - ここで我々に馴染み深い create() に再度助けてもらいましょう。この場合に新しいオブジェクトを作ってそれを Teacher.prototype の値とするのに使います。新しいオブジェクトは Person.prototype を自身のプロトタイプとして保持し、それがゆえに(必要となる時には) Person.prototype 上で利用できるすべてのメソッドを継承します。
  2. -
  3. 先に進む前にもう一つやることがあります。
    - 最後の行を追加した後、Teacher.prototypeconstructor プロパティは Person() と同じになりました。なぜなら、Person.prototype からプロパティを継承するオブジェクトを参照するように Teacher.prototype を設定しただけだからです。コードを保存し、ブラウザでページを読み込み、コンソールに Teacher.prototype.constructor と入力して確認してみてください。
  4. -
  5. これは問題になるかもしれません、なので以下の内容をすぐに設定しましょう。 ソースコードにまた戻って最後に以下の行を追加しましょう。 -
    Object.defineProperty(Teacher.prototype, 'constructor', {
    -    value: Teacher,
    -    enumerable: false, // 'for in'ループで現れないようにする
    -    writable: true });
    -
  6. -
  7. ソースコードを保存およびページの再読み込みを行って、 Teacher.prototype.constructor と入力したならば Teacher() と返すでしょう、希望した通りに Person() から継承することができました!
  8. -
- -

Teacher() に greeting() ファンクションを付け加える

- -

コードを完成させる前に、Teacher() コンストラクタに新たに greeting() 関数を追加する必要があります。

- -

このようにする一番簡単な方法は Teacher() のプロトタイプに定義することです — コードの最後に以下のコードを追加します。

- -
Teacher.prototype.greeting = function() {
-  let prefix;
-
-  if (this.gender === 'male' || this.gender === 'Male' || this.gender === 'm' || this.gender === 'M') {
-    prefix = 'Mr.';
-  } else if (this.gender === 'female' || this.gender === 'Female' || this.gender === 'f' || this.gender === 'F') {
-    prefix = 'Mrs.';
-  } else {
-    prefix = 'Mx.';
-  }
-
-  alert('Hello. My name is ' + prefix + ' ' + this.name.last + ', and I teach ' + this.subject + '.');
-};
- -

これは性別を基にした適切な敬称を使う教師の挨拶を通知します。条件文を使うことで実現します。

- -

例を試してみよう

- -

これまでのコードをすべて入力し終えているなら、ソースコード(もしくはあなたの用意した同じようなコードに)の最後に続けて Teacher() からオブジェクトインスタンスを生成してみましょう。

- -
let teacher1 = new Teacher('Dave', 'Griffiths', 31, 'male', ['football', 'cookery'], 'mathematics');
- -

保存し、再読み込みをしたなら、新たな teacher1 オブジェクトのプロパティとメソッドにアクセスしてみましょう、例えば。

- -
teacher1.name.first;
-teacher1.interests[0];
-teacher1.bio();
-teacher1.subject;
-teacher1.greeting();
-teacher1.farewell();
- -

これらはすべて正常に動作するはずです。1, 2, 3, 6 行目のクエリは、ジェネリックな Person() コンストラクタ (クラス) から継承されたメンバにアクセスします。4 行目のクエリは、より特殊な Teacher() コンストラクタ (クラス) でのみ利用可能なメンバにアクセスしています。5 行目のクエリは Person() から継承したメンバにアクセスしていますが、Teacher() には同じ名前の独自のメンバがあるため、そのメンバにアクセスしています。

- -
-

注記: もしここまでの例がうまく動作しないなら、あなたのコードと完成版(ライブ版も参照)を比較してみてください。

-
- -

ここで述べている手法は JavaScript でクラスを継承する唯一の方法ではなく、問題なく動作し、JavaScript でのどのように実装するかの良いアイデアを提示しています。

- -

また JavaScript でより明瞭に継承を行えるようにした新しい{{glossary("ECMAScript")}}の機能(Classes を参照)にも興味を持つかもしれません。ここではそれらについて言及はしませんでした、それはまだブラウザー間で幅広くサポートされていないためです。一連の記事で検討してきた他のコード構造はすべて、IE9 やそれ以前のバージョンといった、はるか以前よりサポートされており、それより早くからのサポートを確認する方法となります。

- -

一般的な方法は JavaScript ライブラリを使用することです — よく知られた選択肢のうちの大部分は、よりたやすく素早く利用できる簡易な機能セットを持っています。例えば CoffeeScript は class, extends などを提供します。

- -

追加の特訓

- -

OOP 理論のセクションでは、概念として Student クラスも取り上げました。このクラスは Person のすべての機能を継承しており、また Person とは異なる Teacher の greeting よりもはるかにくだけた greeting() メソッドを持っています。このセクションで生徒の挨拶がどのように見えるかを見て、Person() のすべての機能を継承し、異なる greeting() 関数を実装した独自の Student() コンストラクタを実装してみてください。

- -
-

注記: もしここまでの例がうまく動作しないなら、あなたのコードと完成版(動作するライブ版も参照)を比較してみてください。

-
- -

Object メンバーの概要

- -

要約すると、気になるプロパティ/メソッドは4種類あります。

- -
    -
  1. コンストラクタ関数の内部で定義され、オブジェクトインスタンスに与えられるもの。独自のカスタムコードでは、コンストラクタの内部で this.x = x 型の行を使用して定義されたメンバです。組み込みのブラウザコードでは、オブジェクトインスタンス (通常は new キーワードを使用してコンストラクタを呼び出すことで作成されます。例:  let myInstance = new myConstructor()) のみが利用可能なメンバです
  2. -
  3. コンストラクタ自身で直接定義されたもので、コンストラクタ上でのみ利用可能なもの。これらは一般的に組み込みのブラウザオブジェクトでのみ利用可能であり、インスタンスではなくコンストラクタに直接連結されていることで認識されます。たとえば Object.keys() などです。これらは静的プロパティ/メソッドとしても知られています
  4. -
  5. コンストラクタのプロトタイプに定義されているもので、すべてのインスタンスに継承され、オブジェクトクラスを継承しているもの。これらには、コンストラクタの prototype プロパティに定義されている任意のメンバ (例: myConstructor.prototype.x()) が含まれます
  6. -
  7. これらは、上で見たようにコンストラクタがインスタンス化されたときに作成されるオブジェクト (例えば let teacher1 = new Teacher('Chris'); の後に teacher1.name)、またはオブジェクトリテラル (let teacher1 = { name : 'Chris' } の後に teacher1.name) のいずれかであることができます
  8. -
- -

もしどれがどれを指すかを区別できないのであれば、まだ気にしないでください — あなたはまだ学習中で、実践を通じて精通することでしょう。

- -

ECMAScript 2015 のクラス

- -

ECMAScript 2015では、C++ や Java のクラスに似た、より簡単で洗練された構文を使用して再利用可能なクラスを記述する方法として、JavaScript にクラス構文を導入しています。このセクションでは、Person と Teacher の例をプロトタイプの継承からクラスに変更して、どのようにして行うかを示します。

- -
-

メモ: この近代的なクラスの作成方法は現在のすべてのブラウザでサポートされていますが、この構文をサポートしていないブラウザ (特に Internet Explorer) をサポートする必要があるプロジェクトで作業する場合に備えて、基本的なプロトタイプの継承の仕組みについて知っておくことはまだ価値があります。

-
- -

Person の例を class-style で書き直したバージョンを見てみましょう:

- -
class Person {
-  constructor(first, last, age, gender, interests) {
-    this.name = {
-      first,
-      last
-    };
-    this.age = age;
-    this.gender = gender;
-    this.interests = interests;
-  }
-
-  greeting() {
-    console.log(`Hi! I'm ${this.name.first}`);
-  };
-
-  farewell() {
-    console.log(`${this.name.first} has left the building. Bye for now!`);
-  };
-}
- -

classステートメントは、新しいクラスを作成していることを示します。 このブロックの中で、クラスのすべての機能を定義します。

- - - -

以前と同じようにnew演算子を使用してオブジェクトインスタンスをインスタンス化できるようになりました。

- -
let han = new Person('Han', 'Solo', 25, 'male', ['Smuggling']);
-han.greeting();
-// Hi! I'm Han
-
-let leia = new Person('Leia', 'Organa', 19, 'female', ['Government']);
-leia.farewell();
-// Leia has left the building. Bye for now
- -
-

メモ: 内部ではクラスはプロトタイプの継承モデルに変換されています。これはシンタックスシュガーです。しかし、私たちはあなたがそれを書く方が簡単だと考えるだろうと確信しています。

-
- -

クラス構文による継承

- -

上記では人を表すクラスを作成しました。彼らはすべての人々に共通の一連の属性を持っています。このセクションでは、特殊なTeacherクラスを作成し、現代のクラス構文を使用してPersonから継承します。これはサブクラス、またはサブクラスの作成と呼ばれます。
-
- サブクラスを作成するには extends キーワードを使用して、クラスの基底となるクラスをJavaScriptに通知します。

- -
constructor(first, last, age, gender, interests) {
-   this.name = {
-     first,
-     last
-   };
-   this.age = age;
-   this.gender = gender;
-   this.interests = interests;
-} 
- -

super() 演算子を constructor() 内の最初の項目として定義することで、コードをより読みやすくすることができます。これは親クラスのコンストラクタを呼び出し、そこに定義されている限り、指定したメンバーをsuper() のパラメータとして継承します。

- -
class Teacher extends Person {
-  constructor(first, last, age, gender, interests, subject, grade) {
-    super(first, last, age, gender, interests);
-
-    // 科目と学年は教師によって決まっている
-    this.subject = subject;
-    this.grade = grade;
-  }
-}
- -

Teacherのオブジェクトをインスタンス化するときには、TeacherPersonの両方で定義されたメソッドとプロパティを呼び出すことができます。

- -
let snape = new Teacher('Severus', 'Snape', 58, 'male', ['Potions'], 'Dark arts', 5);
-snape.greeting(); // Hi! I'm Severus.
-snape.farewell(); // Severus has left the building. Bye for now.
-snape.age // 58
-snape.subject; // Dark arts
- -

Teachers と同じように、基本クラスを変更せずに Person をさらに特化したものにするために、他のサブクラスを作成できます。

- -
-

メモ: この例は、GitHub で es2015-class-inheritance.html として見つけることができます(これも実際に参照してください)。

-
- -

Getter と Setter

- -

作成するクラスの属性の値を変更したい場合や、属性の最終値がわからない場合があります。Teacher の例を使用すると、教師が教材を作成する前にどの教科を教えるか分からないことがあります。
-
- getter や setter でこのような状況を処理できます。
-
- getter と setter で Teacher クラスを強化しましょう。私たちが最後に見たときと同じようにクラスが始まります。
-
- getter と setter はペアで動作します。getter は変数の現在の値を返し、対応する setter は変数の値をひとつの値に変更します。
-
- 変更された Teacher クラスは次のようになります。

- -
class Teacher extends Person {
-  constructor(first, last, age, gender, interests, subject, grade) {
-    super(first, last, age, gender, interests);
-    // 科目と学年は教師によって決まっている
-    this._subject = subject;
-    this.grade = grade;
-  }
-
-  get subject() {
-    return this._subject;
-  }
-
-  set subject(newSubject) {
-    this._subject = newSubject;
-  }
-}
- -

上のクラスでは、subject プロパティの getter と setter があります。 Nameプロパティを格納するために _ を使用して別の値を作成します。この規約を使用しないと、get または set を呼び出すたびにエラーが発生します。 この時点で:

- - - -

以下の例は、動作している2つの機能を示しています。

- -
// デフォルトの値をチェックする
-console.log(snape.subject) // Returns "Dark arts"
-
-// 値を変更する
-snape.subject = "Balloon animals" // Sets _subject to "Balloon animals"
-
-// 新しい値と一致しているか再度チェックする
-console.log(snape.subject) // Returns "Balloon animals"
- -
-

メモ: この例は、GitHub で es2015-class-inheritance.html として見つけることができます(これも実際に参照してください)。

-
- -
-

メモ: ゲッターやセッターは、プロパティが要求されたり設定されたりするたびにコードを実行したい場合など、非常に便利な場合があります。しかし、単純なケースでは、ゲッターやセッターを使用しないプレーンなプロパティアクセスで十分です。

-
- -

JavaScript でいつ継承を使用するのでしょうか?

- -

特にこの最後の記事を読み終えた後、「うーん、これはややこしいな。」と考えることでしょう。ええ、それは正しい感想です。プロトタイプと継承は JavaScript のもっとも複雑な面のいくつかに当たります、しかし多くの JavaScript の能力と柔軟性はそのオブジェクトの構造と継承に由来します、そしてそれがどのように動作するかは理解するに値します。

- -

ある意味では、常に継承を使用しています。Web API の様々な機能、文字列や配列といったブラウザーに組み込まれたオブジェクトで定義されているメソッド/プロパティを使用するときはいつも、暗黙の内に継承を使用しています。

- -

コードに継承を使用していることに関して、特に開始時には、そして小さなプロジェクトでは多分頻繁には使っていないでしょう。不要にも関わらず、継承のためだけにオブジェクトおよび継承を使用するのは時間の浪費です。しかしコードの母体が大きくなればなるほど、継承についての必要性が目に付きやすくなってきます。同じような機能を持ついくつものオブジェクトを作成していることに気付いた場合は、共有機能を持つ汎化オブジェクトタイプを作成し、特化オブジェクトタイプでそれらの機能を継承させるのがお手軽であり、便利です。

- -
-

注記: プロトタイプチェーンなどを使って JavaScript が動作する方法のために、オブジェクト間での機能の共有をしばしば 移譲 と呼ぶ事があります。特化オブジェクトは汎化オブジェクトタイプから機能的に移譲されています。

-
- -

継承を使用している時、継承をやたら多いレベルに行わないように、メソッドとプロパティをどこに定義したかを注意深く追跡し続けるようにアドバイスされるでしょう。組み込みブラウザーのオブジェクトのプロトタイプを一時的に変更するコードを書き始めることは可能ですが、実際に良い理由がないのであれば、そうすべきではありません。過剰な継承は終わりない混乱や、そんなコードをデバッグする時は終わりない痛みに繋がりかねません。

- -

究極的には、オブジェクトは関数やループのような、自身の固有の役割や長所を活かした、コードの再利用の単なる別の形でもあります。もし関連のある変数や関数の一団を作成していることに気付き、それらすべてを追跡して適切にパッケージ化したいのであれば、オブジェクトは良いアイデアです。オブジェクトはまた、ある所から別の所にデータの集合を渡したい場合にも大変便利です。これらの事柄の両方がコンストラクタや継承を使用する事なく達成できます。もしオブジェクトの単一のインスタンスが必要なだけであれば、オブジェクトリテラルを使用するのが多分より良く、確実に継承は必要ないでしょう。

- -

プロトタイプチェーンを拡張するための代替案

- -

JavaScript では、上で示したものとは別に、オブジェクトのプロトタイプを拡張する方法がいくつかあります。その他の方法についての詳細は、継承とプロトタイプチェーンの記事を参照してください。

- -

あなたのスキルをテストしてみましょう!

- -

この記事はここまでですが、最も重要な情報を覚えていますか?先に進む前に、この情報を保持しているかどうかを確認するためのテストがいくつかあります - あなたのスキルをテストする: オブジェクト指向 JavaScript を参照してください。

- -

まとめ

- -

この記事は今知っておくべき考えられる OOJS の核となる理論および文法の残りの部分をカバーしています。この時点で、 JavaScript オブジェクトおよび オブジェクト指向プログラミングの基本、プロトタイプとプロトタイプにおける継承、クラス(コンストラクタ)とオブジェクトのインスタンスの生成、クラスへの機能の追加、他のクラスから継承されたサブクラスの生成をどのように行うか、を理解しているでしょう。

- -

次の記事では JavaScript Object Notaion (JSON) 、つまり  JavaScript オブジェクトを使用して書かれた共通データ交換フォーマット、がどのように動作するかをを見て行きましょう。

- -

あわせて参照

- - - -

{{PreviousMenuNext("Learn/JavaScript/Objects/Object_prototypes", "Learn/JavaScript/Objects/JSON", "Learn/JavaScript/Objects")}}

- -

このモジュール

- - diff --git a/files/ja/learn/javascript/objects/object-oriented_js/index.html b/files/ja/learn/javascript/objects/object-oriented_js/index.html deleted file mode 100644 index 387400f5b0..0000000000 --- a/files/ja/learn/javascript/objects/object-oriented_js/index.html +++ /dev/null @@ -1,291 +0,0 @@ ---- -title: 初心者のためのオブジェクト指向 JavaScript -slug: Learn/JavaScript/Objects/Object-oriented_JS -tags: - - Beginner - - Create - - JavaScript - - OOJS - - OOP - - オブジェクト - - オブジェクト指向 - - 学習 - - 記事 -translation_of: Learn/JavaScript/Objects/Object-oriented_JS ---- -
{{LearnSidebar}}
- -
{{PreviousMenuNext("Learn/JavaScript/Objects/Basics", "Learn/JavaScript/Objects/Object_prototypes", "Learn/JavaScript/Objects")}}
- -

基礎が片付いたところで、オブジェクト指向 JavaScript (OOJS) について取り上げます。この記事ではオブジェクト指向プログラミング (OOP) の基本的な視点を説明し、 JavaScript がどのようにコンストラクター関数を通じてオブジェクトクラスをエミュレートしているか、またどのようにオブジェクトインスタンスを生成しているかを紹介します。

- - - - - - - - - - - - -
前提知識:基礎的なコンピュータリテラシー、基礎的な HTML と CSS の理解、JavaScript (JavaScript の第一歩JavaScript の構成要素を参照) および OOJS (JavaScript オブジェクトの基本を参照)の基礎知識。
目標:オブジェクト指向プログラミングの基本理論、どのようにそれが JavaScript (「すべてはオブジェクトである」) に関連しているか、どのようにコンストラクターがオブジェクトインスタンスを生成しているかを理解する。
- -

オブジェクト指向プログラミング - その基本

- -

はじめに、オブジェクト指向プログラミング (OOP) とは何か、シンプルかつ高レベルな視点を提示します。シンプルと述べたのは、OOP はあっという間にひどく複雑になり得るためで、現時点でそのすべてを論じてしまうと、助けとなるよりもむしろ混乱を生んでしまうことでしょう。OOP の基本的な考え方は、プログラムの中で扱いたい、現実世界の事物を模るためにオブジェクトを使用すること、またはそうしなければ使うことが難しいあるいは不可能だった機能にアクセスするための、シンプルな方法を提供することです。

- -

オブジェクトは、モデル化しようとしている事物に関する情報および、持たせたい機能や動作を表現する、関連したデータとコードを持つことができます。オブジェクトのデータ (しばしば関数も含む) はオブジェクトのパッケージの中 (名前空間と呼ばれることがある) に適切に格納されます (カプセル化)。オブジェクトは一般に、ネットワークを通じて容易に送信することが可能な、データストアとしても使われます。

- -

オブジェクトのテンプレートを定義する

- -

学校の生徒と教師の情報を表示する、シンプルなプログラムを考えてみましょう。特定のプログラミング言語の文脈ではなく、OOP 一般の理論を眺めていきます。

- -

はじめに、オブジェクト入門の最初の記事にある、人物の包括的なデータや機能を定義した、Person オブジェクトに戻りましょう。ある人物について知り得る事柄は数多くあります (住所、身長、靴のサイズ、DNA 情報、パスポート番号、顕著な人格特性など) が、このケースでは名前、年齢、性別、趣味を表示することに興味があるだけです。また、このデータに基づいた短い自己紹介や、挨拶をさせられるようにもしましょう。これは抽象化 — より複雑な事物を、プログラムの目的に沿って簡単に操作できるように、その最も重要な側面を表現する、シンプルなモデルを作ること — として知られています。

- -

- -

実際のオブジェクトの生成

- -

このクラスから、オブジェクトインスタンスを生成することができます。オブジェクトインスタンスは、クラスで定義されたデータや機能を持ったオブジェクトです。 Person クラスから、何名かの実際の人物を生成します。

- -

- -

クラスからオブジェクトインスタンスが生成されるとき、クラスのコンストラクター関数が生成のために実行されます。クラスからオブジェクトインスタンスが生成される過程をインスタンス化と呼びます。オブジェクトインスタンスは、クラスをインスタンス化したものです。

- -

専門のクラス

- -

このケースで求めているのは、包括的な人物ではなく、より特定のタイプである、教師と生徒です。OOP では、他のクラスを元にした新しいクラスを作ることができます。これらの新しい子クラスは、親クラスからデータやコード機能を継承することができ、すべてのオブジェクトタイプに共通する機能を、重複させるのではなく、再利用することができます。クラス間で機能が異なる場合は、必要に応じて特殊化された機能を直接定義することができます。

- -

- -

これは実に役立ちます。教師と生徒は名前、性別、年齢のように多数の共通機能を共有しており、これらの機能を一度だけ定義すればいいので便利です。異なるクラスで、同じ機能を分けて定義することもでき、その機能の各定義は異なる名前空間に置かれます。例えば、生徒の挨拶は "Yo, I'm [firstName]" (例:Yo, I'm Sam) という形式とし、一方の教師の挨拶は、より形式的に "Hello, my name is [Prefix] [lastName], and I teach [Subject]." (例:Hello, My name is Mr Griffiths, and I teach Chemistry) のように。

- -
-

: 同じ機能を複数のオブジェクトタイプが実装する能力のことを示す用語に、ポリモーフィズムがあります。不思議に感じているかも知れないので念のため。

-
- -

子クラスのオブジェクトインスタンスを生成しましょう。例:

- -

- -

記事の続きでは、OOP 理論が JavaScript でどのように実践されているかを見ていきます。

- -

コンストラクターとオブジェクトインスタンス

- -

JavaScript では、オブジェクトやその機能を定義し初期化するためにコンストラクター関数と呼ばれる特殊な関数を使用します。これは便利です。なぜならオブジェクトをいくつ作成することになるか分からない状況に出くわすでしょうから。コンストラクターは必要な数のオブジェクトを効率的な方法で作成し、必要に応じてデータや関数を付加する手段を提供します。

- -

JavaScript でコンストラクターを通じてクラスを作り、そこからオブジェクトのインスタンスを生成するところを見ていきましょう。まずは、最初のオブジェクトの記事で見た oojs.html ファイルの新しいコピーを、ローカルに作成しておいてください。

- -

シンプルな例

- -
    -
  1. どのように通常の関数で人物を定義できるかを見てみるところから始めましょう。この関数を script 要素の中に加えてください。 - -
    function createNewPerson(name) {
    -  const obj = {};
    -  obj.name = name;
    -  obj.greeting = function() {
    -    alert('Hi! I\'m ' + obj.name + '.');
    -  };
    -  return obj;
    -}
    -
  2. -
  3. この関数を呼び出すことで、新しい人物を生成することができます。次の 3 行をブラウザーの JavaScript コンソールで試してみてください。 -
    const salva = createNewPerson('Salva');
    -salva.name;
    -salva.greeting();
    - これも十分上手くいっていますが、やや長ったらしいです。オブジェクトを生成したいと知っているなら、なぜ明示的に空のオブジェクトを生成し、返すことが必要なのでしょうか?幸いにも、 JavaScript はコンストラクター関数という形で、便利なショートカットを提供してくれます。早速作ってみましょう!
  4. -
  5. 前の関数を、以下のもので置き換えてください。 -
    function Person(name) {
    -  this.name = name;
    -  this.greeting = function() {
    -    alert('Hi! I\'m ' + this.name + '.');
    -  };
    -}
    -
  6. -
- -

コンストラクター関数は、 JavaScript 版のクラスです。それは関数に期待される全ての機能を持っていますが、何も返さないし、明示的にオブジェクトを生成しもしないという点に注意してください。基本的には、プロパティとメソッドを定義するだけです。加えて、 this キーワードが使われていることにも注意してください。基本、オブジェクトインスタンスの 1 つが作成されるときにはいつでも、オブジェクトの name プロパティはコンストラクター呼び出しに渡される name 値と等しくなり、 greeting() メソッドもコンストラクター呼び出しに渡される name 値を使用します。

- -
-

メモ: 通常、コンストラクター関数の名前は大文字で始まります。コードの中で、コンストラクター関数がより容易に認識されるようにするための慣習です。

-
- -

では、オブジェクトを生成するために、どのようにコンストラクターを呼び出したらよいでしょうか?

- -
    -
  1. 次の 2 行を、前のコードの続きに加えてください。 -
    let person1 = new Person('Bob');
    -let person2 = new Person('Sarah');
    -
  2. -
  3. コードを保存し、ブラウザーをリロードして、以下の 4 行を JavaScript コンソールに入れて試してみてください。 -
    person1.name
    -person1.greeting()
    -person2.name
    -person2.greeting()
    -
  4. -
- -

素晴らしい!2 つの新しいオブジェクトが、異なる名前空間の下でページに格納されていることが確認できます。それらのプロパティとメソッドにアクセスするときには、 person1 または person2 を呼び出すことから始めなければなりません。中に含まれている機能は適切にパッケージ化されており、他の機能と衝突しないようになっています。しかしながら、それらは同じように name プロパティと greeting() メソッドが利用可能です。 2 つのオブジェクトはそれぞれ、生成されたときに割り当てられた、自身の name 値を使っていることに注意してください。これが this を使うことがとても重要である理由の 1 つであり、他の値ではなく、自身の値を使っているのです。

- -

コンストラクターをもう一度呼び出してみましょう。

- -
let person1 = new Person('Bob');
-let person2 = new Person('Sarah');
- -

いずれのケースでも、新しいオブジェクトインスタンスを生成したいとブラウザーに伝えるために new キーワードが使われており、その後に、括弧に必要なパラメーターを入れた関数名が続き、その結果が変数に格納されていて、一般的な関数の呼ばれ方とよく似ています。どちらのインスタンスも、次の定義によって生成されています。

- -
function Person(name) {
-  this.name = name;
-  this.greeting = function() {
-    alert('Hi! I\'m ' + this.name + '.');
-  };
-}
- -

新しいオブジェクトが生成された後、 person1 および person2 変数は、次のオブジェクトを格納しています。

- -
{
-  name: 'Bob',
-  greeting: function() {
-    alert('Hi! I\'m ' + this.name + '.');
-  }
-}
-
-{
-  name: 'Sarah',
-  greeting: function() {
-    alert('Hi! I\'m ' + this.name + '.');
-  }
-}
- -

コンストラクター関数を呼び出すとき、毎回 greeting() メソッドを定義していることに注意してください。これは理想的ではありません。これを回避するために、代わりにプロトタイプに関数を定義することができます。後で見てみましょう。

- -

最終的なコンストラクターの作成

- -

上で見てきた例は、スタートのためのシンプルな例に過ぎません。次は最終的な Person() コンストラクター関数を作りましょう。

- -
    -
  1. ここまでに挿入したコードを削除し、代わりとなるコンストラクターを追加してください。これはシンプルな例とほぼ同じもので、ほんのわずか複雑になっているだけです。 -
    function Person(first, last, age, gender, interests) {
    -  this.name = {
    -     first : first,
    -     last : last
    -  };
    -  this.age = age;
    -  this.gender = gender;
    -  this.interests = interests;
    -  this.bio = function() {
    -    alert(this.name.first + ' ' + this.name.last + ' is ' + this.age + ' years old. He likes ' + this.interests[0] + ' and ' + this.interests[1] + '.');
    -  };
    -  this.greeting = function() {
    -    alert('Hi! I\'m ' + this.name.first + '.');
    -  };
    -}
    -
  2. -
  3. ではその下に、コンストラクターからオブジェクトインスタンスを生成するため、次の行を追加してください。 -
    let person1 = new Person('Bob', 'Smith', 32, 'male', ['music', 'skiing']);
    -
  4. -
- -

ちょうど以前行ったように、プロパティやメソッドにアクセスできることを確認できます。 JavaScript コンソールの中でやってみましょう。

- -
person1['age']
-person1.interests[1]
-person1.bio()
-// etc.
- -
-

メモ: もしこの工程で何らかのトラブルがあった場合は、あなたのコードを我々のバージョン (oojs-class-finished.htmlライブサンプルも) と比べてみてください。

-
- -

さらなる練習

- -

まずはじめに、さらにいくつかのオブジェクトを生成する独自の行を追加し、オブジェクトインスタンスのメンバーの取得や設定をしてみてください。

- -

加えて、 bio() メソッドにはいくつかの問題点があります。人物が女性である、あるいは他の優先される性別分類の場合でも、その出力には常に "He" という代名詞が含まれています。また、 bio は interests 配列により多くのものが列挙されていても、2 つの趣味しか含みません。このクラス定義 (コンストラクター) の問題を、あなたはどのように修正することができますか?コンストラクター内に任意のコード (恐らく、いくつかの条件分岐やループが必要となるでしょう) を入れてみてください。性別や、趣味の数が 1、2、あるいは 2 よりも多いかどうかによって、文がどのように構築されるべきか考えてみてください。

- -
-

: もし行き詰まってしまった場合は、GitHub に答えとなるリポジトリ (ライブ) があります。最初はあなた自身で書いてみてください!

-
- -

オブジェクトインスタンスを生成する他の方法

- -

ここまで、オブジェクトインスタンスを生成する 2 つの異なる方法を見てきました。オブジェクトリテラルの宣言と、上で見たコンストラクター関数の使用です。

- -

これで十分かもしれませんが、他にも方法はあります。ウェブを巡る際に遭遇したときに備えて、よく知っておいてください。

- -

Object() コンストラクター

- -

まず最初に、 Object() コンストラクターを新しいオブジェクトの生成のために使うことができます。はい、一般的なオブジェクトにも、空のオブジェクトを生成するコンストラクターがあります。

- -
    -
  1. このコードを JavaScript コンソールに入力してみましょう。 -
    let person1 = new Object();
    -
  2. -
  3. person1 変数に空のオブジェクトが格納されました。このオブジェクトに、ドット記法とブラケット記法を使ってプロパティを追加することができます。次の例を JavaScript コンソールで試してみましょう。 -
    person1.name = 'Chris';
    -person1['age'] = 38;
    -person1.greeting = function() {
    -  alert('Hi! I\'m ' + this.name + '.');
    -};
    -
  4. -
  5. あらかじめプロパティやメソッドを設定するため、Object() コンストラクターに引数としてオブジェクトリテラルを渡すことも可能です。次のコードを JavaScript コンソールで試してみてください。 -
    let person1 = new Object({
    -  name: 'Chris',
    -  age: 38,
    -  greeting: function() {
    -    alert('Hi! I\'m ' + this.name + '.');
    -  }
    -});
    -
  6. -
- -

create() メソッドの使用

- -

コードの順序についてもコンストラクターが助けとなります。コンストラクターを一箇所で作っておくと、必要に応じてインスタンスを生成することができ、それらがどこから来たものであるか、明瞭です。

- -

しかしながら、特に少数のインスタンスのみを生成する場合に、最初にコンストラクターを作らずにインスタンスを生成することを好む人もいます。JavaScript にはそれを可能とする、create() と呼ばれる組み込みメソッドがあります。それにより、既存のオブジェクトを基にして、新しいオブジェクトを生成することができます。

- -
    -
  1. 前のセクションの練習をブラウザーで終えた状態で、こちらを JavaScript コンソールで試してみてください。 -
    let person2 = Object.create(person1);
    -
  2. -
  3. 次は以下のコードです。 -
    person2.name;
    -person2.greeting();
    -
  4. -
- -

person1 を基に person2 が生成され、person2 では同じプロパティとメソッドが利用可能であることを確認することができます。

- -

create() には、IE8 が対応していないという制限があります。つまり、コンストラクターは古いブラウザーに対応したい場合により効果的かもしれません。

- -

いずれ、create() の効果についてより詳細に紹介するつもりです。

- -

あなたのスキルをテストしてみましょう!

- -

この記事はここまでですが、最も重要な情報を覚えていますか?先に進む前に、この情報を保持しているかどうかを確認するために、さらにいくつかのテストを見つけることができます。あなたのスキルをテストする: オブジェクト指向 JavaScript を参照してください。

- -

この一連のテストは次の記事で紹介する知識に依存していることに注意してください。なので、試してみる前に、まずそれを読んでみるといいかもしれません。

- -

まとめ

- -

この記事はオブジェクト指向の理論の概略を見てきました。これですべてではありませんが、ここで扱っていることに関する考えを提示しました。加えて、オブジェクトのインスタンスを生成する様々な方法を見始めたところです。

- -

次の記事では、 JavaScript オブジェクトのプロトタイプについて紹介します。

- -

{{PreviousMenuNext("Learn/JavaScript/Objects/Basics", "Learn/JavaScript/Objects/Object_prototypes", "Learn/JavaScript/Objects")}}

- -

このモジュール内の文書

- - diff --git a/files/ko/_redirects.txt b/files/ko/_redirects.txt index 05d11bdcbd..8769ccde13 100644 --- a/files/ko/_redirects.txt +++ b/files/ko/_redirects.txt @@ -314,6 +314,8 @@ /ko/docs/Learn/HTML/Howto/데이터_속성_사용하기 /ko/docs/Learn/HTML/Howto/Use_data_attributes /ko/docs/Learn/HTML/Multimedia_and_embedding/ideo_and_audio_content /ko/docs/Learn/HTML/Multimedia_and_embedding/Video_and_audio_content /ko/docs/Learn/JavaScript/Building_blocks/조건문 /ko/docs/Learn/JavaScript/Building_blocks/conditionals +/ko/docs/Learn/JavaScript/Objects/Inheritance /ko/docs/Learn/JavaScript/Objects/Classes_in_JavaScript +/ko/docs/Learn/JavaScript/Objects/Object-oriented_JS /ko/docs/conflicting/Learn/JavaScript/Objects/Classes_in_JavaScript /ko/docs/Learn/JavaScript/Objects/얼마나_이해했는지_확인해보자:_JSON /ko/docs/Learn/JavaScript/Objects/Test_your_skills:_JSON /ko/docs/Learn/Server-side/Express_Nodejs/개발_환경 /ko/docs/Learn/Server-side/Express_Nodejs/development_environment /ko/docs/Learn/Server-side/Express_Nodejs/스켈레톤_웹사이트 /ko/docs/Learn/Server-side/Express_Nodejs/skeleton_website diff --git a/files/ko/_wikihistory.json b/files/ko/_wikihistory.json index ddd5aa5b31..b4a8b6a3ff 100644 --- a/files/ko/_wikihistory.json +++ b/files/ko/_wikihistory.json @@ -2144,7 +2144,7 @@ "dsma73" ] }, - "Learn/JavaScript/Objects/Inheritance": { + "Learn/JavaScript/Objects/Classes_in_JavaScript": { "modified": "2020-07-16T22:32:15.138Z", "contributors": [ "youngdeok", @@ -2164,15 +2164,6 @@ "ahnzaz" ] }, - "Learn/JavaScript/Objects/Object-oriented_JS": { - "modified": "2020-07-16T22:32:06.697Z", - "contributors": [ - "alattalatta", - "beowolf9", - "lunarlit", - "dsma73" - ] - }, "Learn/JavaScript/Objects/Object_prototypes": { "modified": "2020-07-16T22:32:21.572Z", "contributors": [ @@ -17476,6 +17467,15 @@ "JaehaAhn" ] }, + "conflicting/Learn/JavaScript/Objects/Classes_in_JavaScript": { + "modified": "2020-07-16T22:32:06.697Z", + "contributors": [ + "alattalatta", + "beowolf9", + "lunarlit", + "dsma73" + ] + }, "conflicting/MDN/Tools": { "modified": "2019-01-16T20:50:22.654Z", "contributors": [ diff --git a/files/ko/conflicting/learn/javascript/objects/classes_in_javascript/index.html b/files/ko/conflicting/learn/javascript/objects/classes_in_javascript/index.html new file mode 100644 index 0000000000..bfee97a562 --- /dev/null +++ b/files/ko/conflicting/learn/javascript/objects/classes_in_javascript/index.html @@ -0,0 +1,288 @@ +--- +title: Object-oriented JavaScript for beginners +slug: conflicting/Learn/JavaScript/Objects/Classes_in_JavaScript +tags: + - Article + - Beginner + - CodingScripting + - JavaScript + - Learn + - l10n:priority +translation_of: Learn/JavaScript/Objects/Object-oriented_JS +original_slug: Learn/JavaScript/Objects/Object-oriented_JS +--- +
{{LearnSidebar}}
+ +
{{PreviousMenuNext("Learn/JavaScript/Objects/Basics", "Learn/JavaScript/Objects/Object_prototypes", "Learn/JavaScript/Objects")}}
+ +

자, 이제 기초 단계를 벗어나서,객체지향 JavaScript (OOJS) 을 보도록 하죠 — 이 문서에서 객체지향 (OOP) 이론에 대한 기초를 훑어본 후, 자바스크립트가 생성자와 함수를 통해 객체 클래스 개념을 따라했는지, 그리고 어떻게 객체를 만드는지 알아볼겁니다.

+ + + + + + + + + + + + +
선수조건:컴퓨터 기본지식, HTML과 CSS에 대한 기본적인 이해,자바스크립트에 어느 정도 익숙할 것 (see First steps and Building blocks).  OOJS 기초 지식 (see Introduction to objects).
학습목표:객체지향에 대한 기본 지식을 습득 하고, 객체 지향이 자바스크립트에 어떻게 적용되었는지 ( "모든 것은 객체다") 와 어떻게 생성자와 객체 인스턴스를 만드는지에 대해 이해한다.
+ +

객체지향 프로그래밍 — 기초

+ +

객체지향 프로그래밍(OOP)의 개요를 설명하는 것으로 시작하겠습니다. 지금 단계에서 OOP의 모든 것을 설명면 너무 복잡해서 혼란만을 가중시킬 것이기 때문에 최대한 간단히 설명하겠습니다. OOP의 기본 컨셉은 프로그램 내에서 표현하고자 하는 실 세계(real world)의 일들을 객체를 사용해서 모델링 하고, 객체를 사용하지 않으면 불가능 혹은 무지 어려웠을 일들을 쉽게 처리하는 방법을 제공한다는 것입니다.

+ +

객체는 당신이 모델링하고자 하고자 하는 일이나 기능 혹은 필요한 행동들을 표현하는 프로그램 코드와 그와 연관된 데이터로 구성됩니다. 객체는 데이터(그리고, 함수 역시)를 감싸서 ,(공식적인 표현으로는 encapsulate) 객체 패키지(해당 객체를 참조하기 위한 이름. namespace 라고도 불리죠)안에 보관합니다. 이는 계층 구조를 만드는데 용이하고 사용하기에도 쉽게 하기 위해서죠; 또한, 객체는 네트워크를 통해 쉽게 전송될 수 있도록 데이터를 저장하는 용도로도 많이 사용됩니다.

+ +

객체 템플릿 정의

+ +

자, 학교의 선생님과 학생들의 정보를 보여주는 간단한 프로그램이 있다고 칩시다. 여기서는 OOP의 일반적인 개념만을 살펴볼 뿐이지, 특정 언어에 국한된 내용을 이야기하지는 않을겁니다.

+ +

시작해보자면, first objects article 에서 배웠던 Person 객체로 돌아가봅시다. 거기서 "사람"에 대한 기초적인 데이터와 기능을 정의했었죠. "사람"을 구별할 수 있는 특징은 많습니다 (그들의 주소, 키,신발사이즈, DNA 프로필, 여권번호, 중요한 개인적 자실 등 ...) ,하지만 이 예제에서는 오직 이름, 나이, 성별 그리고 취미만을 다룰겁니다. 여기에 더불어 이 데이터를 기반으로 각 개인에 대한 간단한 소개말과 인사말을 표시할 수 있도록 할 겁니다 . 이런 과정을 추상화 — 프로그래머의 의도에 맞추어 가장 중요한 것들만을 뽑아서 복잡한 것들을  보다 단순한 모델로 변환하는 작업 - 라고 합니다.

+ +

실제 객체 생성

+ +

객체 인스턴스는 클래스를 통해서 만들 수 있습니다.— 객체는 클래스에 정의된 데이터와 함수를 갖습니다. Person클래스를 통해서, 실제 '사람' 객체를 생성할 수 있습니다.:

+ +

+ +

클래스로부터 객체의 인스턴스가 생성될 때는 클래스의 생성자 함수 가 호출됩니다.클래스에서 객체 인스턴스가 생성되는 일련의 과정을 인스턴스화(instantiation)라고 합니다 — 객체의 인스턴스는 클래스를 통해 만들어집니다.

+ +

특별한 클래스

+ +

자, 이번에는 일반적인 사람이 아니라 — 일반적인 사람보다 세분화된 선생님과 학생들이 필요합니다.  OOP 에서는,특정 클래스를 기반으로 새로운 클래스를 만들 수 있습니다 — child 클래스 는 부모 클래스 상속 받아서 만들어집니다. child 클래스는 상속을 통해 부모 클래스에 정의된 데이터와 함수를 고스란히 사용할 수 있습니다. 클래스마다 기능이 달라지는 부분이 있다면, 직접 해당 클래스에 원하는 기능을 정의할 수 있습니다.

+ +

+ +

이것은 매우 유용합니다. 이름,성별,나이 등과 같이 선생님과 학생이 공유하는 많은 공통적인 특징들을 한번만 정의해도 되기 때문이죠. 또한 서로 다른 클래스에 같은 기능을 따로 정의할 수도 있습니다. 정의된 각각의 기능은 서로 다른 namespace에 존재하기 때문입니다. 예를 들어, 학생의 인사는 "안녕, 난 [이름]이야." 와 같은 형식이 될 것입니다. (ex) 안녕, 난 샘이야.) 반면 선생님은 "안녕하세요, 제 이름은 [성] [이름]이고 [과목명]을 담당하고 있습니다." 와 같이 좀 더 격식있는 형식을 사용할 것입니다. (ex) 안녕하세요, 제 이름은 데이브 그리피스이고 화학을 담당하고 있습니다.)

+ +
+

노트: 혹시 궁금해 하실까봐 말씀드리면, 여러 객체 타입에 같은 기능을 정의할 수 있는 능력을 멋진 용어로 "다형성(polymorphism)" 이라고 합니다.

+
+ +

이제 자식 클래스들로부터 객체 인스턴스를 만들 수 있습니다. 예를 들면 :

+ +

+ +

다음 부분에선, 어떻게 객체지향 프로그래밍 이론이 자바스크립트에 실제로 적용될 수 있는지 살펴보겠습니다.

+ +

생성자와 객체 인스턴스

+ +

자바스크립트는 객체와 그 기능을 정의하기 위해 생성자 함수라고 불리는 특별한 함수를 사용합니다. 이는 보통 우리가 얼마나 많은 객체들을 생성해야 할지 모르기 때문에 유용합니다. 생성자는 효율적으로 필요한 만큼 객체를 생성하고, 데이터와 함수들을 설정하는 방법을 제공합니다.

+ +

생성자로부터 새로운 객체 인스턴스가 생성되면, 객체의 핵심 기능 (프로토타입에 의해 정의됩니다. Object prototypes 글에서 자세히 다룰 것입니다.)이 프로토타입 체인에 의해 연결됩니다.

+ +

자바스크립트에서 생성자를 이용해 클래스를 만들고, 클래스에서 객체 인스턴스를 만드는 방법을 알아봅시다. 가장 먼저, 첫 객체 글에서 보았던 oojs.html 파일을 로컬에 새로 복사하십시오.

+ +

간단한 예제

+ +
    +
  1. 어떻게 일반적인 함수를 이용해 한 사람을 정의할 수 있는지부터 보겠습니다. 이 함수를 script 태그 안에 추가하세요: + +
    function createNewPerson(name) {
    +  var obj = {};
    +  obj.name = name;
    +  obj.greeting = function() {
    +    alert('Hi! I\'m ' + this.name + '.');
    +  };
    +  return obj;
    +}
    +
  2. +
  3. 이제 이 함수를 호출하여 새로운 사람을 만들 수 있습니다. 브라우저의 자바스크립트 콘솔을 열어 다음 코드를 입력해보세요: +
    var salva = createNewPerson('Salva');
    +salva.name;
    +salva.greeting();
    + 이것은 잘 작동하지만, 썩 깔끔하진 않습니다. 객체를 만들기를 원하는데, 왜 굳이 빈 객체를 만들고 내용을 채워 리턴해야 할까요? 다행스럽게도 자바스크립트는 생성자 함수의 형태로 간단한 단축 명령을 제공합니다. 하나 만들어 보도록 하죠!
  4. +
  5. 이전의 createNewPerson 함수를 다음의 코드로 교체하세요: +
    function Person(name) {
    +  this.name = name;
    +  this.greeting = function() {
    +    alert('Hi! I\'m ' + this.name + '.');
    +  };
    +}
    +
  6. +
+ +

생성자 함수는 클래스의 자바스크립트 버전입니다. 이 함수가 함수가 가질 것 같은 모든 특징을 가지고 있지만, 아무것도 리턴하지 않고 객체를 만들지도 않는다는 것을 깨달으셨나요? 생성자 함수는 단순히 프로퍼티와 메소드를 정의합니다. 또 이를 정의할 때 this 라는 키워드가 사용되고 있는 것을 보실 수 있습니다. 이것은 객체 인스턴스가 생성될 때마다, 객체의 name 프로퍼티가 생성자 함수 호출에서 전달된 name 값과 같아질 것이라고 말하고 있습니다. 그리고 greeting() 메소드 역시 생성자에서 전달된 name 값을 사용할 것입니다.

+ +
+

노트: 관습적으로, 생성자 함수명은 대문자로 시작하게 합니다. 이 규칙은 생성자 함수가 코드 안에서 잘 구별되도록 해줍니다.

+
+ +

그래서 어떻게 생성자 함수를 호출하여 객체들을 만들까요?

+ +
    +
  1. 이전 코드 아래에 다음 코드들을 추가하세요: +
    var person1 = new Person('Bob');
    +var person2 = new Person('Sarah');
    +
  2. +
  3. +

    코드를 저장하고 브라우저를 새로고침합니다. 자바스크립트 콘솔에 다음 코드를 입력해보세요:

    +
  4. +
  5. +
    person1.name
    +person1.greeting()
    +person2.name
    +person2.greeting()
    +
  6. +
+ +

멋지군요! 이제 두 객체가 페이지에 생성된 것이 보입니다. 각각은 서로 다른 namespace에 저장되어있습니다. 객체의 프로퍼티와 메소드들을 사용하려면, person1 또는 person2로부터 호출하여야 합니다. 두 객체의 기능은 따로 패키징되어 서로 충돌하지 않을 것입니다. 그리고 두 Person 객체는 각각 고유의 name 프로퍼티와 greeting() 메소드를 사용할 수 있습니다. 이 둘이 생성될 때 부여받은 자신의 name 값을 사용한다는 것에 주목하십시오. 이것이 this를 사용하는 매우 중요한 이유 중 하나입니다. 객체들은 다른 값이 아니라, 그들이 가진 고유의 값을 사용합니다.

+ +

생성자 호출을 다시 봅시다:

+ +
var person1 = new Person('Bob');
+var person2 = new Person('Sarah');
+ +

각각의 경우, new 키워드가 브라우저에게 우리가 새로운 객체 인스턴스를 만들고 싶어한다는 것을 알려줍니다. 괄호로 감싸진 매개변수들과 함께 생성자 이름을 호출하고, 결과는 변수에 담겨집니다. 일반적인 함수가 호출되는 방식과 매우 유사하죠. 각각의 인스턴스는 다음 정의에 따라 생성됩니다.

+ +
function Person(name) {
+  this.name = name;
+  this.greeting = function() {
+    alert('Hi! I\'m ' + this.name + '.');
+  };
+}
+ +

새 객체가 생성된 이후, person1person2 변수는 다음 객체들을 가지게 됩니다.

+ +
{
+  name: 'Bob',
+  greeting: function() {
+    alert('Hi! I\'m ' + this.name + '.');
+  }
+}
+
+{
+  name: 'Sarah',
+  greeting: function() {
+    alert('Hi! I\'m ' + this.name + '.');
+  }
+}
+ +

우리가 생성자 함수를 호출할 때마다 매번 greeting() 함수를 다시 정의하는 것이 보입니다. 최선의 방법은 아니죠. 이를 피하기 위해, 우리는 prototype에 함수를 정의합니다. 이를 차후에 다시 살펴보겠습니다.

+ +

생성자 완성시키기

+ +

위에서 살펴본 예제는 시작에 불과합니다. 최종적인 Person() 생성자를 만들어봅시다.

+ +
    +
  1. 여태 작성한 코드를 지우고 아래의 생성자로 대체하세요. 원리는 이전의 예제와 똑같으며, 약간 더 복잡할 뿐입니다: +
    function Person(first, last, age, gender, interests) {
    +  this.name = {
    +    'first': first,
    +    'last' : last
    +  };
    +  this.age = age;
    +  this.gender = gender;
    +  this.interests = interests;
    +  this.bio = function() {
    +    alert(this.name.first + ' ' + this.name.last + ' is ' + this.age + ' years old. He likes ' + this.interests[0] + ' and ' + this.interests[1] + '.');
    +  };
    +  this.greeting = function() {
    +    alert('Hi! I\'m ' + this.name.first + '.');
    +  };
    +}
    +
  2. +
  3. 이제 생성자로 객체 인스턴스를 만들기 위해, 아래에 이 코드를 추가하세요: +
    var person1 = new Person('Bob', 'Smith', 32, 'male', ['music', 'skiing']);
    +
  4. +
+ +

이제 이전에 해보았듯이, 브라우저의 자바스크립트 콘솔에서 프로퍼티와 메소드를 사용할 수 있습니다:

+ +
person1['age']
+person1.interests[1]
+person1.bio()
+// etc.
+ +
+

노트: 만약 실행에 문제가 생긴다면, 저희가 준비한 코드와 비교해보세요. oojs-class-finished.html (또한 실제로 실행되는 모습을 보세요).

+
+ +

추가 예제

+ +

이를 시작하기 위해서, 몇 개의 객체를 더 생성하는 코드를 추가해보세요. 그리고 생성된 객체 인스턴스의 멤버들을 사용하거나 바꿔보세요.

+ +

더 나아가, 우리의 bio() 메소드엔 몇 가지 문제점이 있습니다. 먼저 결과가 항상 대명사 "He"를 포함한다는 점입니다. 생성된 사람이 여성이거나 다른 성별 분류를 가질지라도 말이죠. 그리고 interests 배열에 몇 개가 포함되어 있더라도 bio는 2개의 취미만을 출력합니다. 클래스 정의 (생성자)에서 이를 해결할 방법이 있을까요? 자유롭게 생성자를 수정해보세요. (약간의 조건문과 반복문이 필요할지도 모르겠습니다). 어떻게 성별에 따라, 혹은 취미의 개수에 따라 문장이 다르게 구성되어야할지 생각해보세요 .

+ +
+

노트: 하다가 막힌다면, 저희가 제공하는 GitHub 저장소의 모법 답안 (그리고 실행 버전)을 참고하세요. 하지만 일단 직접 해보시죠!

+
+ +

객체 인스턴스를 생성하는 다른 방법들

+ +

여태까지 객체 인스턴스를 만드는 두 가지 방법을 살펴보았습니다. 객체 리터럴을 선언하는 방법과, 생성자 함수를 사용하는 방법(위를 보세요)이죠.

+ +

이것들은 잘 동작하지만, 다른 방법들도 있습니다. 웹에서 정보를 찾다가 마주칠 경우를 대비해 익숙해져보는 것도 좋을 것 같습니다.

+ +

Object() 생성자

+ +

첫번째로, 새 객체를 만들기 위해 Object() 생성자를 사용할 수 있습니다. 네, 최초의 object 역시 생성자를 가지고 있습니다. 빈 객체를 생성하는 함수이죠.

+ +
    +
  1. 브라우저의 자바스크립트 콘솔에 아래 코드를 입력해보세요: +
    var person1 = new Object();
    +
  2. +
  3. 이는 빈 객체를 person1 변수에 담습니다. 이제 이 객체에 점 표기법이나 괄호 표기법을 이용해 프로퍼티와 메소드들을 추가할 수 있습니다. 이 예제 코드를 콘솔 창에 입력해보세요. +
    person1.name = 'Chris';
    +person1['age'] = 38;
    +person1.greeting = function() {
    +  alert('Hi! I\'m ' + this.name + '.');
    +};
    +
  4. +
  5. 사전에 프로퍼티와 메소드를 정의하기 위해, Object() 생성자의 파라미터로 객체 리터럴을 전달할 수도 있습니다. 이 예제 코드를 콘솔 창에 입력해보세요. +
    var person1 = new Object({
    +  name: 'Chris',
    +  age: 38,
    +  greeting: function() {
    +    alert('Hi! I\'m ' + this.name + '.');
    +  }
    +});
    +
  6. +
+ +

create() 함수 사용

+ +

생성자는 여러분의 코드에 규칙을 부여해줍니다. 일단 생성자를 만들어두면, 이를 이용해 원하는대로 인스턴스를 생성할 수 있고, 이 인스턴스가 어디서 유래했는지 명백합니다.

+ +

하지만 몇몇 사람들은 객체 인스턴스들을 생성할 때 먼저 생성자를 만들기를 원하지 않습니다. 특히 그들이 적은 수의 객체만을 생성할 때 말이죠. 자바스크립트는 create()라는 내장함수를 가지고 있어 이를 가능하게 해줍니다. 이를 이용하면, 이미 존재하는 객체를 이용해 새로운 객체를 만들 수 있습니다.

+ +
    +
  1. 이전 섹션에서 완료한 예제를 브라우저에서 열어, 아래 코드를 콘솔창에 입력해보세요. +
    var person2 = Object.create(person1);
    +
  2. +
  3. 이제 이 코드를 입력해보세요. +
    person2.name
    +person2.greeting()
    +
  4. +
+ +

person2person1을 기반으로 만들어졌습니다. 새 객체는 원 객체와 같은 프로퍼티와 메소드들을 가집니다. 

+ +

create() 함수의 한 가지 단점은 익스플로러 8에서는 지원하지 않는다는 점입니다. 따라서 오래된 브라우저들까지 지원하고 싶다면 생성자를 사용하는 것이 효과적입니다.

+ +

다음에 create() 함수의 효과에 대해 더 살펴보겠습니다.

+ +

요약

+ +

이 글은 객체지향 이론을 요약하여 설명해줍니다. 모든 부분을 다루지는 않지만, 지금 어떤 것들을 다루고 있는지에 대한 아이디어 정도는 얻을 수 있습니다. 게다가 객체 인스턴스를 생성하는 여러가지 방법에 대해서도 알아보기 시작했습니다.

+ +

다음 글에서는 자바스크립트 객체 프로토타입에 대해 탐험해보겠습니다.

+ +

{{PreviousMenuNext("Learn/JavaScript/Objects/Basics", "Learn/JavaScript/Objects/Object_prototypes", "Learn/JavaScript/Objects")}}

+ +

In this module

+ + + +

 

diff --git a/files/ko/learn/javascript/objects/classes_in_javascript/index.html b/files/ko/learn/javascript/objects/classes_in_javascript/index.html new file mode 100644 index 0000000000..0549a416ef --- /dev/null +++ b/files/ko/learn/javascript/objects/classes_in_javascript/index.html @@ -0,0 +1,395 @@ +--- +title: Inheritance in JavaScript +slug: Learn/JavaScript/Objects/Classes_in_JavaScript +translation_of: Learn/JavaScript/Objects/Inheritance +original_slug: Learn/JavaScript/Objects/Inheritance +--- +
{{LearnSidebar}}
+ +
{{PreviousMenuNext("Learn/JavaScript/Objects/Object_prototypes", "Learn/JavaScript/Objects/JSON", "Learn/JavaScript/Objects")}}
+ +

OOJS에 대한 온갖 잡지식을 설명했으니, 이 글에서는 부모 클래스에서 자식 클래스를 상속하는 방법을 알아봅니다. 덤으로 OOJS를 구현하는데 몇 가지 참고사항도 있습니다.

+ + + + + + + + + + + + +
선수조건:컴퓨터 기본지식, HTML과 CSS에 대한 기본적인 이해,자바스크립트에 어느 정도 익숙할 것 (see First steps and Building blocks).  OOJS 기초 지식 (see Introduction to objects).
학습목표:Javascript에서 상속을 구현하는 법을 이해합니다.
+ +

프로토타입 상속

+ +

지금까지 몇 가지 상속을 살펴보았습니다 — 프로토타입 체인이 어떻게 동작하는지, 체인을 통해 멤버들을 탐색하는 것도 보았죠. 하지만 이는 대부분 브라우저가 알아서 처리하는 로직이었습니다. 그러면 우리가 직접 객체를 생성하고 상속하려면 어떻게 해야 할까요?

+ +

실질적인 예제를 통해 알아보도록 합시다.

+ +

시작하기

+ +

먼저 oojs-class-inheritance-start.html를 다운 받으시고 (running live 페이지도 보시구요). 파일 내에서 이전 예제에서 계속 봐 왔던 Person() 생성자를 보실 수 있습니다 — 생성자에 속성 몇 개를 정의했기에 조금 다릅니다:

+ +
function Person(first, last, age, gender, interests) {
+  this.name = {
+    first,
+    last
+  };
+  this.age = age;
+  this.gender = gender;
+  this.interests = interests;
+};
+ +

메소드는 전부 아래처럼 prototype에 정의되어 있습니다:

+ +
Person.prototype.greeting = function() {
+  alert('Hi! I\'m ' + this.name.first + '.');
+};
+ +
+

Note: 소스 코드에는 bio()와 farewell()메소드가 정의되어 있습니다. 잠시 후에 다른 생성자로 어떻게 상속하는지 알아보도록 합시다.

+
+ +

객체 지향에 대해 처음 정의할 때 언급했었던 Teacher 클래스를 만들어 봅시다. Person을 상속받고 아래 몇 가지를 추가해서요:

+ +
    +
  1. subject 속성 — 교사가 가르치는 과목을 나타냅니다.
  2. +
  3. 기존의 greeting() 보다 조금 더 공손한 인사를 하는 메소드  — 교사가 학생들에게 건넬 만한 표현으로 하죠.
  4. +
+ +

Teacher() 생성자 함수 정의

+ +

제일 처음 단계에서는 Teacher() 생성자를 만들어야 합니다 — 기존 코드 밑에 아래 코드를 추가하세요:

+ +
function Teacher(first, last, age, gender, interests, subject) {
+  Person.call(this, first, last, age, gender, interests);
+
+  this.subject = subject;
+}
+ +

Person() 생성자와 여러모로 비슷해 보이지만 여지껏 보지 못했던 한가지 차이점이 있습니다 — call() 함수죠. call() 함수의 첫번째 매개변수는 다른 곳에서 정의된 함수를 현재 컨텍스트에서 실행할 수 있도록 합니다. 실행하고자 하는 함수의 첫 번째 매개변수로 this를 전달하고 나머지는 실제 함수 실행에 필요한 인자들을 전달하면 됩니다.

+ +

Teacher()의 생성자는 Person()을 상속받았으므로 같은 매개변수들이 필요합니다. 따라서 동일한 매개변수들을 call()의 인자로 전달하여 실행합니다.

+ +

마지막 줄에서는 새 속성인 subject를 정의하여 Person이 아닌 Teacher만이 갖는 속성을 만들어 줍니다.

+ +

참고로 아래와 같이 할 수도 있습니다:

+ +
function Teacher(first, last, age, gender, interests, subject) {
+  this.name = {
+    first,
+    last
+  };
+  this.age = age;
+  this.gender = gender;
+  this.interests = interests;
+  this.subject = subject;
+}
+ +

다만 이는 Person()을 상속받은게 아니라 단지 동일한 인자를 정의했을 뿐이죠. 이건 원하는 방법이 아닐 뿐더러 코드의 길이만 더 늘어났습니다.

+ +

매개변수가 없는 생성자 상속하기

+ +

상속하려는 생성자가 속성을 매개변수로 받지 않는다면 call()의 매개변수에도 아무것도 전달할 필요가 없습니다. 아래처럼 간단한 생성자가 있다면:

+ +
function Brick() {
+  this.width = 10;
+  this.height = 20;
+}
+ +

widthheight 속성을 상속받기 위해 아래처럼만 하면 됩니다(물론 이후 설명할 방법을 써도 되구요):

+ +
function BlueGlassBrick() {
+  Brick.call(this);
+
+  this.opacity = 0.5;
+  this.color = 'blue';
+}
+ +

call() 함수에 this만 넘긴 것을 보세요. — Brick() 생성자에서 매개변수를 통해 초기화 하는 속성들이 없으므로 call()에도 넘길 필요가 없습니다.

+ +

Teacher()의 프로토타입과 생성자 참조 설정하기

+ +

다 좋은데 문제가 있습니다. 방금 정의한 새 생성자에는 생성자 함수 자신에 대한 참조만 가지고 있는 프로토타입 속성이 할당되어 있습니다. 정작 상속 받은 Person() 생성자의 prototype 속성은 없죠. Javascript 콘솔에서 Object.getOwnPropertyNames(Teacher.prototype)을 쳐서 확인해 보세요. 다음엔 TeacherPerson으로 바꿔서 확인해 보세요. Teacher()생성자는 Person()의 메소드를 상속받지 못하였습니다. Person.prototype.greetingTeacher.prototype.greeting 구문을 실행하여 비교해 보세요. Teacher()가 메소드도 상속 받으려면 어떻게 해야 할까요?

+ +
    +
  1. 기존 코드에 아래 코드를 추가하세요: +
    Teacher.prototype = Object.create(Person.prototype);
    + 구원 투수 create()의 등판입니다.  새 객체를 생성하여 Teacher.prototype으로 할당했죠. 새 객체는 Person.prototype 객체를 자신의 프로토타입으로 가지고 있으므로 Person.prototype에 정의된 모든 메소드를 사용할 수 있습니다.
  2. +
  3. 넘어가기 전에 한가지 더 해야 합니다. 마지막 줄을 추가하고 나면 Teacher.prototypeconstructor 속성이 Person()으로 되어 있습니다. Teacher.prototype에 Person.prototype을 상속받은 객체를 할당했기 때문이죠. 코드를 저장한 뒤 브라우저로 불러와서 Teacher.prototype.constructor 구문의 반환 값을 확인해 보세요.
  4. +
  5. 문제의 소지가 있으므로 고쳐야 됩니다. 소스에 아래 코드를 추가하세요: +
    Teacher.prototype.constructor = Teacher;
    +
  6. +
  7. 저장하고 다시 브라우저에서 불러오면 의도한대로 Teacher.prototype.constructorTeacher()를 반환합니다. 게다가 Person()도 상속받았죠!
  8. +
+ +

Teacher()에 새 greeting() 함수 부여하기

+ +

Teacher()에 새로운 greeting() 함수를 정의하여 코드를 완성합시다.

+ +

가장 간단한 방법은 Teacher()의 프로토타입에 정의합니다. — 아래 코드를 추가하세요:

+ +
Teacher.prototype.greeting = function() {
+  var prefix;
+
+  if (this.gender === 'male' || this.gender === 'Male' || this.gender === 'm' || this.gender === 'M') {
+    prefix = 'Mr.';
+  } else if (this.gender === 'female' || this.gender === 'Female' || this.gender === 'f' || this.gender === 'F') {
+    prefix = 'Mrs.';
+  } else {
+    prefix = 'Mx.';
+  }
+
+  alert('Hello. My name is ' + prefix + ' ' + this.name.last + ', and I teach ' + this.subject + '.');
+};
+ +

조건문을 이용해서 성별에 따라 적절한 호칭이 붙은 교사의 인삿말을 alert 창으로 띄웁니다.

+ +

예제 사용해 보기

+ +

소스를 환성했으니 아래 코드를 통해 새 Teacher() 인스턴스를 생성해 봅시다(아니면 인자를 원하는 값으로 변경하시거나요):

+ +
var teacher1 = new Teacher('Dave', 'Griffiths', 31, 'male', ['football', 'cookery'], 'mathematics');
+ +

저장한 코드를 다시 불러와서 아래처럼 teacher1의 속성과 메소드를 확인해 봅시다:

+ +
teacher1.name.first;
+teacher1.interests[0];
+teacher1.bio();
+teacher1.subject;
+teacher1.greeting();
+teacher1.farewell();
+ +

아주 잘 실행될 겁니다. 1, 2, 3, 6 줄은 Person() 생성자(클래스)에서 상속 받은 멤버에 접근합니다. 4번째 줄은 Teacher() 생성자(클래스)만 가지고 있는 멤버에 접근합니다. 5번째 줄은 Person()에서 상속 받은 멤버도 있지만 Teacher()가 이미 자신만의 새 메소드를 정의했으므로 Teacher()의 메소드에 접근합니다.

+ +
+

Note: 코드가 잘 동작하지 않으면 완성된 버전을 확인해 보세요. (실행 페이지도 보시구요).

+
+ +

이 테크닉이 Javascript에서 상속 받는 클래스를 만드는 유일한 방법은 아니지만 잘 동작하며 상속을 구현하는 방법을 잘 설명하고 있습니다.

+ +

조금 더 명확한 방식으로 Javascript에서 상속을 구현하는 새 {{glossary("ECMAScript")}} 기능도 관심 가질만한 주제입니다(Classes 참조). 아직까지 많은 브라우저에서 지원하지 못하고 있기 때문에 여기서 다를 주제는 아닙니다. 여러 문서에서 제시한 코드들은 IE9보다 더 오래된 구형 브라우저에서도 사용 가능하며 더 이전 버전을 지원하기 위한 방법들도 있습니다. 

+ +

JavaScript 라이브러리를 쓰면 간단합니다 — 상속 기능을 사용하기 위한 보편적인 방법이죠. 예를들어 CoffeeScriptclassextends등의 기능을 제공합니다.

+ +

더 연습하기

+ +

OOP theory section, 에서는 개념적으로 Person을 상속받고 Teacher보다 덜 공손한 greeting() 메소드를 재정의한 Student 클래스를 정의했었습니다. 해당 절에서 Student의 인삿말이 어땠는지 확인해 보시고 Person()을 상속받는 Student() 생성자를 구현해 보세요. greeting() 함수도 재정의 해 보시구요.

+ +
+

Note: 코드가 잘 동작하지 않으면 완성된 버전 을 확인해 보세요.(실행 페이지도 보시구요).

+
+ +

객체 멤버 요약

+ +

요약하면, 상속에 있어 고려해야 할 세 가지 유형의 속성/메소드가 있습니다:

+ +
    +
  1. 생성자 함수 내에서 인스턴스에 정의하는 유형. 직접 작성한 코드에서는 생성자 함수 내에 this.x = x 구문과 유사하게 정의되어 있으므로 발견하기 쉽습니다. 브라우저 내장 코드에서는 객체 인스턴스(보통 new 키워드를 통해 생성, ex) var myInstance = new myConstructor())에서만 접근할 수 있는 멤버입니다.
  2. +
  3. 생성자에 직접 정의하는 유형, 생성자에서만 사용 가능합니다. 브라우저 내장 객체에서 흔히 사용하는 방식인데, 인스턴스가 아니라 생성자 함수에서 바로 호출되는 유형입니다. Object.key() 같은 함수들이죠.
  4. +
  5. 인스턴스와 자식 클래스에 상속하기 위해 생성자의 프로토타입에 정의하는 유형. 생성자의 프로토타이비 속성에 정의되는 모든 멤버를 의미합니다. ex) myConstructor.prototype.x().
  6. +
+ +

뭐가 뭔지 헷갈려도 걱정하지 마세요 — 배우는 중이니 차츰 익숙해질겁니다.

+ +

ECMAScript 2015 클래스

+ +

ECMAScript 2015에서는 C++나 Java와 유사한 클래스 문법을 공개하여 클래스를 조금 더 쉽고 명확하게 재활용 할 수 있게 되었습니다. 이 절에서는 프로토타입 상속으로 작성한 Person과 Teacher 예제를 클래스 문법으로 변경하고 어떻게 동작하는지 설명하겠습니다.

+ +
+

Note: 대부분의 최신 브라우저에서 새로운 클래스 작성 방식을 지원합니다만 일부 구형 브라우저(Internet Explorer가 대표적)에서는 동작하지 않으므로 하위호환성을 위해 프로토타입 상속을 배워둘 필요가 있습니다.

+
+ +

Class-스타일로 재작성한 Person 예제를 보시죠:

+ +
class Person {
+  constructor(first, last, age, gender, interests) {
+    this.name = {
+      first,
+      last
+    };
+    this.age = age;
+    this.gender = gender;
+    this.interests = interests;
+  }
+
+  greeting() {
+    console.log(`Hi! I'm ${this.name.first}`);
+  };
+
+  farewell() {
+    console.log(`${this.name.first} has left the building. Bye for now!`);
+  };
+}
+ +

class 구문은 새로운 클래스를 작성함을 의미합니다. Class 블록 내에서 모든 기능을 정의할 수 있습니다.

+ + + + + +

이제 위에서 했듯이 new 연산자로 객체 인스턴스를 생성할 수 있습니다:

+ +
let han = new Person('Han', 'Solo', 25, 'male', ['Smuggling']);
+han.greeting();
+// Hi! I'm Han
+
+let leia = new Person('Leia', 'Organa', 19, 'female' ['Government']);
+leia.farewell();
+// Leia has left the building. Bye for now
+ +
+

Note: 코드를 까보면 class 부분은 프로토타입 상속으로 변환이 됩니다. — 문법 설탕(syntactic sugar)의 일종인거죠. 하지만 읽기 쉽다는데 대부분 동의하실 겁니다.

+
+ +

class 문법으로 상속

+ +

위에서 사람을 나타내는 클래스를 만들었습니다. Person 클래스는 일반적인 사람이 가질 만한 특성들을 나열하고 있죠; 이 절에서는 Person을 class 문법으로 상속받아 Teacher 클래스를 만들 예정입니다. 이 작업을 하위 클래스 생성이라 부릅니다.

+ +

하위 클래스를 만드려면 Javascript에서 extends 키워드를 통해 상속 받을 클래스를 명시합니다.

+ +
class Teacher extends Person {
+  constructor(first, last, age, gender, interests, subject, grade) {
+    this.name = {
+      first,
+      last
+    };
+
+  this.age = age;
+  this.gender = gender;
+  this.interests = interests;
+  // subject and grade are specific to Teacher
+  this.subject = subject;
+  this.grade = grade;
+  }
+}
+ +

constructor()에서 첫번쨰로 super() 연산자를 정의하면 코드를 조금 더 읽기 쉬워집니다. 이는 상위 클래스의 생성자를 호출하며 super()의 매개변수를 통해 상위 클래스의 멤버를 상속받을 수 있는 코드입니다.

+ +
class Teacher extends Person {
+  constructor(first, last, age, gender, interests, subject, grade) {
+    super(first, last, age, gender, interests);
+
+    // subject and grade are specific to Teacher
+    this.subject = subject;
+    this.grade = grade;
+  }
+}
+ +

Teacher의 인스턴스를 생성하면 의도한대로 이제 TeacherPerson 양 쪽의 메소드와 속성을 사용할 수 있습니다.

+ +
let snape = new Teacher('Severus', 'Snape', 58, 'male', ['Potions'], 'Dark arts', 5);
+snape.greeting(); // Hi! I'm Severus.
+snape.farewell(); // Severus has left the building. Bye for now.
+snape.age // 58
+snape.subject; // Dark arts
+ +

Person을 수정하지 않고 Teacher를 생성한 것처럼 또 다른 하위클래스도 생성할 수 있습니다.

+ +
+

Note: GitHub에서 es2015-class-inheritance.html 예제를 참조하세요(실행 페이지).

+
+ +

Getters와 Setters

+ +

생성한 클래스 인스턴스의 속성 값을 변경하거나 최종 값을 예측할 수 없는 경우가 있을 겁니다. Teacher 예제를 보면 인스턴스를 생성하기 전에는 어떤 과목을 가르칠지 아직 모릅니다. 학기 도중에 가르치는 과목이 변경될 수도 있구요.

+ +

이런 상황에 getter/setter가 필요합니다.

+ +

Teacher 클래스에 getter/setter를 추가해 봅시다. 마지막에 작성했던 예제를 그대로 사용해보죠.

+ +

Getter와 setter는 쌍으로 동작합니다. Getter가 현재 값을 반환한다면 그에 대응하는 setter는 해당하는 값을 변경합니다.

+ +

수정된 Teacher 클래스는 아래와 같습니다:

+ +
class Teacher extends Person {
+  constructor(first, last, age, gender, interests, subject, grade) {
+    super(first, last, age, gender, interests);
+    // subject and grade are specific to Teacher
+    this._subject = subject;
+    this.grade = grade;
+  }
+
+  get subject() {
+    return this._subject;
+  }
+
+  set subject(newSubject) {
+    this._subject = newSubject;
+  }
+}
+ +

위 클래스를 보시면 subject 속성에 대해 getter와 setter가 생겼습니다. 멤버 변수에는 _를 붙여 getter/setter와 구분을 하였습니다. 이렇게 하지 않으면 get/set을 호출할때마다 에러가 발생합니다:

+ + + +

두 기능이 실제로 어떻게 작동하는지 아래를 참조하세요:

+ +
// Check the default value
+console.log(snape._subject) // Returns "Dark arts"
+
+// Change the value
+snape._subject="Balloon animals" // Sets subject to "Balloon animals"
+
+// Check it again and see if it matches the new value
+console.log(snape._subject) // Returns "Balloon animals"
+ +
+

Note: GitHub에서 es2015-getters-setters.html 예제를 참조하세요(실행 페이지).

+
+ +

JavaScript에서 언제 상속을 사용해야 할까?

+ +

이 마지막 문서를 읽고 나면 "뭐가 이리 어렵냐"고 생각하실지도 모르겠습니다. 어렵긴 합니다 프로토타입과 상속은 Javascript에서 가장 난해한 부분이거든요. 하지만 이 부분은 Javascript가 강력하고 유연한 언어로써 작용할 수 있는 원동력이기에 충분한 시간을 들여 배울 가치가 있습니다.

+ +

어찌보면 여러분은 항상 상속하고 있었습니다. Web API나 브라우저 내장 객체인 string, array 등의 메소드/속성을 사용하면서 암묵적으로 상속을 사용하고 있었던거죠.

+ +

처음 시작하거나 작은 프로젝트에서 직접 상속을 구현하는 코드를 작성하는 경우는 그리 많지 않습니다. 필요하지도 않는데 상속을 위한 코드를 구현하는 건 시간 낭비에 불과하죠. 하지만 코드량이 많아질수록 상속이 필요한 경우가 생깁니다. 동일한 기능을 가진 클래스가 많아졌음을 발견했다면 기능들을 한데 묶어 공유할 수 있도록 일반 객체를 만들고 특이 객체들에게 상속하는 방식이 훨씬 편하고 유용하다는 점을 알 수 있습니다.

+ +
+

Note: Javascript에서는 프로토타입을 통해 상속이 구현되어 있어 이 방식을 흔히 위임이라고 표현합니다. 특이 객체들이 일반 객체에게 일부 기능의 실행을 위임하는 것이죠.

+
+ +

상속을 구현할때 상속 레벨을 너무 깊게 하지 말고, 메소드와 속성들이 정확히 어디에 구현되어 있는지 항상 인지해야 합니다. 브라우저 내장 객체의 prototype 역시 일시적으로 수정이 가능하지만 정말로 필요한 경우를 제외하고는 건드리지 말아야 합니다. 너무 깊은 상속은 디버그 할 때 끝없는 혼돈과 고통만을 줄 겁니다.

+ +

궁극적으로 객체는 함수나 반복문과 같이 고유한 역할과 장점을 지닌 채 코드를 재사용하는 또 다른 방법입니다. 서로 연관된 변수와 함수들을 하나로 묶어 다룰 필요가 있을때 객체가 좋은 아이디어입니다. 한 곳에서 다른 곳으로 데이터 집합을 전달할 때에도 객체가 유용합니다. 두가지 모두 생성자나 상속 없이도 가능한 일입니다. 딱 하나의 인스턴스만 필요할 경우 객체를 선언하지 않고 객체 리터럴만으로도 충분합니다. 당연히 상속은 필요없구요.

+ +

요약

+ +

이 글에서는 여러분들이 반드시 알아야 할 OOJS 이론과 문법의 나머지 부분에 대해 다루고 있습니다. 이 시점에서 여러분은 javascript 객체와 OOP 기초, 프로토타입과 프로토타입 상속, 클래스(생성자)를 만들고 인스턴스를 생성하며 기능을 추가하고, 다른 클래스를 상속 받아 하위 클래스를 만드는 방법을 배웠습니다.

+ +

다음 글에서는 Javascript 객체로 데이터를 교환하는 방식인 Javascript Object Notation(JSON)에 대해 알아봅시다.

+ +

See also

+ + + +

{{PreviousMenuNext("Learn/JavaScript/Objects/Object_prototypes", "Learn/JavaScript/Objects/JSON", "Learn/JavaScript/Objects")}}

+ + + +

In this module

+ + diff --git a/files/ko/learn/javascript/objects/inheritance/index.html b/files/ko/learn/javascript/objects/inheritance/index.html deleted file mode 100644 index 72a2302d15..0000000000 --- a/files/ko/learn/javascript/objects/inheritance/index.html +++ /dev/null @@ -1,394 +0,0 @@ ---- -title: Inheritance in JavaScript -slug: Learn/JavaScript/Objects/Inheritance -translation_of: Learn/JavaScript/Objects/Inheritance ---- -
{{LearnSidebar}}
- -
{{PreviousMenuNext("Learn/JavaScript/Objects/Object_prototypes", "Learn/JavaScript/Objects/JSON", "Learn/JavaScript/Objects")}}
- -

OOJS에 대한 온갖 잡지식을 설명했으니, 이 글에서는 부모 클래스에서 자식 클래스를 상속하는 방법을 알아봅니다. 덤으로 OOJS를 구현하는데 몇 가지 참고사항도 있습니다.

- - - - - - - - - - - - -
선수조건:컴퓨터 기본지식, HTML과 CSS에 대한 기본적인 이해,자바스크립트에 어느 정도 익숙할 것 (see First steps and Building blocks).  OOJS 기초 지식 (see Introduction to objects).
학습목표:Javascript에서 상속을 구현하는 법을 이해합니다.
- -

프로토타입 상속

- -

지금까지 몇 가지 상속을 살펴보았습니다 — 프로토타입 체인이 어떻게 동작하는지, 체인을 통해 멤버들을 탐색하는 것도 보았죠. 하지만 이는 대부분 브라우저가 알아서 처리하는 로직이었습니다. 그러면 우리가 직접 객체를 생성하고 상속하려면 어떻게 해야 할까요?

- -

실질적인 예제를 통해 알아보도록 합시다.

- -

시작하기

- -

먼저 oojs-class-inheritance-start.html를 다운 받으시고 (running live 페이지도 보시구요). 파일 내에서 이전 예제에서 계속 봐 왔던 Person() 생성자를 보실 수 있습니다 — 생성자에 속성 몇 개를 정의했기에 조금 다릅니다:

- -
function Person(first, last, age, gender, interests) {
-  this.name = {
-    first,
-    last
-  };
-  this.age = age;
-  this.gender = gender;
-  this.interests = interests;
-};
- -

메소드는 전부 아래처럼 prototype에 정의되어 있습니다:

- -
Person.prototype.greeting = function() {
-  alert('Hi! I\'m ' + this.name.first + '.');
-};
- -
-

Note: 소스 코드에는 bio()와 farewell()메소드가 정의되어 있습니다. 잠시 후에 다른 생성자로 어떻게 상속하는지 알아보도록 합시다.

-
- -

객체 지향에 대해 처음 정의할 때 언급했었던 Teacher 클래스를 만들어 봅시다. Person을 상속받고 아래 몇 가지를 추가해서요:

- -
    -
  1. subject 속성 — 교사가 가르치는 과목을 나타냅니다.
  2. -
  3. 기존의 greeting() 보다 조금 더 공손한 인사를 하는 메소드  — 교사가 학생들에게 건넬 만한 표현으로 하죠.
  4. -
- -

Teacher() 생성자 함수 정의

- -

제일 처음 단계에서는 Teacher() 생성자를 만들어야 합니다 — 기존 코드 밑에 아래 코드를 추가하세요:

- -
function Teacher(first, last, age, gender, interests, subject) {
-  Person.call(this, first, last, age, gender, interests);
-
-  this.subject = subject;
-}
- -

Person() 생성자와 여러모로 비슷해 보이지만 여지껏 보지 못했던 한가지 차이점이 있습니다 — call() 함수죠. call() 함수의 첫번째 매개변수는 다른 곳에서 정의된 함수를 현재 컨텍스트에서 실행할 수 있도록 합니다. 실행하고자 하는 함수의 첫 번째 매개변수로 this를 전달하고 나머지는 실제 함수 실행에 필요한 인자들을 전달하면 됩니다.

- -

Teacher()의 생성자는 Person()을 상속받았으므로 같은 매개변수들이 필요합니다. 따라서 동일한 매개변수들을 call()의 인자로 전달하여 실행합니다.

- -

마지막 줄에서는 새 속성인 subject를 정의하여 Person이 아닌 Teacher만이 갖는 속성을 만들어 줍니다.

- -

참고로 아래와 같이 할 수도 있습니다:

- -
function Teacher(first, last, age, gender, interests, subject) {
-  this.name = {
-    first,
-    last
-  };
-  this.age = age;
-  this.gender = gender;
-  this.interests = interests;
-  this.subject = subject;
-}
- -

다만 이는 Person()을 상속받은게 아니라 단지 동일한 인자를 정의했을 뿐이죠. 이건 원하는 방법이 아닐 뿐더러 코드의 길이만 더 늘어났습니다.

- -

매개변수가 없는 생성자 상속하기

- -

상속하려는 생성자가 속성을 매개변수로 받지 않는다면 call()의 매개변수에도 아무것도 전달할 필요가 없습니다. 아래처럼 간단한 생성자가 있다면:

- -
function Brick() {
-  this.width = 10;
-  this.height = 20;
-}
- -

widthheight 속성을 상속받기 위해 아래처럼만 하면 됩니다(물론 이후 설명할 방법을 써도 되구요):

- -
function BlueGlassBrick() {
-  Brick.call(this);
-
-  this.opacity = 0.5;
-  this.color = 'blue';
-}
- -

call() 함수에 this만 넘긴 것을 보세요. — Brick() 생성자에서 매개변수를 통해 초기화 하는 속성들이 없으므로 call()에도 넘길 필요가 없습니다.

- -

Teacher()의 프로토타입과 생성자 참조 설정하기

- -

다 좋은데 문제가 있습니다. 방금 정의한 새 생성자에는 생성자 함수 자신에 대한 참조만 가지고 있는 프로토타입 속성이 할당되어 있습니다. 정작 상속 받은 Person() 생성자의 prototype 속성은 없죠. Javascript 콘솔에서 Object.getOwnPropertyNames(Teacher.prototype)을 쳐서 확인해 보세요. 다음엔 TeacherPerson으로 바꿔서 확인해 보세요. Teacher()생성자는 Person()의 메소드를 상속받지 못하였습니다. Person.prototype.greetingTeacher.prototype.greeting 구문을 실행하여 비교해 보세요. Teacher()가 메소드도 상속 받으려면 어떻게 해야 할까요?

- -
    -
  1. 기존 코드에 아래 코드를 추가하세요: -
    Teacher.prototype = Object.create(Person.prototype);
    - 구원 투수 create()의 등판입니다.  새 객체를 생성하여 Teacher.prototype으로 할당했죠. 새 객체는 Person.prototype 객체를 자신의 프로토타입으로 가지고 있으므로 Person.prototype에 정의된 모든 메소드를 사용할 수 있습니다.
  2. -
  3. 넘어가기 전에 한가지 더 해야 합니다. 마지막 줄을 추가하고 나면 Teacher.prototypeconstructor 속성이 Person()으로 되어 있습니다. Teacher.prototype에 Person.prototype을 상속받은 객체를 할당했기 때문이죠. 코드를 저장한 뒤 브라우저로 불러와서 Teacher.prototype.constructor 구문의 반환 값을 확인해 보세요.
  4. -
  5. 문제의 소지가 있으므로 고쳐야 됩니다. 소스에 아래 코드를 추가하세요: -
    Teacher.prototype.constructor = Teacher;
    -
  6. -
  7. 저장하고 다시 브라우저에서 불러오면 의도한대로 Teacher.prototype.constructorTeacher()를 반환합니다. 게다가 Person()도 상속받았죠!
  8. -
- -

Teacher()에 새 greeting() 함수 부여하기

- -

Teacher()에 새로운 greeting() 함수를 정의하여 코드를 완성합시다.

- -

가장 간단한 방법은 Teacher()의 프로토타입에 정의합니다. — 아래 코드를 추가하세요:

- -
Teacher.prototype.greeting = function() {
-  var prefix;
-
-  if (this.gender === 'male' || this.gender === 'Male' || this.gender === 'm' || this.gender === 'M') {
-    prefix = 'Mr.';
-  } else if (this.gender === 'female' || this.gender === 'Female' || this.gender === 'f' || this.gender === 'F') {
-    prefix = 'Mrs.';
-  } else {
-    prefix = 'Mx.';
-  }
-
-  alert('Hello. My name is ' + prefix + ' ' + this.name.last + ', and I teach ' + this.subject + '.');
-};
- -

조건문을 이용해서 성별에 따라 적절한 호칭이 붙은 교사의 인삿말을 alert 창으로 띄웁니다.

- -

예제 사용해 보기

- -

소스를 환성했으니 아래 코드를 통해 새 Teacher() 인스턴스를 생성해 봅시다(아니면 인자를 원하는 값으로 변경하시거나요):

- -
var teacher1 = new Teacher('Dave', 'Griffiths', 31, 'male', ['football', 'cookery'], 'mathematics');
- -

저장한 코드를 다시 불러와서 아래처럼 teacher1의 속성과 메소드를 확인해 봅시다:

- -
teacher1.name.first;
-teacher1.interests[0];
-teacher1.bio();
-teacher1.subject;
-teacher1.greeting();
-teacher1.farewell();
- -

아주 잘 실행될 겁니다. 1, 2, 3, 6 줄은 Person() 생성자(클래스)에서 상속 받은 멤버에 접근합니다. 4번째 줄은 Teacher() 생성자(클래스)만 가지고 있는 멤버에 접근합니다. 5번째 줄은 Person()에서 상속 받은 멤버도 있지만 Teacher()가 이미 자신만의 새 메소드를 정의했으므로 Teacher()의 메소드에 접근합니다.

- -
-

Note: 코드가 잘 동작하지 않으면 완성된 버전을 확인해 보세요. (실행 페이지도 보시구요).

-
- -

이 테크닉이 Javascript에서 상속 받는 클래스를 만드는 유일한 방법은 아니지만 잘 동작하며 상속을 구현하는 방법을 잘 설명하고 있습니다.

- -

조금 더 명확한 방식으로 Javascript에서 상속을 구현하는 새 {{glossary("ECMAScript")}} 기능도 관심 가질만한 주제입니다(Classes 참조). 아직까지 많은 브라우저에서 지원하지 못하고 있기 때문에 여기서 다를 주제는 아닙니다. 여러 문서에서 제시한 코드들은 IE9보다 더 오래된 구형 브라우저에서도 사용 가능하며 더 이전 버전을 지원하기 위한 방법들도 있습니다. 

- -

JavaScript 라이브러리를 쓰면 간단합니다 — 상속 기능을 사용하기 위한 보편적인 방법이죠. 예를들어 CoffeeScriptclassextends등의 기능을 제공합니다.

- -

더 연습하기

- -

OOP theory section, 에서는 개념적으로 Person을 상속받고 Teacher보다 덜 공손한 greeting() 메소드를 재정의한 Student 클래스를 정의했었습니다. 해당 절에서 Student의 인삿말이 어땠는지 확인해 보시고 Person()을 상속받는 Student() 생성자를 구현해 보세요. greeting() 함수도 재정의 해 보시구요.

- -
-

Note: 코드가 잘 동작하지 않으면 완성된 버전 을 확인해 보세요.(실행 페이지도 보시구요).

-
- -

객체 멤버 요약

- -

요약하면, 상속에 있어 고려해야 할 세 가지 유형의 속성/메소드가 있습니다:

- -
    -
  1. 생성자 함수 내에서 인스턴스에 정의하는 유형. 직접 작성한 코드에서는 생성자 함수 내에 this.x = x 구문과 유사하게 정의되어 있으므로 발견하기 쉽습니다. 브라우저 내장 코드에서는 객체 인스턴스(보통 new 키워드를 통해 생성, ex) var myInstance = new myConstructor())에서만 접근할 수 있는 멤버입니다.
  2. -
  3. 생성자에 직접 정의하는 유형, 생성자에서만 사용 가능합니다. 브라우저 내장 객체에서 흔히 사용하는 방식인데, 인스턴스가 아니라 생성자 함수에서 바로 호출되는 유형입니다. Object.key() 같은 함수들이죠.
  4. -
  5. 인스턴스와 자식 클래스에 상속하기 위해 생성자의 프로토타입에 정의하는 유형. 생성자의 프로토타이비 속성에 정의되는 모든 멤버를 의미합니다. ex) myConstructor.prototype.x().
  6. -
- -

뭐가 뭔지 헷갈려도 걱정하지 마세요 — 배우는 중이니 차츰 익숙해질겁니다.

- -

ECMAScript 2015 클래스

- -

ECMAScript 2015에서는 C++나 Java와 유사한 클래스 문법을 공개하여 클래스를 조금 더 쉽고 명확하게 재활용 할 수 있게 되었습니다. 이 절에서는 프로토타입 상속으로 작성한 Person과 Teacher 예제를 클래스 문법으로 변경하고 어떻게 동작하는지 설명하겠습니다.

- -
-

Note: 대부분의 최신 브라우저에서 새로운 클래스 작성 방식을 지원합니다만 일부 구형 브라우저(Internet Explorer가 대표적)에서는 동작하지 않으므로 하위호환성을 위해 프로토타입 상속을 배워둘 필요가 있습니다.

-
- -

Class-스타일로 재작성한 Person 예제를 보시죠:

- -
class Person {
-  constructor(first, last, age, gender, interests) {
-    this.name = {
-      first,
-      last
-    };
-    this.age = age;
-    this.gender = gender;
-    this.interests = interests;
-  }
-
-  greeting() {
-    console.log(`Hi! I'm ${this.name.first}`);
-  };
-
-  farewell() {
-    console.log(`${this.name.first} has left the building. Bye for now!`);
-  };
-}
- -

class 구문은 새로운 클래스를 작성함을 의미합니다. Class 블록 내에서 모든 기능을 정의할 수 있습니다.

- - - - - -

이제 위에서 했듯이 new 연산자로 객체 인스턴스를 생성할 수 있습니다:

- -
let han = new Person('Han', 'Solo', 25, 'male', ['Smuggling']);
-han.greeting();
-// Hi! I'm Han
-
-let leia = new Person('Leia', 'Organa', 19, 'female' ['Government']);
-leia.farewell();
-// Leia has left the building. Bye for now
- -
-

Note: 코드를 까보면 class 부분은 프로토타입 상속으로 변환이 됩니다. — 문법 설탕(syntactic sugar)의 일종인거죠. 하지만 읽기 쉽다는데 대부분 동의하실 겁니다.

-
- -

class 문법으로 상속

- -

위에서 사람을 나타내는 클래스를 만들었습니다. Person 클래스는 일반적인 사람이 가질 만한 특성들을 나열하고 있죠; 이 절에서는 Person을 class 문법으로 상속받아 Teacher 클래스를 만들 예정입니다. 이 작업을 하위 클래스 생성이라 부릅니다.

- -

하위 클래스를 만드려면 Javascript에서 extends 키워드를 통해 상속 받을 클래스를 명시합니다.

- -
class Teacher extends Person {
-  constructor(first, last, age, gender, interests, subject, grade) {
-    this.name = {
-      first,
-      last
-    };
-
-  this.age = age;
-  this.gender = gender;
-  this.interests = interests;
-  // subject and grade are specific to Teacher
-  this.subject = subject;
-  this.grade = grade;
-  }
-}
- -

constructor()에서 첫번쨰로 super() 연산자를 정의하면 코드를 조금 더 읽기 쉬워집니다. 이는 상위 클래스의 생성자를 호출하며 super()의 매개변수를 통해 상위 클래스의 멤버를 상속받을 수 있는 코드입니다.

- -
class Teacher extends Person {
-  constructor(first, last, age, gender, interests, subject, grade) {
-    super(first, last, age, gender, interests);
-
-    // subject and grade are specific to Teacher
-    this.subject = subject;
-    this.grade = grade;
-  }
-}
- -

Teacher의 인스턴스를 생성하면 의도한대로 이제 TeacherPerson 양 쪽의 메소드와 속성을 사용할 수 있습니다.

- -
let snape = new Teacher('Severus', 'Snape', 58, 'male', ['Potions'], 'Dark arts', 5);
-snape.greeting(); // Hi! I'm Severus.
-snape.farewell(); // Severus has left the building. Bye for now.
-snape.age // 58
-snape.subject; // Dark arts
- -

Person을 수정하지 않고 Teacher를 생성한 것처럼 또 다른 하위클래스도 생성할 수 있습니다.

- -
-

Note: GitHub에서 es2015-class-inheritance.html 예제를 참조하세요(실행 페이지).

-
- -

Getters와 Setters

- -

생성한 클래스 인스턴스의 속성 값을 변경하거나 최종 값을 예측할 수 없는 경우가 있을 겁니다. Teacher 예제를 보면 인스턴스를 생성하기 전에는 어떤 과목을 가르칠지 아직 모릅니다. 학기 도중에 가르치는 과목이 변경될 수도 있구요.

- -

이런 상황에 getter/setter가 필요합니다.

- -

Teacher 클래스에 getter/setter를 추가해 봅시다. 마지막에 작성했던 예제를 그대로 사용해보죠.

- -

Getter와 setter는 쌍으로 동작합니다. Getter가 현재 값을 반환한다면 그에 대응하는 setter는 해당하는 값을 변경합니다.

- -

수정된 Teacher 클래스는 아래와 같습니다:

- -
class Teacher extends Person {
-  constructor(first, last, age, gender, interests, subject, grade) {
-    super(first, last, age, gender, interests);
-    // subject and grade are specific to Teacher
-    this._subject = subject;
-    this.grade = grade;
-  }
-
-  get subject() {
-    return this._subject;
-  }
-
-  set subject(newSubject) {
-    this._subject = newSubject;
-  }
-}
- -

위 클래스를 보시면 subject 속성에 대해 getter와 setter가 생겼습니다. 멤버 변수에는 _를 붙여 getter/setter와 구분을 하였습니다. 이렇게 하지 않으면 get/set을 호출할때마다 에러가 발생합니다:

- - - -

두 기능이 실제로 어떻게 작동하는지 아래를 참조하세요:

- -
// Check the default value
-console.log(snape._subject) // Returns "Dark arts"
-
-// Change the value
-snape._subject="Balloon animals" // Sets subject to "Balloon animals"
-
-// Check it again and see if it matches the new value
-console.log(snape._subject) // Returns "Balloon animals"
- -
-

Note: GitHub에서 es2015-getters-setters.html 예제를 참조하세요(실행 페이지).

-
- -

JavaScript에서 언제 상속을 사용해야 할까?

- -

이 마지막 문서를 읽고 나면 "뭐가 이리 어렵냐"고 생각하실지도 모르겠습니다. 어렵긴 합니다 프로토타입과 상속은 Javascript에서 가장 난해한 부분이거든요. 하지만 이 부분은 Javascript가 강력하고 유연한 언어로써 작용할 수 있는 원동력이기에 충분한 시간을 들여 배울 가치가 있습니다.

- -

어찌보면 여러분은 항상 상속하고 있었습니다. Web API나 브라우저 내장 객체인 string, array 등의 메소드/속성을 사용하면서 암묵적으로 상속을 사용하고 있었던거죠.

- -

처음 시작하거나 작은 프로젝트에서 직접 상속을 구현하는 코드를 작성하는 경우는 그리 많지 않습니다. 필요하지도 않는데 상속을 위한 코드를 구현하는 건 시간 낭비에 불과하죠. 하지만 코드량이 많아질수록 상속이 필요한 경우가 생깁니다. 동일한 기능을 가진 클래스가 많아졌음을 발견했다면 기능들을 한데 묶어 공유할 수 있도록 일반 객체를 만들고 특이 객체들에게 상속하는 방식이 훨씬 편하고 유용하다는 점을 알 수 있습니다.

- -
-

Note: Javascript에서는 프로토타입을 통해 상속이 구현되어 있어 이 방식을 흔히 위임이라고 표현합니다. 특이 객체들이 일반 객체에게 일부 기능의 실행을 위임하는 것이죠.

-
- -

상속을 구현할때 상속 레벨을 너무 깊게 하지 말고, 메소드와 속성들이 정확히 어디에 구현되어 있는지 항상 인지해야 합니다. 브라우저 내장 객체의 prototype 역시 일시적으로 수정이 가능하지만 정말로 필요한 경우를 제외하고는 건드리지 말아야 합니다. 너무 깊은 상속은 디버그 할 때 끝없는 혼돈과 고통만을 줄 겁니다.

- -

궁극적으로 객체는 함수나 반복문과 같이 고유한 역할과 장점을 지닌 채 코드를 재사용하는 또 다른 방법입니다. 서로 연관된 변수와 함수들을 하나로 묶어 다룰 필요가 있을때 객체가 좋은 아이디어입니다. 한 곳에서 다른 곳으로 데이터 집합을 전달할 때에도 객체가 유용합니다. 두가지 모두 생성자나 상속 없이도 가능한 일입니다. 딱 하나의 인스턴스만 필요할 경우 객체를 선언하지 않고 객체 리터럴만으로도 충분합니다. 당연히 상속은 필요없구요.

- -

요약

- -

이 글에서는 여러분들이 반드시 알아야 할 OOJS 이론과 문법의 나머지 부분에 대해 다루고 있습니다. 이 시점에서 여러분은 javascript 객체와 OOP 기초, 프로토타입과 프로토타입 상속, 클래스(생성자)를 만들고 인스턴스를 생성하며 기능을 추가하고, 다른 클래스를 상속 받아 하위 클래스를 만드는 방법을 배웠습니다.

- -

다음 글에서는 Javascript 객체로 데이터를 교환하는 방식인 Javascript Object Notation(JSON)에 대해 알아봅시다.

- -

See also

- - - -

{{PreviousMenuNext("Learn/JavaScript/Objects/Object_prototypes", "Learn/JavaScript/Objects/JSON", "Learn/JavaScript/Objects")}}

- - - -

In this module

- - diff --git a/files/ko/learn/javascript/objects/object-oriented_js/index.html b/files/ko/learn/javascript/objects/object-oriented_js/index.html deleted file mode 100644 index df1bf59c17..0000000000 --- a/files/ko/learn/javascript/objects/object-oriented_js/index.html +++ /dev/null @@ -1,287 +0,0 @@ ---- -title: Object-oriented JavaScript for beginners -slug: Learn/JavaScript/Objects/Object-oriented_JS -tags: - - Article - - Beginner - - CodingScripting - - JavaScript - - Learn - - 'l10n:priority' -translation_of: Learn/JavaScript/Objects/Object-oriented_JS ---- -
{{LearnSidebar}}
- -
{{PreviousMenuNext("Learn/JavaScript/Objects/Basics", "Learn/JavaScript/Objects/Object_prototypes", "Learn/JavaScript/Objects")}}
- -

자, 이제 기초 단계를 벗어나서,객체지향 JavaScript (OOJS) 을 보도록 하죠 — 이 문서에서 객체지향 (OOP) 이론에 대한 기초를 훑어본 후, 자바스크립트가 생성자와 함수를 통해 객체 클래스 개념을 따라했는지, 그리고 어떻게 객체를 만드는지 알아볼겁니다.

- - - - - - - - - - - - -
선수조건:컴퓨터 기본지식, HTML과 CSS에 대한 기본적인 이해,자바스크립트에 어느 정도 익숙할 것 (see First steps and Building blocks).  OOJS 기초 지식 (see Introduction to objects).
학습목표:객체지향에 대한 기본 지식을 습득 하고, 객체 지향이 자바스크립트에 어떻게 적용되었는지 ( "모든 것은 객체다") 와 어떻게 생성자와 객체 인스턴스를 만드는지에 대해 이해한다.
- -

객체지향 프로그래밍 — 기초

- -

객체지향 프로그래밍(OOP)의 개요를 설명하는 것으로 시작하겠습니다. 지금 단계에서 OOP의 모든 것을 설명면 너무 복잡해서 혼란만을 가중시킬 것이기 때문에 최대한 간단히 설명하겠습니다. OOP의 기본 컨셉은 프로그램 내에서 표현하고자 하는 실 세계(real world)의 일들을 객체를 사용해서 모델링 하고, 객체를 사용하지 않으면 불가능 혹은 무지 어려웠을 일들을 쉽게 처리하는 방법을 제공한다는 것입니다.

- -

객체는 당신이 모델링하고자 하고자 하는 일이나 기능 혹은 필요한 행동들을 표현하는 프로그램 코드와 그와 연관된 데이터로 구성됩니다. 객체는 데이터(그리고, 함수 역시)를 감싸서 ,(공식적인 표현으로는 encapsulate) 객체 패키지(해당 객체를 참조하기 위한 이름. namespace 라고도 불리죠)안에 보관합니다. 이는 계층 구조를 만드는데 용이하고 사용하기에도 쉽게 하기 위해서죠; 또한, 객체는 네트워크를 통해 쉽게 전송될 수 있도록 데이터를 저장하는 용도로도 많이 사용됩니다.

- -

객체 템플릿 정의

- -

자, 학교의 선생님과 학생들의 정보를 보여주는 간단한 프로그램이 있다고 칩시다. 여기서는 OOP의 일반적인 개념만을 살펴볼 뿐이지, 특정 언어에 국한된 내용을 이야기하지는 않을겁니다.

- -

시작해보자면, first objects article 에서 배웠던 Person 객체로 돌아가봅시다. 거기서 "사람"에 대한 기초적인 데이터와 기능을 정의했었죠. "사람"을 구별할 수 있는 특징은 많습니다 (그들의 주소, 키,신발사이즈, DNA 프로필, 여권번호, 중요한 개인적 자실 등 ...) ,하지만 이 예제에서는 오직 이름, 나이, 성별 그리고 취미만을 다룰겁니다. 여기에 더불어 이 데이터를 기반으로 각 개인에 대한 간단한 소개말과 인사말을 표시할 수 있도록 할 겁니다 . 이런 과정을 추상화 — 프로그래머의 의도에 맞추어 가장 중요한 것들만을 뽑아서 복잡한 것들을  보다 단순한 모델로 변환하는 작업 - 라고 합니다.

- -

실제 객체 생성

- -

객체 인스턴스는 클래스를 통해서 만들 수 있습니다.— 객체는 클래스에 정의된 데이터와 함수를 갖습니다. Person클래스를 통해서, 실제 '사람' 객체를 생성할 수 있습니다.:

- -

- -

클래스로부터 객체의 인스턴스가 생성될 때는 클래스의 생성자 함수 가 호출됩니다.클래스에서 객체 인스턴스가 생성되는 일련의 과정을 인스턴스화(instantiation)라고 합니다 — 객체의 인스턴스는 클래스를 통해 만들어집니다.

- -

특별한 클래스

- -

자, 이번에는 일반적인 사람이 아니라 — 일반적인 사람보다 세분화된 선생님과 학생들이 필요합니다.  OOP 에서는,특정 클래스를 기반으로 새로운 클래스를 만들 수 있습니다 — child 클래스 는 부모 클래스 상속 받아서 만들어집니다. child 클래스는 상속을 통해 부모 클래스에 정의된 데이터와 함수를 고스란히 사용할 수 있습니다. 클래스마다 기능이 달라지는 부분이 있다면, 직접 해당 클래스에 원하는 기능을 정의할 수 있습니다.

- -

- -

이것은 매우 유용합니다. 이름,성별,나이 등과 같이 선생님과 학생이 공유하는 많은 공통적인 특징들을 한번만 정의해도 되기 때문이죠. 또한 서로 다른 클래스에 같은 기능을 따로 정의할 수도 있습니다. 정의된 각각의 기능은 서로 다른 namespace에 존재하기 때문입니다. 예를 들어, 학생의 인사는 "안녕, 난 [이름]이야." 와 같은 형식이 될 것입니다. (ex) 안녕, 난 샘이야.) 반면 선생님은 "안녕하세요, 제 이름은 [성] [이름]이고 [과목명]을 담당하고 있습니다." 와 같이 좀 더 격식있는 형식을 사용할 것입니다. (ex) 안녕하세요, 제 이름은 데이브 그리피스이고 화학을 담당하고 있습니다.)

- -
-

노트: 혹시 궁금해 하실까봐 말씀드리면, 여러 객체 타입에 같은 기능을 정의할 수 있는 능력을 멋진 용어로 "다형성(polymorphism)" 이라고 합니다.

-
- -

이제 자식 클래스들로부터 객체 인스턴스를 만들 수 있습니다. 예를 들면 :

- -

- -

다음 부분에선, 어떻게 객체지향 프로그래밍 이론이 자바스크립트에 실제로 적용될 수 있는지 살펴보겠습니다.

- -

생성자와 객체 인스턴스

- -

자바스크립트는 객체와 그 기능을 정의하기 위해 생성자 함수라고 불리는 특별한 함수를 사용합니다. 이는 보통 우리가 얼마나 많은 객체들을 생성해야 할지 모르기 때문에 유용합니다. 생성자는 효율적으로 필요한 만큼 객체를 생성하고, 데이터와 함수들을 설정하는 방법을 제공합니다.

- -

생성자로부터 새로운 객체 인스턴스가 생성되면, 객체의 핵심 기능 (프로토타입에 의해 정의됩니다. Object prototypes 글에서 자세히 다룰 것입니다.)이 프로토타입 체인에 의해 연결됩니다.

- -

자바스크립트에서 생성자를 이용해 클래스를 만들고, 클래스에서 객체 인스턴스를 만드는 방법을 알아봅시다. 가장 먼저, 첫 객체 글에서 보았던 oojs.html 파일을 로컬에 새로 복사하십시오.

- -

간단한 예제

- -
    -
  1. 어떻게 일반적인 함수를 이용해 한 사람을 정의할 수 있는지부터 보겠습니다. 이 함수를 script 태그 안에 추가하세요: - -
    function createNewPerson(name) {
    -  var obj = {};
    -  obj.name = name;
    -  obj.greeting = function() {
    -    alert('Hi! I\'m ' + this.name + '.');
    -  };
    -  return obj;
    -}
    -
  2. -
  3. 이제 이 함수를 호출하여 새로운 사람을 만들 수 있습니다. 브라우저의 자바스크립트 콘솔을 열어 다음 코드를 입력해보세요: -
    var salva = createNewPerson('Salva');
    -salva.name;
    -salva.greeting();
    - 이것은 잘 작동하지만, 썩 깔끔하진 않습니다. 객체를 만들기를 원하는데, 왜 굳이 빈 객체를 만들고 내용을 채워 리턴해야 할까요? 다행스럽게도 자바스크립트는 생성자 함수의 형태로 간단한 단축 명령을 제공합니다. 하나 만들어 보도록 하죠!
  4. -
  5. 이전의 createNewPerson 함수를 다음의 코드로 교체하세요: -
    function Person(name) {
    -  this.name = name;
    -  this.greeting = function() {
    -    alert('Hi! I\'m ' + this.name + '.');
    -  };
    -}
    -
  6. -
- -

생성자 함수는 클래스의 자바스크립트 버전입니다. 이 함수가 함수가 가질 것 같은 모든 특징을 가지고 있지만, 아무것도 리턴하지 않고 객체를 만들지도 않는다는 것을 깨달으셨나요? 생성자 함수는 단순히 프로퍼티와 메소드를 정의합니다. 또 이를 정의할 때 this 라는 키워드가 사용되고 있는 것을 보실 수 있습니다. 이것은 객체 인스턴스가 생성될 때마다, 객체의 name 프로퍼티가 생성자 함수 호출에서 전달된 name 값과 같아질 것이라고 말하고 있습니다. 그리고 greeting() 메소드 역시 생성자에서 전달된 name 값을 사용할 것입니다.

- -
-

노트: 관습적으로, 생성자 함수명은 대문자로 시작하게 합니다. 이 규칙은 생성자 함수가 코드 안에서 잘 구별되도록 해줍니다.

-
- -

그래서 어떻게 생성자 함수를 호출하여 객체들을 만들까요?

- -
    -
  1. 이전 코드 아래에 다음 코드들을 추가하세요: -
    var person1 = new Person('Bob');
    -var person2 = new Person('Sarah');
    -
  2. -
  3. -

    코드를 저장하고 브라우저를 새로고침합니다. 자바스크립트 콘솔에 다음 코드를 입력해보세요:

    -
  4. -
  5. -
    person1.name
    -person1.greeting()
    -person2.name
    -person2.greeting()
    -
  6. -
- -

멋지군요! 이제 두 객체가 페이지에 생성된 것이 보입니다. 각각은 서로 다른 namespace에 저장되어있습니다. 객체의 프로퍼티와 메소드들을 사용하려면, person1 또는 person2로부터 호출하여야 합니다. 두 객체의 기능은 따로 패키징되어 서로 충돌하지 않을 것입니다. 그리고 두 Person 객체는 각각 고유의 name 프로퍼티와 greeting() 메소드를 사용할 수 있습니다. 이 둘이 생성될 때 부여받은 자신의 name 값을 사용한다는 것에 주목하십시오. 이것이 this를 사용하는 매우 중요한 이유 중 하나입니다. 객체들은 다른 값이 아니라, 그들이 가진 고유의 값을 사용합니다.

- -

생성자 호출을 다시 봅시다:

- -
var person1 = new Person('Bob');
-var person2 = new Person('Sarah');
- -

각각의 경우, new 키워드가 브라우저에게 우리가 새로운 객체 인스턴스를 만들고 싶어한다는 것을 알려줍니다. 괄호로 감싸진 매개변수들과 함께 생성자 이름을 호출하고, 결과는 변수에 담겨집니다. 일반적인 함수가 호출되는 방식과 매우 유사하죠. 각각의 인스턴스는 다음 정의에 따라 생성됩니다.

- -
function Person(name) {
-  this.name = name;
-  this.greeting = function() {
-    alert('Hi! I\'m ' + this.name + '.');
-  };
-}
- -

새 객체가 생성된 이후, person1person2 변수는 다음 객체들을 가지게 됩니다.

- -
{
-  name: 'Bob',
-  greeting: function() {
-    alert('Hi! I\'m ' + this.name + '.');
-  }
-}
-
-{
-  name: 'Sarah',
-  greeting: function() {
-    alert('Hi! I\'m ' + this.name + '.');
-  }
-}
- -

우리가 생성자 함수를 호출할 때마다 매번 greeting() 함수를 다시 정의하는 것이 보입니다. 최선의 방법은 아니죠. 이를 피하기 위해, 우리는 prototype에 함수를 정의합니다. 이를 차후에 다시 살펴보겠습니다.

- -

생성자 완성시키기

- -

위에서 살펴본 예제는 시작에 불과합니다. 최종적인 Person() 생성자를 만들어봅시다.

- -
    -
  1. 여태 작성한 코드를 지우고 아래의 생성자로 대체하세요. 원리는 이전의 예제와 똑같으며, 약간 더 복잡할 뿐입니다: -
    function Person(first, last, age, gender, interests) {
    -  this.name = {
    -    'first': first,
    -    'last' : last
    -  };
    -  this.age = age;
    -  this.gender = gender;
    -  this.interests = interests;
    -  this.bio = function() {
    -    alert(this.name.first + ' ' + this.name.last + ' is ' + this.age + ' years old. He likes ' + this.interests[0] + ' and ' + this.interests[1] + '.');
    -  };
    -  this.greeting = function() {
    -    alert('Hi! I\'m ' + this.name.first + '.');
    -  };
    -}
    -
  2. -
  3. 이제 생성자로 객체 인스턴스를 만들기 위해, 아래에 이 코드를 추가하세요: -
    var person1 = new Person('Bob', 'Smith', 32, 'male', ['music', 'skiing']);
    -
  4. -
- -

이제 이전에 해보았듯이, 브라우저의 자바스크립트 콘솔에서 프로퍼티와 메소드를 사용할 수 있습니다:

- -
person1['age']
-person1.interests[1]
-person1.bio()
-// etc.
- -
-

노트: 만약 실행에 문제가 생긴다면, 저희가 준비한 코드와 비교해보세요. oojs-class-finished.html (또한 실제로 실행되는 모습을 보세요).

-
- -

추가 예제

- -

이를 시작하기 위해서, 몇 개의 객체를 더 생성하는 코드를 추가해보세요. 그리고 생성된 객체 인스턴스의 멤버들을 사용하거나 바꿔보세요.

- -

더 나아가, 우리의 bio() 메소드엔 몇 가지 문제점이 있습니다. 먼저 결과가 항상 대명사 "He"를 포함한다는 점입니다. 생성된 사람이 여성이거나 다른 성별 분류를 가질지라도 말이죠. 그리고 interests 배열에 몇 개가 포함되어 있더라도 bio는 2개의 취미만을 출력합니다. 클래스 정의 (생성자)에서 이를 해결할 방법이 있을까요? 자유롭게 생성자를 수정해보세요. (약간의 조건문과 반복문이 필요할지도 모르겠습니다). 어떻게 성별에 따라, 혹은 취미의 개수에 따라 문장이 다르게 구성되어야할지 생각해보세요 .

- -
-

노트: 하다가 막힌다면, 저희가 제공하는 GitHub 저장소의 모법 답안 (그리고 실행 버전)을 참고하세요. 하지만 일단 직접 해보시죠!

-
- -

객체 인스턴스를 생성하는 다른 방법들

- -

여태까지 객체 인스턴스를 만드는 두 가지 방법을 살펴보았습니다. 객체 리터럴을 선언하는 방법과, 생성자 함수를 사용하는 방법(위를 보세요)이죠.

- -

이것들은 잘 동작하지만, 다른 방법들도 있습니다. 웹에서 정보를 찾다가 마주칠 경우를 대비해 익숙해져보는 것도 좋을 것 같습니다.

- -

Object() 생성자

- -

첫번째로, 새 객체를 만들기 위해 Object() 생성자를 사용할 수 있습니다. 네, 최초의 object 역시 생성자를 가지고 있습니다. 빈 객체를 생성하는 함수이죠.

- -
    -
  1. 브라우저의 자바스크립트 콘솔에 아래 코드를 입력해보세요: -
    var person1 = new Object();
    -
  2. -
  3. 이는 빈 객체를 person1 변수에 담습니다. 이제 이 객체에 점 표기법이나 괄호 표기법을 이용해 프로퍼티와 메소드들을 추가할 수 있습니다. 이 예제 코드를 콘솔 창에 입력해보세요. -
    person1.name = 'Chris';
    -person1['age'] = 38;
    -person1.greeting = function() {
    -  alert('Hi! I\'m ' + this.name + '.');
    -};
    -
  4. -
  5. 사전에 프로퍼티와 메소드를 정의하기 위해, Object() 생성자의 파라미터로 객체 리터럴을 전달할 수도 있습니다. 이 예제 코드를 콘솔 창에 입력해보세요. -
    var person1 = new Object({
    -  name: 'Chris',
    -  age: 38,
    -  greeting: function() {
    -    alert('Hi! I\'m ' + this.name + '.');
    -  }
    -});
    -
  6. -
- -

create() 함수 사용

- -

생성자는 여러분의 코드에 규칙을 부여해줍니다. 일단 생성자를 만들어두면, 이를 이용해 원하는대로 인스턴스를 생성할 수 있고, 이 인스턴스가 어디서 유래했는지 명백합니다.

- -

하지만 몇몇 사람들은 객체 인스턴스들을 생성할 때 먼저 생성자를 만들기를 원하지 않습니다. 특히 그들이 적은 수의 객체만을 생성할 때 말이죠. 자바스크립트는 create()라는 내장함수를 가지고 있어 이를 가능하게 해줍니다. 이를 이용하면, 이미 존재하는 객체를 이용해 새로운 객체를 만들 수 있습니다.

- -
    -
  1. 이전 섹션에서 완료한 예제를 브라우저에서 열어, 아래 코드를 콘솔창에 입력해보세요. -
    var person2 = Object.create(person1);
    -
  2. -
  3. 이제 이 코드를 입력해보세요. -
    person2.name
    -person2.greeting()
    -
  4. -
- -

person2person1을 기반으로 만들어졌습니다. 새 객체는 원 객체와 같은 프로퍼티와 메소드들을 가집니다. 

- -

create() 함수의 한 가지 단점은 익스플로러 8에서는 지원하지 않는다는 점입니다. 따라서 오래된 브라우저들까지 지원하고 싶다면 생성자를 사용하는 것이 효과적입니다.

- -

다음에 create() 함수의 효과에 대해 더 살펴보겠습니다.

- -

요약

- -

이 글은 객체지향 이론을 요약하여 설명해줍니다. 모든 부분을 다루지는 않지만, 지금 어떤 것들을 다루고 있는지에 대한 아이디어 정도는 얻을 수 있습니다. 게다가 객체 인스턴스를 생성하는 여러가지 방법에 대해서도 알아보기 시작했습니다.

- -

다음 글에서는 자바스크립트 객체 프로토타입에 대해 탐험해보겠습니다.

- -

{{PreviousMenuNext("Learn/JavaScript/Objects/Basics", "Learn/JavaScript/Objects/Object_prototypes", "Learn/JavaScript/Objects")}}

- -

In this module

- - - -

 

diff --git a/files/pt-br/_redirects.txt b/files/pt-br/_redirects.txt index da89c57846..82705e2d46 100644 --- a/files/pt-br/_redirects.txt +++ b/files/pt-br/_redirects.txt @@ -69,9 +69,9 @@ /pt-BR/docs/Aprender/JavaScript/Objetos /pt-BR/docs/Learn/JavaScript/Objects /pt-BR/docs/Aprender/JavaScript/Objetos/Adding_bouncing_balls_features /pt-BR/docs/Learn/JavaScript/Objects/Adding_bouncing_balls_features /pt-BR/docs/Aprender/JavaScript/Objetos/Básico /pt-BR/docs/Learn/JavaScript/Objects/Basics -/pt-BR/docs/Aprender/JavaScript/Objetos/Herança /pt-BR/docs/Learn/JavaScript/Objects/Inheritance +/pt-BR/docs/Aprender/JavaScript/Objetos/Herança /pt-BR/docs/Learn/JavaScript/Objects/Classes_in_JavaScript /pt-BR/docs/Aprender/JavaScript/Objetos/JSON /pt-BR/docs/Learn/JavaScript/Objects/JSON -/pt-BR/docs/Aprender/JavaScript/Objetos/Object-oriented_JS /pt-BR/docs/Learn/JavaScript/Objects/Object-oriented_JS +/pt-BR/docs/Aprender/JavaScript/Objetos/Object-oriented_JS /pt-BR/docs/conflicting/Learn/JavaScript/Objects/Classes_in_JavaScript /pt-BR/docs/Aprender/JavaScript/Objetos/Object_building_practice /pt-BR/docs/Learn/JavaScript/Objects/Object_building_practice /pt-BR/docs/Aprender/JavaScript/Objetos/Object_prototypes /pt-BR/docs/Learn/JavaScript/Objects/Object_prototypes /pt-BR/docs/Aprender/Learning_and_getting_help /pt-BR/docs/Learn/Learning_and_getting_help @@ -399,6 +399,8 @@ /pt-BR/docs/Learn/JavaScript/First_steps/O_que_e_JavaScript /pt-BR/docs/Learn/JavaScript/First_steps/What_is_JavaScript /pt-BR/docs/Learn/JavaScript/First_steps/Teste_suas_habilidades:_variaveis /pt-BR/docs/Learn/JavaScript/First_steps/Test_your_skills:_variables /pt-BR/docs/Learn/JavaScript/First_steps/Variáveis /pt-BR/docs/Learn/JavaScript/First_steps/Variables +/pt-BR/docs/Learn/JavaScript/Objects/Inheritance /pt-BR/docs/Learn/JavaScript/Objects/Classes_in_JavaScript +/pt-BR/docs/Learn/JavaScript/Objects/Object-oriented_JS /pt-BR/docs/conflicting/Learn/JavaScript/Objects/Classes_in_JavaScript /pt-BR/docs/Learn/Server-side/Django/Hospedagem /pt-BR/docs/Learn/Server-side/Django/Deployment /pt-BR/docs/Learn/Server-side/Django/Introdução /pt-BR/docs/Learn/Server-side/Django/Introduction /pt-BR/docs/Learn/Server-side/Django/Sessões /pt-BR/docs/Learn/Server-side/Django/Sessions diff --git a/files/pt-br/_wikihistory.json b/files/pt-br/_wikihistory.json index 638fa546c6..a742393df3 100644 --- a/files/pt-br/_wikihistory.json +++ b/files/pt-br/_wikihistory.json @@ -2328,7 +2328,7 @@ "webfelipemaia" ] }, - "Learn/JavaScript/Objects/Inheritance": { + "Learn/JavaScript/Objects/Classes_in_JavaScript": { "modified": "2020-07-16T22:32:15.784Z", "contributors": [ "WellingtonFR", @@ -2344,14 +2344,6 @@ "greysonf" ] }, - "Learn/JavaScript/Objects/Object-oriented_JS": { - "modified": "2020-07-16T22:32:07.136Z", - "contributors": [ - "rpizzolato", - "WellingtonFR", - "greysonf" - ] - }, "Learn/JavaScript/Objects/Object_building_practice": { "modified": "2020-07-16T22:32:32.103Z", "contributors": [ @@ -16318,6 +16310,14 @@ "augustowebd" ] }, + "conflicting/Learn/JavaScript/Objects/Classes_in_JavaScript": { + "modified": "2020-07-16T22:32:07.136Z", + "contributors": [ + "rpizzolato", + "WellingtonFR", + "greysonf" + ] + }, "conflicting/MDN/Contribute/Getting_started": { "modified": "2019-03-23T23:38:18.895Z", "contributors": [ diff --git a/files/pt-br/conflicting/learn/javascript/objects/classes_in_javascript/index.html b/files/pt-br/conflicting/learn/javascript/objects/classes_in_javascript/index.html new file mode 100644 index 0000000000..125287707b --- /dev/null +++ b/files/pt-br/conflicting/learn/javascript/objects/classes_in_javascript/index.html @@ -0,0 +1,276 @@ +--- +title: JavaScript orientado a objetos para iniciantes +slug: conflicting/Learn/JavaScript/Objects/Classes_in_JavaScript +translation_of: Learn/JavaScript/Objects/Object-oriented_JS +original_slug: Learn/JavaScript/Objects/Object-oriented_JS +--- +
{{LearnSidebar}}
+ +
{{PreviousMenuNext("Learn/JavaScript/Objects/Basics", "Learn/JavaScript/Objects/Object_prototypes", "Learn/JavaScript/Objects")}}
+ +

Com o básico fora do caminho, agora vamos nos concentrar no JavaScript orientado a objetos (OOJS) — Este artigo apresenta uma visão básica da teoria de programação orientada a objeto (OOP), em seguida, explora como o JavaScript emula as classes de objetos através de funções de construtor e como criar instâncias de objeto.

+ + + + + + + + + + + + +
Pré-requisitos:Alfabetização básica em informática, um entendimento básico de HTML e CSS, familiaridade com o básico do JavaScript (consulte Primeiros passos e Blocos de construção) e noções básicas do OOJS (consulte Introdução aos objetos).
Objetivo:Para entender a teoria básica por trás da programação orientada a objetos, como isso se relaciona com JavaScript ("tudo é um objeto") e como criar construtores e instâncias de objetos.
+ +

Programação orientada a objetos - o básico

+ +

Para começar, vamos dar uma visão simplista e de alto nível do que é programação orientada a objeto (OOP). Dizemos simplista, porque a OOP pode rapidamente se tornar muito complicada, e dar a ela um tratamento completo agora provavelmente confundiria mais do que ajuda. A idéia básica da OOP é que usamos objetos para modelar coisas do mundo real que queremos representar dentro de nossos programas, e / ou fornecer uma maneira simples de acessar funcionalidades que de outra forma seriam difíceis ou impossíveis de usar.

+ +

Os objetos podem conter dados e códigos relacionados, que representam informações sobre o que você está tentando modelar e a funcionalidade ou o comportamento que você deseja ter. Dados de objeto (e muitas vezes, funções também) podem ser armazenados ordenadamente (a palavra oficial é encapsulados) dentro de um pacote de objetos (que pode ser dado um nome específico para se referir, que é às vezes chamado de namespace), tornando fácil de estruturar e acessar; objetos também são comumente usados como armazenamentos de dados que podem ser facilmente enviados pela rede.

+ +

Definindo um modelo de objeto

+ +

Vamos considerar um programa simples que exibe informações sobre os alunos e professores de uma escola. Aqui vamos olhar para a teoria OOP em geral, não no contexto de qualquer linguagem de programação específica.

+ +

Para começar, poderíamos retornar ao nosso tipo de objeto Person do nosso primeiro artigo de objetos, que define os dados genéricos e a funcionalidade de uma pessoa. Há muitas coisas que você poderia saber sobre uma pessoa (endereço, altura, tamanho do sapato, perfil de DNA, número de passaporte, traços de personalidade significativos ...), mas neste caso estamos interessados apenas em mostrar seu nome, idade, sexo e interesses, e também queremos ser capazes de escrever uma breve introdução sobre eles com base nesses dados e fazê-los dizer oi. Isso é conhecido como abstração — criando um modelo simples de uma coisa mais complexa, que representa seus aspectos mais importantes de uma forma que é fácil trabalhar com os objetivos do nosso programa.

+ +

+ +

Criando objetos reais

+ +

De nossa classe, podemos criar instâncias de objeto — objetos que contêm os dados e a funcionalidade definidos na classe. Da nossa classe Person, podemos criar algumas pessoas reais:

+ +

+ +

Quando uma instância de objeto é criada a partir de uma classe, a função construtora da classe é executada para criá-la. Esse processo de criação de uma instância de objeto de uma classe é chamado de instanciação — a instância do objeto é instanciada a partir da classe.

+ +

Classes especialistas

+ +

Neste caso, não queremos pessoas genéricas — queremos professores e alunos, que são tipos mais específicos de pessoas. Em OOP, podemos criar novas classes com base em outras classes — essas novas classes filhas podem herdar os recursos de dados e código de sua classe pai, para que você possa reutilizar a funcionalidade comum a todos os tipos de objetos em vez de duplicá-los. Onde a funcionalidade difere entre as classes, você pode definir recursos especializados diretamente sobre eles, conforme necessário.

+ +

+ +

Isso é realmente útil — professores e alunos compartilham muitos recursos comuns, como nome, sexo e idade, por isso é conveniente definir apenas esses recursos uma vez. Você também pode definir o mesmo recurso separadamente em classes diferentes, já que cada definição desse recurso estará em um namespace diferente. Por exemplo, a saudação de um aluno pode estar no formato "Yo, I'm [firstName]" (por exemplo, Yo, I'm Sam), enquanto um professor pode usar algo mais formal, como "Olá, meu nome é [Prefixo [lastName], e eu ensino [Subject]. " (por exemplo Olá, Meu nome é Mr Griffiths, e eu ensino Química).

+ +
+

Nota: A palavra chique para a capacidade de múltiplos tipos de objeto de implementar a mesma funcionalidade é o polimorfismo. Apenas no caso de você estar se perguntando.

+
+ +

Agora você pode criar instâncias de objetos de suas classes filhas. Por exemplo:

+ +

+ +

No restante do artigo, começaremos a analisar como a teoria da POO pode ser colocada em prática no JavaScript.

+ +

Construtores e instâncias de objeto

+ +

O JavaScript usa funções especiais chamadas funções construtoras para definir objetos e seus recursos. Eles são úteis porque muitas vezes você encontrará situações em que não sabe quantos objetos estará criando; Os construtores fornecem os meios para criar quantos objetos forem necessários de forma eficaz, anexando dados e funções a eles, conforme necessário.

+ +

Vamos explorar a criação de classes por meio de construtores e criar instâncias de objeto a partir deles em JavaScript. Primeiro de tudo, gostaríamos que você fizesse uma nova cópia local do arquivo oojs.html que vimos em nosso primeiro artigo Objetos.

+ +

Um exemplo simples

+ +
    +
  1. Vamos começar observando como você pode definir uma pessoa com uma função normal. Adicione esta função dentro do elemento script: + +
    function createNewPerson(name) {
    +  var obj = {};
    +  obj.name = name;
    +  obj.greeting = function() {
    +    alert('Hi! I\'m ' + obj.name + '.');
    +  };
    +  return obj;
    +}
    +
  2. +
  3. Agora você pode criar uma nova pessoa chamando essa função — tente as seguintes linhas no console JavaScript do seu navegador: +
    var salva = createNewPerson('Salva');
    +salva.name;
    +salva.greeting();
    + Isso funciona bem o suficiente, mas é um pouco prolixo; Se sabemos que queremos criar um objeto, por que precisamos criar explicitamente um novo objeto vazio e devolvê-lo? Felizmente, o JavaScript nos fornece um atalho útil, na forma de funções de construtor — vamos criar um agora!
  4. +
  5. Substitua sua função anterior pelo seguinte: +
    function Person(name) {
    +  this.name = name;
    +  this.greeting = function() {
    +    alert('Hi! I\'m ' + this.name + '.');
    +  };
    +}
    +
  6. +
+ +

A função de construtor é a versão do JavaScript de uma classe. Você notará que ela tem todos os recursos que você espera em uma função, embora ela não retorne nada ou crie explicitamente um objeto — ela basicamente define propriedades e métodos. Você verá a palavra-chave this sendo usada aqui também — é basicamente dizer que sempre que uma dessas instâncias de objeto é criada, a propriedade name  do objeto será igual ao valor do nome passado à chamada do construtor, e o método greeting() usará o valor do nome passado para a chamada do construtor também.

+ +
+

Nota: Um nome de função de construtor geralmente começa com uma letra maiúscula — essa convenção é usada para tornar as funções do construtor mais fáceis de reconhecer no código.

+
+ +

Então, como podemos chamar um construtor para criar alguns objetos?

+ +
    +
  1. Adicione as seguintes linhas abaixo da sua adição de código anterior: +
    var person1 = new Person('Bob');
    +var person2 = new Person('Sarah');
    +
  2. +
  3. Salve seu código e recarregue-o no navegador e tente inserir as seguintes linhas em seu console JS: +
    person1.name
    +person1.greeting()
    +person2.name
    +person2.greeting()
    +
  4. +
+ +

Legal! Você verá agora que temos dois novos objetos na página, cada um deles armazenado em um namespace diferente — quando você acessa suas propriedades e métodos, é necessário iniciar chamadas com person1 ou person2; a funcionalidade contida é cuidadosamente empacotada para que não entre em conflito com outras funcionalidades. Eles, no entanto, têm a mesma propriedade de name e o método greeting() disponível. Observe que eles estão usando seu próprio valor de name que foi atribuído a eles quando foram criados; Esta é uma razão pela qual é muito importante usar this, então eles usarão seus próprios valores e não algum outro valor.

+ +

Vamos ver novamente as chamadas do construtor:

+ +
var person1 = new Person('Bob');
+var person2 = new Person('Sarah');
+ +

Em cada caso, a palavra-chave new é usada para informar ao navegador que queremos criar uma nova instância de objeto, seguida pelo nome da função com seus parâmetros obrigatórios contidos entre parênteses, e o resultado é armazenado em uma variável — muito semelhante a como uma função padrão é chamada. Cada instância é criada de acordo com esta definição:

+ +
function Person(name) {
+  this.name = name;
+  this.greeting = function() {
+    alert('Hi! I\'m ' + this.name + '.');
+  };
+}
+ +

Após a criação dos novos objetos, as variáveis person1person2 contêm os seguintes objetos:

+ +
{
+  name: 'Bob',
+  greeting: function() {
+    alert('Hi! I\'m ' + this.name + '.');
+  }
+}
+
+{
+  name: 'Sarah',
+  greeting: function() {
+    alert('Hi! I\'m ' + this.name + '.');
+  }
+}
+ +

Note que quando estamos chamando nossa função de construtor, estamos definindo greeting() toda vez, o que não é ideal. Para evitar isso, podemos definir funções no protótipo, que veremos mais adiante.

+ +

Criando nosso construtor acabado

+ +

O exemplo que vimos acima foi apenas um exemplo simples para começarmos. Vamos agora começar e criar nossa função final do construtor Person().

+ +
    +
  1. Remova o código que você inseriu até agora e inclua este construtor de substituição — isso é exatamente o mesmo que o exemplo simples em princípio, com um pouco mais de complexidade: +
    function Person(first, last, age, gender, interests) {
    +  this.name = {
    +    'first': first,
    +    'last' : last
    +  };
    +  this.age = age;
    +  this.gender = gender;
    +  this.interests = interests;
    +  this.bio = function() {
    +    alert(this.name.first + ' ' + this.name.last + ' is ' + this.age + ' years old. He likes ' + this.interests[0] + ' and ' + this.interests[1] + '.');
    +  };
    +  this.greeting = function() {
    +    alert('Hi! I\'m ' + this.name.first + '.');
    +  };
    +}
    +
  2. +
  3. Agora adicione a seguinte linha abaixo, para criar uma instância de objeto a partir dela: +
    var person1 = new Person('Bob', 'Smith', 32, 'male', ['music', 'skiing']);
    +
  4. +
+ +

Agora você verá que pode acessar as propriedades e os métodos exatamente como fizemos anteriormente — Tente isso no seu console JS:

+ +
person1['age']
+person1.interests[1]
+person1.bio()
+// etc.
+ +
+

Nota: Se você está tendo problemas para fazer isso funcionar, tente comparar seu código com a nossa versão — veja o código em oojs-class-finished.html (também você pode ve-lo sendo executado aqui).

+
+ +

Exercícios adicionais

+ +

Para começar, tente adicionar mais algumas linhas de criação de objetos e tente obter e configurar os membros das instâncias de objetos resultantes.

+ +

Além disso, há alguns problemas com nosso método  bio() — a saída sempre inclui o pronome "Ele", mesmo que sua pessoa seja do sexo feminino ou alguma outra classificação de gênero preferida. E a biografia incluirá apenas dois interesses, mesmo que mais sejam listados na matriz interests. Você pode descobrir como corrigir isso na definição de classe (construtor)? Você pode colocar qualquer código que você gosta dentro de um construtor (você provavelmente precisará de alguns condicionais e um loop). Pense em como as sentenças devem ser estruturadas de maneira diferente dependendo do gênero e dependendo se o número de interesses listados é 1, 2 ou mais de 2.

+ +
+

Note: If you get stuck, we have provided an answer inside our GitHub repo (see it live) — try writing it yourself first though!

+
+ +

Outras maneiras de criar instâncias de objeto

+ +

Até agora, vimos duas maneiras diferentes de criar uma instância de objeto — declarar um literal de objeto, e usar uma função de construtor (veja acima).

+ +

Isso faz sentido, mas existem outras maneiras — queremos familiarizá-lo com essas informações caso você as encontre em suas viagens pela Web.

+ +

O construtor Object() 

+ +

Primeiro de tudo, você pode usar o construtor Object() para criar um novo objeto. Sim, até objetos genéricos possuem um construtor, o que gera um objeto vazio.

+ +
    +
  1. Tente inserir isso no console JavaScript do seu navegador: +
    var person1 = new Object();
    +
  2. +
  3. Isso armazena um objeto vazio na variável person1. Você pode adicionar propriedades e métodos a esse objeto usando a notação de pontos ou colchetes conforme desejado; tente estes exemplos no seu console: +
    person1.name = 'Chris';
    +person1['age'] = 38;
    +person1.greeting = function() {
    +  alert('Hi! I\'m ' + this.name + '.');
    +};
    +
  4. +
  5. Você também pode passar um literal de objeto para o construtor Object() como um parâmetro, para preenchê-lo com propriedades / métodos. Tente isso no seu console JS: +
    var person1 = new Object({
    +  name: 'Chris',
    +  age: 38,
    +  greeting: function() {
    +    alert('Hi! I\'m ' + this.name + '.');
    +  }
    +});
    +
  6. +
+ +

Usando o método create()

+ +

Os construtores podem ajudá-lo a fornecer seu pedido de código — você pode criar construtores em um único local e, em seguida, criar instâncias conforme necessário, e fica claro de onde eles vieram.

+ +

No entanto, algumas pessoas preferem criar instâncias de objeto sem primeiro criar construtores, especialmente se estiverem criando apenas algumas instâncias de um objeto. JavaScript tem um método embutido chamado create() que permite que você faça isso. Com ele, você pode criar um novo objeto com base em qualquer objeto existente.

+ +
    +
  1. Com o exercício concluído das seções anteriores carregadas no navegador, tente isso no seu console JavaScript: +
    var person2 = Object.create(person1);
    +
  2. +
  3. Agora tente estes: +
    person2.name
    +person2.greeting()
    +
  4. +
+ +

Você verá que a person2 foi criada com base na  person1  —  ela tem as mesmas propriedades e métodos disponíveis para ela.

+ +

Uma limitação do create()  é que o IE8 não o suporta. Então os construtores são mais efetivos se você quiser que funcione em navegadores antigos.

+ +

Vamos explorar os efeitos de create() em mais detalhes posteriormente.

+ +

Sumário

+ +

Este artigo forneceu uma visão simplificada da teoria orientada a objetos — isso não é toda a história, mas dá uma idéia do que estamos lidando aqui. Além disso, começamos a analisar diferentes maneiras de gerar instâncias de objetos.

+ +

No próximo artigo, vamos explorar os protótipos de objetos JavaScript.

+ +

{{PreviousMenuNext("Learn/JavaScript/Objects/Basics", "Learn/JavaScript/Objects/Object_prototypes", "Learn/JavaScript/Objects")}}

+ +

Neste módulo

+ + diff --git a/files/pt-br/learn/javascript/objects/classes_in_javascript/index.html b/files/pt-br/learn/javascript/objects/classes_in_javascript/index.html new file mode 100644 index 0000000000..7b144c91e0 --- /dev/null +++ b/files/pt-br/learn/javascript/objects/classes_in_javascript/index.html @@ -0,0 +1,403 @@ +--- +title: Herança em JavaScript +slug: Learn/JavaScript/Objects/Classes_in_JavaScript +translation_of: Learn/JavaScript/Objects/Inheritance +original_slug: Learn/JavaScript/Objects/Inheritance +--- +
{{LearnSidebar}}
+ +
{{PreviousMenuNext("Learn/JavaScript/Objects/Object_prototypes", "Learn/JavaScript/Objects/JSON", "Learn/JavaScript/Objects")}}
+ +

Com a maior parte dos detalhes principais do OOJS agora explicados, este artigo mostra como criar classes de objetos "child" (construtores) que herdam recursos de suas classes "parent". Além disso, apresentamos alguns conselhos sobre quando e onde você pode usar o OOJS e veja como as classes são tratadas na sintaxe moderna do ECMAScript.

+ + + + + + + + + + + + +
Pré-requisitos:Conhecimento básico de computação, conhecimento básico de HTML e CSS, familiaridade com com o básico de Javascript (veja Primeiros passos e Construindo blocos) e OOJS básico (veja Introdução a objetos).
Objetivo:Entender como é possível implementar a herança em Javascript.
+ +

Herança Prototipada

+ +

Até agora vimos alguma herança em ação — vimos como funcionam as cadeias de protótipos e como os membros são herdados subindo em uma cadeia. Mas principalmente isso envolveu funções internas do navegador. Como criamos um objeto em JavaScript que herda de outro objeto?

+ +

Vamos explorar como fazer isso com um exemplo concreto.

+ +

Começando

+ +

Primeiro de tudo, faça uma cópia local do arquivo oojs-class-inheritance-start.html (veja também ao vivo). Aqui dentro você encontrará o mesmo exemplo de construtor  Person() que utilizamos durante todo o módulo, com uma pequena diferença — definimos apenas as propriedades dentro do construtor:

+ +
function Person(first, last, age, gender, interests) {
+  this.name = {
+    first,
+    last
+  };
+  this.age = age;
+  this.gender = gender;
+  this.interests = interests;
+};
+ +

Os métodos são todos definidos no protótipo do construtor. Por exemplo:

+ +
Person.prototype.greeting = function() {
+  alert('Hi! I\'m ' + this.name.first + '.');
+};
+ +
+

Nota: No código fonte, você também verá os métodos bio() e farewell() definidos. Depois você verá como eles podem ser herdados por outros construtores.

+
+ +

Digamos que quiséssemos criar uma classe Teacher, como a que descrevemos em nossa definição inicial orientada a objetos, que herda todos os membros de Person, mas também inclui:

+ +
    +
  1. Uma nova propriedade, subject — isso irá conter o assunto que o professor ensina.
  2. +
  3. Um método greeting() atualizado, que soa um pouco mais formal do que o método padrão  greeting() — mais adequado para um professor que se dirige a alguns alunos da escola.
  4. +
+ +

Definindo uma função construtora Teacher()

+ +

A primeira coisa que precisamos fazer é criar um construtor Teacher() — adicione o seguinte abaixo do código existente:

+ +
function Teacher(first, last, age, gender, interests, subject) {
+  Person.call(this, first, last, age, gender, interests);
+
+  this.subject = subject;
+}
+ +

Isto parece similar ao construtor Person de várias maneiras, mas há algo estranho aqui que nós não vimos antes — a função call(). Esta função basicamente permite chamar uma função definida em outro lugar, mas no contexto atual. O primeiro parâmetro especifica o valor this que você deseja usar ao executar a função, e os outros parâmetros são aqueles que devem ser passados para a função quando ela é invocada.

+ +

Nós queremos que o construtor Teacher() pegue os mesmos parâmetros que o construtor Person() de onde ele está herdando, então especificamos todos eles como parâmetros na chamada call().

+ +

A última linha dentro do construtor simplesmente define a nova propriedade subject que os professores terão, que pessoas genéricas não possuem.

+ +

Como nota, poderíamos simplesmente ter feito isso:

+ +
function Teacher(first, last, age, gender, interests, subject) {
+  this.name = {
+    first,
+    last
+  };
+  this.age = age;
+  this.gender = gender;
+  this.interests = interests;
+  this.subject = subject;
+}
+ +

Mas isso é apenas redefinir as propriedades de novo, não herdá-las de  Person(), de modo que ela derrota o ponto que estamos tentando fazer. Também leva mais linhas de código.

+ +

Herdando de um construtor sem parâmetros

+ +

Observe que, se o construtor do qual você está herdando não tomar seus valores de propriedade de parâmetros, não será necessário especificá-los como argumentos adicionais em call(). Então, por exemplo, se você tivesse algo realmente simples assim:

+ +
function Brick() {
+  this.width = 10;
+  this.height = 20;
+}
+ +

Você pode herdar as propriedades widthheight fazendo isso (assim como as outras etapas descritas abaixo, é claro):

+ +
function BlueGlassBrick() {
+  Brick.call(this);
+
+  this.opacity = 0.5;
+  this.color = 'blue';
+}
+ +

Observe que apenas especificamos this dentro de call() — nenhum outro parâmetro é necessário, já que não estamos herdando propriedades do pai que são configuradas por meio de parâmetros.

+ +

Definindo o protótipo e referência de construtor do Teacher()

+ +

Tudo está bem até agora, mas nós temos um problema. Nós definimos um novo construtor, e ele tem uma propriedade  prototype, que por padrão apenas contém uma referência à própria função construtora. Ele não contém os métodos da propriedade prototype do construtor Person. Para ver isso, insira Object.getOwnPropertyNames(Teacher.prototype) no campo de entrada de texto ou no seu console JavaScript. Em seguida, insira-o novamente, substituindo Teacher por Person. O novo construtor também não herda esses métodos. Para ver isso, compare as saídas de Person.prototype.greetingTeacher.prototype.greeting. Precisamos obter Teacher() para herdar os métodos definidos no protótipo Person(). Então, como fazemos isso?

+ +
    +
  1. Adicione a seguinte linha abaixo da sua adição anterior: +
    Teacher.prototype = Object.create(Person.prototype);
    + Aqui nosso amigo create() vem para o resgate novamente. Nesse caso, estamos usando para criar um novo objeto e torná-lo o valor de Teacher.prototype. O novo objeto tem Person.prototype como seu protótipo e, portanto, herdará, se e quando necessário, todos os métodos disponíveis no Person.prototype.
  2. +
  3. Precisamos fazer mais uma coisa antes de prosseguirmos. Depois de adicionar a última linha, a propriedade constructor de Teacher.prototype agora é igual a Person(), porque apenas definimos Teacher.prototype para fazer referência a um objeto que herda suas propriedades de Person.prototype! Tente salvar seu código, carregar a página em um navegador e inserir Teacher.prototype.constructor no console para verificar.
  4. +
  5. Isso pode se tornar um problema, então precisamos definir isso corretamente. Você pode fazer isso voltando ao seu código-fonte e adicionando a seguinte linha na parte inferior: +
    Object.defineProperty(Teacher.prototype, 'constructor', {
    +    value: Teacher,
    +    enumerable: false, // so that it does not appear in 'for in' loop
    +    writable: true });
    +
  6. +
  7. Agora, se você salvar e atualizar, entrar em Teacher.prototype.constructor deve retornar Teacher(), conforme desejado, além de estarmos herdando de Person()!
  8. +
+ +

Dar a Teacher() uma nova função greeting() 

+ +

Para finalizar nosso código, precisamos definir uma nova função greeting() no construtor Teacher().

+ +

A maneira mais fácil de fazer isso é defini-lo no protótipo do Teacher() — adicione o seguinte na parte inferior do seu código:

+ +
Teacher.prototype.greeting = function() {
+  var prefix;
+
+  if (this.gender === 'male' || this.gender === 'Male' || this.gender === 'm' || this.gender === 'M') {
+    prefix = 'Mr.';
+  } else if (this.gender === 'female' || this.gender === 'Female' || this.gender === 'f' || this.gender === 'F') {
+    prefix = 'Mrs.';
+  } else {
+    prefix = 'Mx.';
+  }
+
+  alert('Hello. My name is ' + prefix + ' ' + this.name.last + ', and I teach ' + this.subject + '.');
+};
+ +

Isso alerta a saudação do professor, que também usa um prefixo de nome apropriado para seu gênero, elaborado usando uma instrução condicional.

+ +

Testando o exemplo

+ +

Agora que você digitou todo o código, tente criar uma instância de objeto do Teacher() colocando o seguinte na parte inferior do seu JavaScript (ou algo semelhante à sua escolha):

+ +
var teacher1 = new Teacher('Dave', 'Griffiths', 31, 'male', ['football', 'cookery'], 'mathematics');
+ +

Agora salve e atualize e tente acessar as propriedades e os métodos do novo objeto teacher1, por exemplo:

+ +
teacher1.name.first;
+teacher1.interests[0];
+teacher1.bio();
+teacher1.subject;
+teacher1.greeting();
+teacher1.farewell();
+ +

Tudo isso deve funcionar bem. As consultas nas linhas 1, 2, 3 e 6 acessam membros herdados do construtor genérico Person() (class). A consulta na linha 4 acessa um membro que está disponível somente no construtor mais especializado  Teacher() (class). A consulta na linha 5 teria acessado um membro herdado de Person(), exceto pelo fato de que Teacher() tem seu próprio membro com o mesmo nome, portanto, a consulta acessa esse membro.

+ +
+

Note: If you have trouble getting this to work, compare your code to our finished version (see it running live also).

+
+ +

A técnica que abordamos aqui não é a única maneira de criar classes herdadas em JavaScript, mas funciona bem e dá uma boa idéia sobre como implementar a herança em JavaScript.

+ +

Você também pode estar interessado em conferir alguns dos novos recursos {{glossary("ECMAScript")}} que nos permitem fazer herança mais claramente em JavaScript (veja Classes). Nós não cobrimos esses aqui, pois eles ainda não são suportados amplamente pelos navegadores. Todas as outras construções de código que discutimos neste conjunto de artigos são suportadas desde o IE9 ou anterior, e existem maneiras de obter suporte anterior a isso.

+ +

Uma maneira comum é usar uma biblioteca JavaScript — a maioria das opções populares tem um conjunto fácil de funcionalidade disponível para fazer herança com mais facilidade e rapidez. CoffeeScript por exemplo, fornece class, extends, etc.

+ +

Um exercício adicional

+ +

Em nossa seção de teoria OOP, incluímos também uma classe Student como um conceito, que herda todos os recursos de Person, e também tem um método  greeting() diferente de Person que é muito mais informal do que a saudação do Teacher. Dê uma olhada na aparência da saudação do aluno nessa seção e tente implementar seu próprio construtor Student() que herda todos os recursos de Person(), e implemente a função greeting() diferente.

+ +
+

Note: If you have trouble getting this to work, have a look at our finished version (see it running live also).

+
+ +

Sumário de membro do objeto

+ +

Resumindo, você basicamente tem três tipos de propriedade / método para se preocupar:

+ +
    +
  1. Aqueles definidos dentro de uma função construtora que são dadas a instâncias de objetos. Estes são bastante fáceis de detectar — em seu próprio código personalizado, eles são os membros definidos dentro de um construtor usando as linhas this.x = x ; no código do navegador, eles são os membros disponíveis apenas para instâncias de objetos (geralmente criados chamando um construtor usando a palavra-chave new, por exemplo, var myInstance = new myConstructor()).
  2. +
  3. Aqueles definidos diretamente no próprio construtor, que estão disponíveis apenas no construtor. Geralmente, eles estão disponíveis apenas em objetos de navegador internos e são reconhecidos por serem encadeados diretamente em um construtor, não em uma instância. Por exemplo, Object.keys().
  4. +
  5. Aqueles definidos no protótipo de um construtor, que são herdados por todas as instâncias e herdam as classes de objetos. Estes incluem qualquer membro definido na propriedade de protótipo de um Construtor, por ex. myConstructor.prototype.x().
  6. +
+ +

Se você não tem certeza de qual é qual, não se preocupe com isso ainda — você ainda está aprendendo e a familiaridade virá com a prática.

+ +

Classes ECMAScript 2015

+ +

O ECMAScript 2015 introduz a sintaxe de classe em JavaScript como uma maneira de escrever classes reutilizáveis usando uma sintaxe mais fácil e mais limpa, que é mais semelhante a classes em C ++ ou Java. Nesta seção, converteremos os exemplos Pessoa e Professor da herança protótipo para as classes, para mostrar como é feito.

+ +
+

Nota: Essa forma moderna de escrever classes é suportada em todos os navegadores modernos, mas ainda vale a pena saber como a herança prototípica subjacente, caso você trabalhe em um projeto que exija suporte a um navegador que não suporte essa sintaxe (mais notavelmente o Internet Explorer) .

+
+ +

Vejamos uma versão reescrita do exemplo Person, estilo de classe:

+ +
class Person {
+  constructor(first, last, age, gender, interests) {
+    this.name = {
+      first,
+      last
+    };
+    this.age = age;
+    this.gender = gender;
+    this.interests = interests;
+  }
+
+  greeting() {
+    console.log(`Hi! I'm ${this.name.first}`);
+  };
+
+  farewell() {
+    console.log(`${this.name.first} has left the building. Bye for now!`);
+  };
+}
+
+ +

A declaração class indica que estamos criando uma nova classe. Dentro deste bloco, definimos todos os recursos da classe:

+ + + +

Agora podemos instanciar instâncias de objeto usando o operador new, da mesma maneira que fizemos antes:

+ +
let han = new Person('Han', 'Solo', 25, 'male', ['Smuggling']);
+han.greeting();
+// Hi! I'm Han
+
+let leia = new Person('Leia', 'Organa', 19, 'female', ['Government']);
+leia.farewell();
+// Leia has left the building. Bye for now
+
+ +
+

Nota: Sob o capô, suas classes estão sendo convertidas em modelos de herança protótipos — isso é apenas açúcar sintático. Mas tenho certeza que você concordará que é mais fácil escrever.

+
+ +

Herança com sintaxe de classe

+ +

Acima nós criamos uma classe para representar uma pessoa. Eles têm uma série de atributos que são comuns a todas as pessoas; Nesta seção, criaremos nossa classe especializada Teacher, tornando-a herdada de Person usando a sintaxe de classe moderna. Isso é chamado de criação de uma subclasse ou subclasse.

+ +

Para criar uma subclasse, usamos a palavra-chave extends para informar ao JavaScript a classe na qual queremos basear nossa classe.

+ +
class Teacher extends Person {
+  constructor(first, last, age, gender, interests, subject, grade) {
+    this.name = {
+      first,
+      last
+    };
+
+  this.age = age;
+  this.gender = gender;
+  this.interests = interests;
+  // subject and grade are specific to Teacher
+  this.subject = subject;
+  this.grade = grade;
+  }
+}
+ +

Podemos tornar o código mais legível definindo o operador super() como o primeiro item dentro do constructor(). Isso chamará o construtor da classe pai e herdará os membros que especificarmos como parâmetros de super(), desde que sejam definidos lá:

+ +
class Teacher extends Person {
+  constructor(first, last, age, gender, interests, subject, grade) {
+    super(first, last, age, gender, interests);
+
+    // subject and grade are specific to Teacher
+    this.subject = subject;
+    this.grade = grade;
+  }
+}
+
+ +

Quando instanciamos instâncias de objeto Teacher , podemos agora chamar métodos e propriedades definidos em TeacherPerson, como seria de esperar:

+ +
let snape = new Teacher('Severus', 'Snape', 58, 'male', ['Potions'], 'Dark arts', 5);
+snape.greeting(); // Hi! I'm Severus.
+snape.farewell(); // Severus has left the building. Bye for now.
+snape.age // 58
+snape.subject; // Dark arts
+
+ +

Como fizemos com Teachers, poderíamos criar outras subclasses de Person para torná-las mais especializadas sem modificar a classe base.

+ +
+

Note: You can find this example on GitHub as es2015-class-inheritance.html (see it live also).

+
+ +

Getters e Setters

+ +

Pode haver momentos em que queremos alterar os valores de um atributo nas classes que criamos ou não sabemos qual será o valor final de um atributo. Usando o exemplo Teacher, podemos não saber o assunto que o professor ensinará antes de criá-lo, ou o assunto pode mudar entre os termos.

+ +

Podemos lidar com essas situações com getters e setters.

+ +

Vamos melhorar a classe Professor com getters e setters. A aula começa da mesma forma que foi a última vez que olhamos para ela.

+ +

Os getters e setters trabalham em pares. Um getter retorna o valor atual da variável e seu setter correspondente altera o valor da variável para o que ela define.

+ +

A classe Teacher modificada é assim:

+ +
class Teacher extends Person {
+  constructor(first, last, age, gender, interests, subject, grade) {
+    super(first, last, age, gender, interests);
+    // subject and grade are specific to Teacher
+    this._subject = subject;
+    this.grade = grade;
+  }
+
+  get subject() {
+    return this._subject;
+  }
+
+  set subject(newSubject) {
+    this._subject = newSubject;
+  }
+}
+
+ +

Em nossa classe acima, temos um getter e setter para a propriedade subject. Usamos  _  para criar um valor separado no qual armazenar nossa propriedade de nome. Sem usar essa convenção, obteríamos erros toda vez que chamássemos get ou set. Neste ponto:

+ + + +

O exemplo abaixo mostra os dois recursos em ação:

+ +
// Check the default value
+console.log(snape.subject) // Returns "Dark arts"
+
+// Change the value
+snape.subject="Balloon animals" // Sets _subject to "Balloon animals"
+
+// Check it again and see if it matches the new value
+console.log(snape.subject) // Returns "Balloon animals"
+
+ +
+

Note: You can find this example on GitHub as es2015-getters-setters.html (see it live also).

+
+ +

Quando você usaria a herança em JavaScript?

+ +

Particularmente após este último artigo, você pode estar pensando "woo, isso é complicado". Bem, você está certo. Protótipos e herança representam alguns dos aspectos mais complexos do JavaScript, mas muito do poder e flexibilidade do JavaScript vem de sua estrutura e herança de objetos, e vale a pena entender como ele funciona.

+ +

De certa forma, você usa herança o tempo todo. Sempre que você usa vários recursos de uma API da Web ou métodos / propriedades definidos em um objeto de navegador interno que você chama em suas cadeias de caracteres, matrizes, etc., você está implicitamente usando herança.

+ +

Em termos de usar a herança em seu próprio código, você provavelmente não a usará com frequência, principalmente no começo e em pequenos projetos. É uma perda de tempo usar objetos e herança apenas por causa dela quando você não precisa deles. Mas à medida que suas bases de código aumentam, é mais provável que você encontre uma necessidade para isso. Se você estiver começando a criar vários objetos com recursos semelhantes, criar um tipo de objeto genérico para conter toda a funcionalidade compartilhada e herdar esses recursos em tipos de objetos mais especializados pode ser conveniente e útil.

+ +
+

Nota: Por causa da maneira como o JavaScript funciona, com a cadeia de protótipos, etc., o compartilhamento de funcionalidade entre objetos é frequentemente chamado de delegação. Os objetos especializados delegam a funcionalidade a um tipo de objeto genérico.

+
+ +

Ao usar a herança, você é aconselhado a não ter muitos níveis de herança, e manter um controle cuidadoso de onde você define seus métodos e propriedades. É possível começar a escrever código que modifica temporariamente os protótipos dos objetos do navegador interno, mas você não deve fazer isso a menos que tenha um bom motivo. Demasiada herança pode levar a confusão sem fim, e dor infinita quando você tenta depurar esse código.

+ +

Em última análise, os objetos são apenas outra forma de reutilização de código, como funções ou loops, com seus próprios papéis e vantagens específicos. Se você estiver criando um monte de variáveis e funções relacionadas e quiser rastreá-las todas juntas e empacotá-las perfeitamente, um objeto é uma boa ideia. Objetos também são muito úteis quando você quer passar uma coleção de dados de um lugar para outro. Ambas as coisas podem ser alcançadas sem o uso de construtores ou herança. Se você precisa apenas de uma única instância de um objeto, provavelmente é melhor usar apenas um literal de objeto e certamente não precisa de herança.

+ +

Alternativas para estender a cadeia de protótipos

+ +

Em JavaScript, existem várias maneiras diferentes de estender o protótipo de um objeto além do que mostramos acima. Para saber mais sobre as outras formas, visite nosso artigo Herança e a cadeia de protótipos.

+ +

Sumário

+ +

Este artigo cobriu o restante da teoria e sintaxe central do OOJS que achamos que você deveria saber agora. Neste ponto, você deve entender os princípios de objeto e OOP JavaScript, protótipos e herança prototypal, como criar classes (construtores) e instâncias de objetos, adicionar recursos a classes e criar subclasses que herdam de outras classes.

+ +

No próximo artigo, veremos como trabalhar com JavaScript Object Notation (JSON), um formato comum de troca de dados escrito usando objetos JavaScript.

+ +

Veja também

+ + + +

{{PreviousMenuNext("Learn/JavaScript/Objects/Object_prototypes", "Learn/JavaScript/Objects/JSON", "Learn/JavaScript/Objects")}}

+ +

In this module

+ + diff --git a/files/pt-br/learn/javascript/objects/inheritance/index.html b/files/pt-br/learn/javascript/objects/inheritance/index.html deleted file mode 100644 index 032e574d6e..0000000000 --- a/files/pt-br/learn/javascript/objects/inheritance/index.html +++ /dev/null @@ -1,403 +0,0 @@ ---- -title: Herança em JavaScript -slug: Learn/JavaScript/Objects/Inheritance -translation_of: Learn/JavaScript/Objects/Inheritance -original_slug: Aprender/JavaScript/Objetos/Herança ---- -
{{LearnSidebar}}
- -
{{PreviousMenuNext("Learn/JavaScript/Objects/Object_prototypes", "Learn/JavaScript/Objects/JSON", "Learn/JavaScript/Objects")}}
- -

Com a maior parte dos detalhes principais do OOJS agora explicados, este artigo mostra como criar classes de objetos "child" (construtores) que herdam recursos de suas classes "parent". Além disso, apresentamos alguns conselhos sobre quando e onde você pode usar o OOJS e veja como as classes são tratadas na sintaxe moderna do ECMAScript.

- - - - - - - - - - - - -
Pré-requisitos:Conhecimento básico de computação, conhecimento básico de HTML e CSS, familiaridade com com o básico de Javascript (veja Primeiros passos e Construindo blocos) e OOJS básico (veja Introdução a objetos).
Objetivo:Entender como é possível implementar a herança em Javascript.
- -

Herança Prototipada

- -

Até agora vimos alguma herança em ação — vimos como funcionam as cadeias de protótipos e como os membros são herdados subindo em uma cadeia. Mas principalmente isso envolveu funções internas do navegador. Como criamos um objeto em JavaScript que herda de outro objeto?

- -

Vamos explorar como fazer isso com um exemplo concreto.

- -

Começando

- -

Primeiro de tudo, faça uma cópia local do arquivo oojs-class-inheritance-start.html (veja também ao vivo). Aqui dentro você encontrará o mesmo exemplo de construtor  Person() que utilizamos durante todo o módulo, com uma pequena diferença — definimos apenas as propriedades dentro do construtor:

- -
function Person(first, last, age, gender, interests) {
-  this.name = {
-    first,
-    last
-  };
-  this.age = age;
-  this.gender = gender;
-  this.interests = interests;
-};
- -

Os métodos são todos definidos no protótipo do construtor. Por exemplo:

- -
Person.prototype.greeting = function() {
-  alert('Hi! I\'m ' + this.name.first + '.');
-};
- -
-

Nota: No código fonte, você também verá os métodos bio() e farewell() definidos. Depois você verá como eles podem ser herdados por outros construtores.

-
- -

Digamos que quiséssemos criar uma classe Teacher, como a que descrevemos em nossa definição inicial orientada a objetos, que herda todos os membros de Person, mas também inclui:

- -
    -
  1. Uma nova propriedade, subject — isso irá conter o assunto que o professor ensina.
  2. -
  3. Um método greeting() atualizado, que soa um pouco mais formal do que o método padrão  greeting() — mais adequado para um professor que se dirige a alguns alunos da escola.
  4. -
- -

Definindo uma função construtora Teacher()

- -

A primeira coisa que precisamos fazer é criar um construtor Teacher() — adicione o seguinte abaixo do código existente:

- -
function Teacher(first, last, age, gender, interests, subject) {
-  Person.call(this, first, last, age, gender, interests);
-
-  this.subject = subject;
-}
- -

Isto parece similar ao construtor Person de várias maneiras, mas há algo estranho aqui que nós não vimos antes — a função call(). Esta função basicamente permite chamar uma função definida em outro lugar, mas no contexto atual. O primeiro parâmetro especifica o valor this que você deseja usar ao executar a função, e os outros parâmetros são aqueles que devem ser passados para a função quando ela é invocada.

- -

Nós queremos que o construtor Teacher() pegue os mesmos parâmetros que o construtor Person() de onde ele está herdando, então especificamos todos eles como parâmetros na chamada call().

- -

A última linha dentro do construtor simplesmente define a nova propriedade subject que os professores terão, que pessoas genéricas não possuem.

- -

Como nota, poderíamos simplesmente ter feito isso:

- -
function Teacher(first, last, age, gender, interests, subject) {
-  this.name = {
-    first,
-    last
-  };
-  this.age = age;
-  this.gender = gender;
-  this.interests = interests;
-  this.subject = subject;
-}
- -

Mas isso é apenas redefinir as propriedades de novo, não herdá-las de  Person(), de modo que ela derrota o ponto que estamos tentando fazer. Também leva mais linhas de código.

- -

Herdando de um construtor sem parâmetros

- -

Observe que, se o construtor do qual você está herdando não tomar seus valores de propriedade de parâmetros, não será necessário especificá-los como argumentos adicionais em call(). Então, por exemplo, se você tivesse algo realmente simples assim:

- -
function Brick() {
-  this.width = 10;
-  this.height = 20;
-}
- -

Você pode herdar as propriedades widthheight fazendo isso (assim como as outras etapas descritas abaixo, é claro):

- -
function BlueGlassBrick() {
-  Brick.call(this);
-
-  this.opacity = 0.5;
-  this.color = 'blue';
-}
- -

Observe que apenas especificamos this dentro de call() — nenhum outro parâmetro é necessário, já que não estamos herdando propriedades do pai que são configuradas por meio de parâmetros.

- -

Definindo o protótipo e referência de construtor do Teacher()

- -

Tudo está bem até agora, mas nós temos um problema. Nós definimos um novo construtor, e ele tem uma propriedade  prototype, que por padrão apenas contém uma referência à própria função construtora. Ele não contém os métodos da propriedade prototype do construtor Person. Para ver isso, insira Object.getOwnPropertyNames(Teacher.prototype) no campo de entrada de texto ou no seu console JavaScript. Em seguida, insira-o novamente, substituindo Teacher por Person. O novo construtor também não herda esses métodos. Para ver isso, compare as saídas de Person.prototype.greetingTeacher.prototype.greeting. Precisamos obter Teacher() para herdar os métodos definidos no protótipo Person(). Então, como fazemos isso?

- -
    -
  1. Adicione a seguinte linha abaixo da sua adição anterior: -
    Teacher.prototype = Object.create(Person.prototype);
    - Aqui nosso amigo create() vem para o resgate novamente. Nesse caso, estamos usando para criar um novo objeto e torná-lo o valor de Teacher.prototype. O novo objeto tem Person.prototype como seu protótipo e, portanto, herdará, se e quando necessário, todos os métodos disponíveis no Person.prototype.
  2. -
  3. Precisamos fazer mais uma coisa antes de prosseguirmos. Depois de adicionar a última linha, a propriedade constructor de Teacher.prototype agora é igual a Person(), porque apenas definimos Teacher.prototype para fazer referência a um objeto que herda suas propriedades de Person.prototype! Tente salvar seu código, carregar a página em um navegador e inserir Teacher.prototype.constructor no console para verificar.
  4. -
  5. Isso pode se tornar um problema, então precisamos definir isso corretamente. Você pode fazer isso voltando ao seu código-fonte e adicionando a seguinte linha na parte inferior: -
    Object.defineProperty(Teacher.prototype, 'constructor', {
    -    value: Teacher,
    -    enumerable: false, // so that it does not appear in 'for in' loop
    -    writable: true });
    -
  6. -
  7. Agora, se você salvar e atualizar, entrar em Teacher.prototype.constructor deve retornar Teacher(), conforme desejado, além de estarmos herdando de Person()!
  8. -
- -

Dar a Teacher() uma nova função greeting() 

- -

Para finalizar nosso código, precisamos definir uma nova função greeting() no construtor Teacher().

- -

A maneira mais fácil de fazer isso é defini-lo no protótipo do Teacher() — adicione o seguinte na parte inferior do seu código:

- -
Teacher.prototype.greeting = function() {
-  var prefix;
-
-  if (this.gender === 'male' || this.gender === 'Male' || this.gender === 'm' || this.gender === 'M') {
-    prefix = 'Mr.';
-  } else if (this.gender === 'female' || this.gender === 'Female' || this.gender === 'f' || this.gender === 'F') {
-    prefix = 'Mrs.';
-  } else {
-    prefix = 'Mx.';
-  }
-
-  alert('Hello. My name is ' + prefix + ' ' + this.name.last + ', and I teach ' + this.subject + '.');
-};
- -

Isso alerta a saudação do professor, que também usa um prefixo de nome apropriado para seu gênero, elaborado usando uma instrução condicional.

- -

Testando o exemplo

- -

Agora que você digitou todo o código, tente criar uma instância de objeto do Teacher() colocando o seguinte na parte inferior do seu JavaScript (ou algo semelhante à sua escolha):

- -
var teacher1 = new Teacher('Dave', 'Griffiths', 31, 'male', ['football', 'cookery'], 'mathematics');
- -

Agora salve e atualize e tente acessar as propriedades e os métodos do novo objeto teacher1, por exemplo:

- -
teacher1.name.first;
-teacher1.interests[0];
-teacher1.bio();
-teacher1.subject;
-teacher1.greeting();
-teacher1.farewell();
- -

Tudo isso deve funcionar bem. As consultas nas linhas 1, 2, 3 e 6 acessam membros herdados do construtor genérico Person() (class). A consulta na linha 4 acessa um membro que está disponível somente no construtor mais especializado  Teacher() (class). A consulta na linha 5 teria acessado um membro herdado de Person(), exceto pelo fato de que Teacher() tem seu próprio membro com o mesmo nome, portanto, a consulta acessa esse membro.

- -
-

Note: If you have trouble getting this to work, compare your code to our finished version (see it running live also).

-
- -

A técnica que abordamos aqui não é a única maneira de criar classes herdadas em JavaScript, mas funciona bem e dá uma boa idéia sobre como implementar a herança em JavaScript.

- -

Você também pode estar interessado em conferir alguns dos novos recursos {{glossary("ECMAScript")}} que nos permitem fazer herança mais claramente em JavaScript (veja Classes). Nós não cobrimos esses aqui, pois eles ainda não são suportados amplamente pelos navegadores. Todas as outras construções de código que discutimos neste conjunto de artigos são suportadas desde o IE9 ou anterior, e existem maneiras de obter suporte anterior a isso.

- -

Uma maneira comum é usar uma biblioteca JavaScript — a maioria das opções populares tem um conjunto fácil de funcionalidade disponível para fazer herança com mais facilidade e rapidez. CoffeeScript por exemplo, fornece class, extends, etc.

- -

Um exercício adicional

- -

Em nossa seção de teoria OOP, incluímos também uma classe Student como um conceito, que herda todos os recursos de Person, e também tem um método  greeting() diferente de Person que é muito mais informal do que a saudação do Teacher. Dê uma olhada na aparência da saudação do aluno nessa seção e tente implementar seu próprio construtor Student() que herda todos os recursos de Person(), e implemente a função greeting() diferente.

- -
-

Note: If you have trouble getting this to work, have a look at our finished version (see it running live also).

-
- -

Sumário de membro do objeto

- -

Resumindo, você basicamente tem três tipos de propriedade / método para se preocupar:

- -
    -
  1. Aqueles definidos dentro de uma função construtora que são dadas a instâncias de objetos. Estes são bastante fáceis de detectar — em seu próprio código personalizado, eles são os membros definidos dentro de um construtor usando as linhas this.x = x ; no código do navegador, eles são os membros disponíveis apenas para instâncias de objetos (geralmente criados chamando um construtor usando a palavra-chave new, por exemplo, var myInstance = new myConstructor()).
  2. -
  3. Aqueles definidos diretamente no próprio construtor, que estão disponíveis apenas no construtor. Geralmente, eles estão disponíveis apenas em objetos de navegador internos e são reconhecidos por serem encadeados diretamente em um construtor, não em uma instância. Por exemplo, Object.keys().
  4. -
  5. Aqueles definidos no protótipo de um construtor, que são herdados por todas as instâncias e herdam as classes de objetos. Estes incluem qualquer membro definido na propriedade de protótipo de um Construtor, por ex. myConstructor.prototype.x().
  6. -
- -

Se você não tem certeza de qual é qual, não se preocupe com isso ainda — você ainda está aprendendo e a familiaridade virá com a prática.

- -

Classes ECMAScript 2015

- -

O ECMAScript 2015 introduz a sintaxe de classe em JavaScript como uma maneira de escrever classes reutilizáveis usando uma sintaxe mais fácil e mais limpa, que é mais semelhante a classes em C ++ ou Java. Nesta seção, converteremos os exemplos Pessoa e Professor da herança protótipo para as classes, para mostrar como é feito.

- -
-

Nota: Essa forma moderna de escrever classes é suportada em todos os navegadores modernos, mas ainda vale a pena saber como a herança prototípica subjacente, caso você trabalhe em um projeto que exija suporte a um navegador que não suporte essa sintaxe (mais notavelmente o Internet Explorer) .

-
- -

Vejamos uma versão reescrita do exemplo Person, estilo de classe:

- -
class Person {
-  constructor(first, last, age, gender, interests) {
-    this.name = {
-      first,
-      last
-    };
-    this.age = age;
-    this.gender = gender;
-    this.interests = interests;
-  }
-
-  greeting() {
-    console.log(`Hi! I'm ${this.name.first}`);
-  };
-
-  farewell() {
-    console.log(`${this.name.first} has left the building. Bye for now!`);
-  };
-}
-
- -

A declaração class indica que estamos criando uma nova classe. Dentro deste bloco, definimos todos os recursos da classe:

- - - -

Agora podemos instanciar instâncias de objeto usando o operador new, da mesma maneira que fizemos antes:

- -
let han = new Person('Han', 'Solo', 25, 'male', ['Smuggling']);
-han.greeting();
-// Hi! I'm Han
-
-let leia = new Person('Leia', 'Organa', 19, 'female', ['Government']);
-leia.farewell();
-// Leia has left the building. Bye for now
-
- -
-

Nota: Sob o capô, suas classes estão sendo convertidas em modelos de herança protótipos — isso é apenas açúcar sintático. Mas tenho certeza que você concordará que é mais fácil escrever.

-
- -

Herança com sintaxe de classe

- -

Acima nós criamos uma classe para representar uma pessoa. Eles têm uma série de atributos que são comuns a todas as pessoas; Nesta seção, criaremos nossa classe especializada Teacher, tornando-a herdada de Person usando a sintaxe de classe moderna. Isso é chamado de criação de uma subclasse ou subclasse.

- -

Para criar uma subclasse, usamos a palavra-chave extends para informar ao JavaScript a classe na qual queremos basear nossa classe.

- -
class Teacher extends Person {
-  constructor(first, last, age, gender, interests, subject, grade) {
-    this.name = {
-      first,
-      last
-    };
-
-  this.age = age;
-  this.gender = gender;
-  this.interests = interests;
-  // subject and grade are specific to Teacher
-  this.subject = subject;
-  this.grade = grade;
-  }
-}
- -

Podemos tornar o código mais legível definindo o operador super() como o primeiro item dentro do constructor(). Isso chamará o construtor da classe pai e herdará os membros que especificarmos como parâmetros de super(), desde que sejam definidos lá:

- -
class Teacher extends Person {
-  constructor(first, last, age, gender, interests, subject, grade) {
-    super(first, last, age, gender, interests);
-
-    // subject and grade are specific to Teacher
-    this.subject = subject;
-    this.grade = grade;
-  }
-}
-
- -

Quando instanciamos instâncias de objeto Teacher , podemos agora chamar métodos e propriedades definidos em TeacherPerson, como seria de esperar:

- -
let snape = new Teacher('Severus', 'Snape', 58, 'male', ['Potions'], 'Dark arts', 5);
-snape.greeting(); // Hi! I'm Severus.
-snape.farewell(); // Severus has left the building. Bye for now.
-snape.age // 58
-snape.subject; // Dark arts
-
- -

Como fizemos com Teachers, poderíamos criar outras subclasses de Person para torná-las mais especializadas sem modificar a classe base.

- -
-

Note: You can find this example on GitHub as es2015-class-inheritance.html (see it live also).

-
- -

Getters e Setters

- -

Pode haver momentos em que queremos alterar os valores de um atributo nas classes que criamos ou não sabemos qual será o valor final de um atributo. Usando o exemplo Teacher, podemos não saber o assunto que o professor ensinará antes de criá-lo, ou o assunto pode mudar entre os termos.

- -

Podemos lidar com essas situações com getters e setters.

- -

Vamos melhorar a classe Professor com getters e setters. A aula começa da mesma forma que foi a última vez que olhamos para ela.

- -

Os getters e setters trabalham em pares. Um getter retorna o valor atual da variável e seu setter correspondente altera o valor da variável para o que ela define.

- -

A classe Teacher modificada é assim:

- -
class Teacher extends Person {
-  constructor(first, last, age, gender, interests, subject, grade) {
-    super(first, last, age, gender, interests);
-    // subject and grade are specific to Teacher
-    this._subject = subject;
-    this.grade = grade;
-  }
-
-  get subject() {
-    return this._subject;
-  }
-
-  set subject(newSubject) {
-    this._subject = newSubject;
-  }
-}
-
- -

Em nossa classe acima, temos um getter e setter para a propriedade subject. Usamos  _  para criar um valor separado no qual armazenar nossa propriedade de nome. Sem usar essa convenção, obteríamos erros toda vez que chamássemos get ou set. Neste ponto:

- - - -

O exemplo abaixo mostra os dois recursos em ação:

- -
// Check the default value
-console.log(snape.subject) // Returns "Dark arts"
-
-// Change the value
-snape.subject="Balloon animals" // Sets _subject to "Balloon animals"
-
-// Check it again and see if it matches the new value
-console.log(snape.subject) // Returns "Balloon animals"
-
- -
-

Note: You can find this example on GitHub as es2015-getters-setters.html (see it live also).

-
- -

Quando você usaria a herança em JavaScript?

- -

Particularmente após este último artigo, você pode estar pensando "woo, isso é complicado". Bem, você está certo. Protótipos e herança representam alguns dos aspectos mais complexos do JavaScript, mas muito do poder e flexibilidade do JavaScript vem de sua estrutura e herança de objetos, e vale a pena entender como ele funciona.

- -

De certa forma, você usa herança o tempo todo. Sempre que você usa vários recursos de uma API da Web ou métodos / propriedades definidos em um objeto de navegador interno que você chama em suas cadeias de caracteres, matrizes, etc., você está implicitamente usando herança.

- -

Em termos de usar a herança em seu próprio código, você provavelmente não a usará com frequência, principalmente no começo e em pequenos projetos. É uma perda de tempo usar objetos e herança apenas por causa dela quando você não precisa deles. Mas à medida que suas bases de código aumentam, é mais provável que você encontre uma necessidade para isso. Se você estiver começando a criar vários objetos com recursos semelhantes, criar um tipo de objeto genérico para conter toda a funcionalidade compartilhada e herdar esses recursos em tipos de objetos mais especializados pode ser conveniente e útil.

- -
-

Nota: Por causa da maneira como o JavaScript funciona, com a cadeia de protótipos, etc., o compartilhamento de funcionalidade entre objetos é frequentemente chamado de delegação. Os objetos especializados delegam a funcionalidade a um tipo de objeto genérico.

-
- -

Ao usar a herança, você é aconselhado a não ter muitos níveis de herança, e manter um controle cuidadoso de onde você define seus métodos e propriedades. É possível começar a escrever código que modifica temporariamente os protótipos dos objetos do navegador interno, mas você não deve fazer isso a menos que tenha um bom motivo. Demasiada herança pode levar a confusão sem fim, e dor infinita quando você tenta depurar esse código.

- -

Em última análise, os objetos são apenas outra forma de reutilização de código, como funções ou loops, com seus próprios papéis e vantagens específicos. Se você estiver criando um monte de variáveis e funções relacionadas e quiser rastreá-las todas juntas e empacotá-las perfeitamente, um objeto é uma boa ideia. Objetos também são muito úteis quando você quer passar uma coleção de dados de um lugar para outro. Ambas as coisas podem ser alcançadas sem o uso de construtores ou herança. Se você precisa apenas de uma única instância de um objeto, provavelmente é melhor usar apenas um literal de objeto e certamente não precisa de herança.

- -

Alternativas para estender a cadeia de protótipos

- -

Em JavaScript, existem várias maneiras diferentes de estender o protótipo de um objeto além do que mostramos acima. Para saber mais sobre as outras formas, visite nosso artigo Herança e a cadeia de protótipos.

- -

Sumário

- -

Este artigo cobriu o restante da teoria e sintaxe central do OOJS que achamos que você deveria saber agora. Neste ponto, você deve entender os princípios de objeto e OOP JavaScript, protótipos e herança prototypal, como criar classes (construtores) e instâncias de objetos, adicionar recursos a classes e criar subclasses que herdam de outras classes.

- -

No próximo artigo, veremos como trabalhar com JavaScript Object Notation (JSON), um formato comum de troca de dados escrito usando objetos JavaScript.

- -

Veja também

- - - -

{{PreviousMenuNext("Learn/JavaScript/Objects/Object_prototypes", "Learn/JavaScript/Objects/JSON", "Learn/JavaScript/Objects")}}

- -

In this module

- - diff --git a/files/pt-br/learn/javascript/objects/object-oriented_js/index.html b/files/pt-br/learn/javascript/objects/object-oriented_js/index.html deleted file mode 100644 index ead7aaa74e..0000000000 --- a/files/pt-br/learn/javascript/objects/object-oriented_js/index.html +++ /dev/null @@ -1,276 +0,0 @@ ---- -title: JavaScript orientado a objetos para iniciantes -slug: Learn/JavaScript/Objects/Object-oriented_JS -translation_of: Learn/JavaScript/Objects/Object-oriented_JS -original_slug: Aprender/JavaScript/Objetos/Object-oriented_JS ---- -
{{LearnSidebar}}
- -
{{PreviousMenuNext("Learn/JavaScript/Objects/Basics", "Learn/JavaScript/Objects/Object_prototypes", "Learn/JavaScript/Objects")}}
- -

Com o básico fora do caminho, agora vamos nos concentrar no JavaScript orientado a objetos (OOJS) — Este artigo apresenta uma visão básica da teoria de programação orientada a objeto (OOP), em seguida, explora como o JavaScript emula as classes de objetos através de funções de construtor e como criar instâncias de objeto.

- - - - - - - - - - - - -
Pré-requisitos:Alfabetização básica em informática, um entendimento básico de HTML e CSS, familiaridade com o básico do JavaScript (consulte Primeiros passos e Blocos de construção) e noções básicas do OOJS (consulte Introdução aos objetos).
Objetivo:Para entender a teoria básica por trás da programação orientada a objetos, como isso se relaciona com JavaScript ("tudo é um objeto") e como criar construtores e instâncias de objetos.
- -

Programação orientada a objetos - o básico

- -

Para começar, vamos dar uma visão simplista e de alto nível do que é programação orientada a objeto (OOP). Dizemos simplista, porque a OOP pode rapidamente se tornar muito complicada, e dar a ela um tratamento completo agora provavelmente confundiria mais do que ajuda. A idéia básica da OOP é que usamos objetos para modelar coisas do mundo real que queremos representar dentro de nossos programas, e / ou fornecer uma maneira simples de acessar funcionalidades que de outra forma seriam difíceis ou impossíveis de usar.

- -

Os objetos podem conter dados e códigos relacionados, que representam informações sobre o que você está tentando modelar e a funcionalidade ou o comportamento que você deseja ter. Dados de objeto (e muitas vezes, funções também) podem ser armazenados ordenadamente (a palavra oficial é encapsulados) dentro de um pacote de objetos (que pode ser dado um nome específico para se referir, que é às vezes chamado de namespace), tornando fácil de estruturar e acessar; objetos também são comumente usados como armazenamentos de dados que podem ser facilmente enviados pela rede.

- -

Definindo um modelo de objeto

- -

Vamos considerar um programa simples que exibe informações sobre os alunos e professores de uma escola. Aqui vamos olhar para a teoria OOP em geral, não no contexto de qualquer linguagem de programação específica.

- -

Para começar, poderíamos retornar ao nosso tipo de objeto Person do nosso primeiro artigo de objetos, que define os dados genéricos e a funcionalidade de uma pessoa. Há muitas coisas que você poderia saber sobre uma pessoa (endereço, altura, tamanho do sapato, perfil de DNA, número de passaporte, traços de personalidade significativos ...), mas neste caso estamos interessados apenas em mostrar seu nome, idade, sexo e interesses, e também queremos ser capazes de escrever uma breve introdução sobre eles com base nesses dados e fazê-los dizer oi. Isso é conhecido como abstração — criando um modelo simples de uma coisa mais complexa, que representa seus aspectos mais importantes de uma forma que é fácil trabalhar com os objetivos do nosso programa.

- -

- -

Criando objetos reais

- -

De nossa classe, podemos criar instâncias de objeto — objetos que contêm os dados e a funcionalidade definidos na classe. Da nossa classe Person, podemos criar algumas pessoas reais:

- -

- -

Quando uma instância de objeto é criada a partir de uma classe, a função construtora da classe é executada para criá-la. Esse processo de criação de uma instância de objeto de uma classe é chamado de instanciação — a instância do objeto é instanciada a partir da classe.

- -

Classes especialistas

- -

Neste caso, não queremos pessoas genéricas — queremos professores e alunos, que são tipos mais específicos de pessoas. Em OOP, podemos criar novas classes com base em outras classes — essas novas classes filhas podem herdar os recursos de dados e código de sua classe pai, para que você possa reutilizar a funcionalidade comum a todos os tipos de objetos em vez de duplicá-los. Onde a funcionalidade difere entre as classes, você pode definir recursos especializados diretamente sobre eles, conforme necessário.

- -

- -

Isso é realmente útil — professores e alunos compartilham muitos recursos comuns, como nome, sexo e idade, por isso é conveniente definir apenas esses recursos uma vez. Você também pode definir o mesmo recurso separadamente em classes diferentes, já que cada definição desse recurso estará em um namespace diferente. Por exemplo, a saudação de um aluno pode estar no formato "Yo, I'm [firstName]" (por exemplo, Yo, I'm Sam), enquanto um professor pode usar algo mais formal, como "Olá, meu nome é [Prefixo [lastName], e eu ensino [Subject]. " (por exemplo Olá, Meu nome é Mr Griffiths, e eu ensino Química).

- -
-

Nota: A palavra chique para a capacidade de múltiplos tipos de objeto de implementar a mesma funcionalidade é o polimorfismo. Apenas no caso de você estar se perguntando.

-
- -

Agora você pode criar instâncias de objetos de suas classes filhas. Por exemplo:

- -

- -

No restante do artigo, começaremos a analisar como a teoria da POO pode ser colocada em prática no JavaScript.

- -

Construtores e instâncias de objeto

- -

O JavaScript usa funções especiais chamadas funções construtoras para definir objetos e seus recursos. Eles são úteis porque muitas vezes você encontrará situações em que não sabe quantos objetos estará criando; Os construtores fornecem os meios para criar quantos objetos forem necessários de forma eficaz, anexando dados e funções a eles, conforme necessário.

- -

Vamos explorar a criação de classes por meio de construtores e criar instâncias de objeto a partir deles em JavaScript. Primeiro de tudo, gostaríamos que você fizesse uma nova cópia local do arquivo oojs.html que vimos em nosso primeiro artigo Objetos.

- -

Um exemplo simples

- -
    -
  1. Vamos começar observando como você pode definir uma pessoa com uma função normal. Adicione esta função dentro do elemento script: - -
    function createNewPerson(name) {
    -  var obj = {};
    -  obj.name = name;
    -  obj.greeting = function() {
    -    alert('Hi! I\'m ' + obj.name + '.');
    -  };
    -  return obj;
    -}
    -
  2. -
  3. Agora você pode criar uma nova pessoa chamando essa função — tente as seguintes linhas no console JavaScript do seu navegador: -
    var salva = createNewPerson('Salva');
    -salva.name;
    -salva.greeting();
    - Isso funciona bem o suficiente, mas é um pouco prolixo; Se sabemos que queremos criar um objeto, por que precisamos criar explicitamente um novo objeto vazio e devolvê-lo? Felizmente, o JavaScript nos fornece um atalho útil, na forma de funções de construtor — vamos criar um agora!
  4. -
  5. Substitua sua função anterior pelo seguinte: -
    function Person(name) {
    -  this.name = name;
    -  this.greeting = function() {
    -    alert('Hi! I\'m ' + this.name + '.');
    -  };
    -}
    -
  6. -
- -

A função de construtor é a versão do JavaScript de uma classe. Você notará que ela tem todos os recursos que você espera em uma função, embora ela não retorne nada ou crie explicitamente um objeto — ela basicamente define propriedades e métodos. Você verá a palavra-chave this sendo usada aqui também — é basicamente dizer que sempre que uma dessas instâncias de objeto é criada, a propriedade name  do objeto será igual ao valor do nome passado à chamada do construtor, e o método greeting() usará o valor do nome passado para a chamada do construtor também.

- -
-

Nota: Um nome de função de construtor geralmente começa com uma letra maiúscula — essa convenção é usada para tornar as funções do construtor mais fáceis de reconhecer no código.

-
- -

Então, como podemos chamar um construtor para criar alguns objetos?

- -
    -
  1. Adicione as seguintes linhas abaixo da sua adição de código anterior: -
    var person1 = new Person('Bob');
    -var person2 = new Person('Sarah');
    -
  2. -
  3. Salve seu código e recarregue-o no navegador e tente inserir as seguintes linhas em seu console JS: -
    person1.name
    -person1.greeting()
    -person2.name
    -person2.greeting()
    -
  4. -
- -

Legal! Você verá agora que temos dois novos objetos na página, cada um deles armazenado em um namespace diferente — quando você acessa suas propriedades e métodos, é necessário iniciar chamadas com person1 ou person2; a funcionalidade contida é cuidadosamente empacotada para que não entre em conflito com outras funcionalidades. Eles, no entanto, têm a mesma propriedade de name e o método greeting() disponível. Observe que eles estão usando seu próprio valor de name que foi atribuído a eles quando foram criados; Esta é uma razão pela qual é muito importante usar this, então eles usarão seus próprios valores e não algum outro valor.

- -

Vamos ver novamente as chamadas do construtor:

- -
var person1 = new Person('Bob');
-var person2 = new Person('Sarah');
- -

Em cada caso, a palavra-chave new é usada para informar ao navegador que queremos criar uma nova instância de objeto, seguida pelo nome da função com seus parâmetros obrigatórios contidos entre parênteses, e o resultado é armazenado em uma variável — muito semelhante a como uma função padrão é chamada. Cada instância é criada de acordo com esta definição:

- -
function Person(name) {
-  this.name = name;
-  this.greeting = function() {
-    alert('Hi! I\'m ' + this.name + '.');
-  };
-}
- -

Após a criação dos novos objetos, as variáveis person1person2 contêm os seguintes objetos:

- -
{
-  name: 'Bob',
-  greeting: function() {
-    alert('Hi! I\'m ' + this.name + '.');
-  }
-}
-
-{
-  name: 'Sarah',
-  greeting: function() {
-    alert('Hi! I\'m ' + this.name + '.');
-  }
-}
- -

Note que quando estamos chamando nossa função de construtor, estamos definindo greeting() toda vez, o que não é ideal. Para evitar isso, podemos definir funções no protótipo, que veremos mais adiante.

- -

Criando nosso construtor acabado

- -

O exemplo que vimos acima foi apenas um exemplo simples para começarmos. Vamos agora começar e criar nossa função final do construtor Person().

- -
    -
  1. Remova o código que você inseriu até agora e inclua este construtor de substituição — isso é exatamente o mesmo que o exemplo simples em princípio, com um pouco mais de complexidade: -
    function Person(first, last, age, gender, interests) {
    -  this.name = {
    -    'first': first,
    -    'last' : last
    -  };
    -  this.age = age;
    -  this.gender = gender;
    -  this.interests = interests;
    -  this.bio = function() {
    -    alert(this.name.first + ' ' + this.name.last + ' is ' + this.age + ' years old. He likes ' + this.interests[0] + ' and ' + this.interests[1] + '.');
    -  };
    -  this.greeting = function() {
    -    alert('Hi! I\'m ' + this.name.first + '.');
    -  };
    -}
    -
  2. -
  3. Agora adicione a seguinte linha abaixo, para criar uma instância de objeto a partir dela: -
    var person1 = new Person('Bob', 'Smith', 32, 'male', ['music', 'skiing']);
    -
  4. -
- -

Agora você verá que pode acessar as propriedades e os métodos exatamente como fizemos anteriormente — Tente isso no seu console JS:

- -
person1['age']
-person1.interests[1]
-person1.bio()
-// etc.
- -
-

Nota: Se você está tendo problemas para fazer isso funcionar, tente comparar seu código com a nossa versão — veja o código em oojs-class-finished.html (também você pode ve-lo sendo executado aqui).

-
- -

Exercícios adicionais

- -

Para começar, tente adicionar mais algumas linhas de criação de objetos e tente obter e configurar os membros das instâncias de objetos resultantes.

- -

Além disso, há alguns problemas com nosso método  bio() — a saída sempre inclui o pronome "Ele", mesmo que sua pessoa seja do sexo feminino ou alguma outra classificação de gênero preferida. E a biografia incluirá apenas dois interesses, mesmo que mais sejam listados na matriz interests. Você pode descobrir como corrigir isso na definição de classe (construtor)? Você pode colocar qualquer código que você gosta dentro de um construtor (você provavelmente precisará de alguns condicionais e um loop). Pense em como as sentenças devem ser estruturadas de maneira diferente dependendo do gênero e dependendo se o número de interesses listados é 1, 2 ou mais de 2.

- -
-

Note: If you get stuck, we have provided an answer inside our GitHub repo (see it live) — try writing it yourself first though!

-
- -

Outras maneiras de criar instâncias de objeto

- -

Até agora, vimos duas maneiras diferentes de criar uma instância de objeto — declarar um literal de objeto, e usar uma função de construtor (veja acima).

- -

Isso faz sentido, mas existem outras maneiras — queremos familiarizá-lo com essas informações caso você as encontre em suas viagens pela Web.

- -

O construtor Object() 

- -

Primeiro de tudo, você pode usar o construtor Object() para criar um novo objeto. Sim, até objetos genéricos possuem um construtor, o que gera um objeto vazio.

- -
    -
  1. Tente inserir isso no console JavaScript do seu navegador: -
    var person1 = new Object();
    -
  2. -
  3. Isso armazena um objeto vazio na variável person1. Você pode adicionar propriedades e métodos a esse objeto usando a notação de pontos ou colchetes conforme desejado; tente estes exemplos no seu console: -
    person1.name = 'Chris';
    -person1['age'] = 38;
    -person1.greeting = function() {
    -  alert('Hi! I\'m ' + this.name + '.');
    -};
    -
  4. -
  5. Você também pode passar um literal de objeto para o construtor Object() como um parâmetro, para preenchê-lo com propriedades / métodos. Tente isso no seu console JS: -
    var person1 = new Object({
    -  name: 'Chris',
    -  age: 38,
    -  greeting: function() {
    -    alert('Hi! I\'m ' + this.name + '.');
    -  }
    -});
    -
  6. -
- -

Usando o método create()

- -

Os construtores podem ajudá-lo a fornecer seu pedido de código — você pode criar construtores em um único local e, em seguida, criar instâncias conforme necessário, e fica claro de onde eles vieram.

- -

No entanto, algumas pessoas preferem criar instâncias de objeto sem primeiro criar construtores, especialmente se estiverem criando apenas algumas instâncias de um objeto. JavaScript tem um método embutido chamado create() que permite que você faça isso. Com ele, você pode criar um novo objeto com base em qualquer objeto existente.

- -
    -
  1. Com o exercício concluído das seções anteriores carregadas no navegador, tente isso no seu console JavaScript: -
    var person2 = Object.create(person1);
    -
  2. -
  3. Agora tente estes: -
    person2.name
    -person2.greeting()
    -
  4. -
- -

Você verá que a person2 foi criada com base na  person1  —  ela tem as mesmas propriedades e métodos disponíveis para ela.

- -

Uma limitação do create()  é que o IE8 não o suporta. Então os construtores são mais efetivos se você quiser que funcione em navegadores antigos.

- -

Vamos explorar os efeitos de create() em mais detalhes posteriormente.

- -

Sumário

- -

Este artigo forneceu uma visão simplificada da teoria orientada a objetos — isso não é toda a história, mas dá uma idéia do que estamos lidando aqui. Além disso, começamos a analisar diferentes maneiras de gerar instâncias de objetos.

- -

No próximo artigo, vamos explorar os protótipos de objetos JavaScript.

- -

{{PreviousMenuNext("Learn/JavaScript/Objects/Basics", "Learn/JavaScript/Objects/Object_prototypes", "Learn/JavaScript/Objects")}}

- -

Neste módulo

- - diff --git a/files/ru/_redirects.txt b/files/ru/_redirects.txt index f32afa34e2..ad7a5e09bc 100644 --- a/files/ru/_redirects.txt +++ b/files/ru/_redirects.txt @@ -223,11 +223,13 @@ /ru/docs/Learn/How_the_Internet_works /ru/docs/Learn/Common_questions/How_does_the_Internet_work /ru/docs/Learn/JavaScript/Asynchronous/Таймауты_и_интервалы /ru/docs/Learn/JavaScript/Asynchronous/Timeouts_and_intervals /ru/docs/Learn/JavaScript/Building_blocks/События /ru/docs/Learn/JavaScript/Building_blocks/Events +/ru/docs/Learn/JavaScript/Objects/Inheritance /ru/docs/Learn/JavaScript/Objects/Classes_in_JavaScript +/ru/docs/Learn/JavaScript/Objects/Object-oriented_JS /ru/docs/conflicting/Learn/JavaScript/Objects/Classes_in_JavaScript /ru/docs/Learn/JavaScript/Объекты /ru/docs/Learn/JavaScript/Objects /ru/docs/Learn/JavaScript/Объекты/Adding_bouncing_balls_features /ru/docs/Learn/JavaScript/Objects/Adding_bouncing_balls_features -/ru/docs/Learn/JavaScript/Объекты/Inheritance /ru/docs/Learn/JavaScript/Objects/Inheritance +/ru/docs/Learn/JavaScript/Объекты/Inheritance /ru/docs/Learn/JavaScript/Objects/Classes_in_JavaScript /ru/docs/Learn/JavaScript/Объекты/JSON /ru/docs/Learn/JavaScript/Objects/JSON -/ru/docs/Learn/JavaScript/Объекты/Object-oriented_JS /ru/docs/Learn/JavaScript/Objects/Object-oriented_JS +/ru/docs/Learn/JavaScript/Объекты/Object-oriented_JS /ru/docs/conflicting/Learn/JavaScript/Objects/Classes_in_JavaScript /ru/docs/Learn/JavaScript/Объекты/Object_building_practice /ru/docs/Learn/JavaScript/Objects/Object_building_practice /ru/docs/Learn/JavaScript/Объекты/Object_prototypes /ru/docs/Learn/JavaScript/Objects/Object_prototypes /ru/docs/Learn/JavaScript/Объекты/Основы /ru/docs/Learn/JavaScript/Objects/Basics diff --git a/files/ru/_wikihistory.json b/files/ru/_wikihistory.json index 7a042b1535..605e67f660 100644 --- a/files/ru/_wikihistory.json +++ b/files/ru/_wikihistory.json @@ -3582,7 +3582,7 @@ "superpuper32" ] }, - "Learn/JavaScript/Objects/Inheritance": { + "Learn/JavaScript/Objects/Classes_in_JavaScript": { "modified": "2020-10-17T04:55:20.992Z", "contributors": [ "raylyanway", @@ -3606,27 +3606,6 @@ "slychai85" ] }, - "Learn/JavaScript/Objects/Object-oriented_JS": { - "modified": "2020-11-28T15:15:40.063Z", - "contributors": [ - "Tartalon", - "Viatcheslav-Malahov", - "wind-of", - "Detrimon", - "BadLame", - "ConstantineZz", - "ellegre", - "injashkin", - "NooNoo1337", - "Roman-Halenko", - "sergeomak", - "Elena_Petrenko", - "uandrew", - "slychai85", - "superpuper32", - "GennadyGlushenkov" - ] - }, "Learn/JavaScript/Objects/Object_building_practice": { "modified": "2020-07-16T22:32:32.476Z", "contributors": [ @@ -24039,6 +24018,27 @@ "iegik" ] }, + "conflicting/Learn/JavaScript/Objects/Classes_in_JavaScript": { + "modified": "2020-11-28T15:15:40.063Z", + "contributors": [ + "Tartalon", + "Viatcheslav-Malahov", + "wind-of", + "Detrimon", + "BadLame", + "ConstantineZz", + "ellegre", + "injashkin", + "NooNoo1337", + "Roman-Halenko", + "sergeomak", + "Elena_Petrenko", + "uandrew", + "slychai85", + "superpuper32", + "GennadyGlushenkov" + ] + }, "conflicting/MDN/Contribute": { "modified": "2020-10-30T09:21:05.942Z", "contributors": [ diff --git a/files/ru/conflicting/learn/javascript/objects/classes_in_javascript/index.html b/files/ru/conflicting/learn/javascript/objects/classes_in_javascript/index.html new file mode 100644 index 0000000000..3fad32994e --- /dev/null +++ b/files/ru/conflicting/learn/javascript/objects/classes_in_javascript/index.html @@ -0,0 +1,287 @@ +--- +title: Объектно-ориентированный JavaScript для начинающих +slug: conflicting/Learn/JavaScript/Objects/Classes_in_JavaScript +tags: + - Constructor + - Create + - JavaScript + - OOJS + - Object + - Новичку + - ООП + - экземпляр +translation_of: Learn/JavaScript/Objects/Object-oriented_JS +original_slug: Learn/JavaScript/Objects/Object-oriented_JS +--- +
{{LearnSidebar}}
+ +
{{PreviousMenuNext("Learn/JavaScript/Objects/Basics", "Learn/JavaScript/Objects/Object_prototypes", "Learn/JavaScript/Objects")}}
+ +

Разобравшись с основами, сосредоточимся на объектно-ориентированном JavaScript (OOJS) — данная статья даёт базовое представление о теории объектно-ориентированного программирования (ООП), далее рассмотрено как JavaScript эмулирует классы объектов с помощью функции-конструктора и как создаются экземпляры объектов.

+ + + + + + + + + + + + +
Необходимые знания: +

Базовая компьютерная грамотность, базовое понимание HTML и CSS, знакомство с основами JavaScript (см. Первые шаги и Cструктурные элементы JavaScript) и основы OOJS (см. Введение в объекты).

+
Цель:Понять основную теорию объектно-ориентированного программирования, как это относится к JavaScript («все является объектом») и как создавать конструкторы и экземпляры объектов.
+ +

Объектно-ориентированное программирование: основы

+ +

Начнём с упрощённого высокоуровневого представления о том, что такое объектно-ориентированное программирование (ООП). Мы говорим упрощённого, потому что ООП может быстро стать очень сложным, и если сейчас дать полный курс, вероятно, можно запутать больше, чем помочь. Основная идея ООП заключается в том, что мы используем объекты для отображения моделей из реального мира в наших программах и/или упрощения доступа к функциям, которые в противном случае было бы трудно или невозможно использовать.

+ +

Объекты могут содержать данные и код, представляющие информацию о том, что вы пытаетесь смоделировать, а также о том, какие у этих объектов должны быть функциональные возможности или поведение. Данные объекта (а часто так же и функции) могут быть точно сохранены (официальный термин "инкапсулированы") внутри пакета объекта, упрощая структуру и доступ к ним. Пакету объекта может быть присвоено определённое имя, на которое можно сослаться и которое иногда называют пространством имён. Объекты также широко используются в качестве хранилищ данных, которые могут быть легко отправлены по сети.

+ +

Определение шаблона объекта

+ +

Рассмотрим простую программу, которая отображает информацию об учениках и учителях в школе. Здесь мы рассмотрим теорию ООП в целом, а не в контексте какого-либо конкретного языка программирования.

+ +

Вернёмся к объекту Person из нашей статьи Основы объектов, который определяет общие сведения и функциональные возможности человека. Есть много вещей, которые вы можете узнать о человеке (его адрес, рост, размер обуви, профиль ДНК, номер паспорта, значимые черты личности ...), но в данном случае нас интересует только имя, возраст, пол и интересы, а также мы хотим иметь возможность написать краткую информацию о нём, основываясь на этих данных, и сделать так, чтобы он поздоровался. Это известно как абстракция — создание простой модели более сложной сущности, которая представляет её наиболее важные аспекты таким образом, чтобы с ней было удобно работать для выполнения целей нашей программы.

+ +

+ +

В некоторых языках ООП, это общее определение типа объекта называется class (JavaScript использует другой механизм и терминологию, как вы увидите ниже) — это на самом деле не объект, а шаблон, который определяет, какие характеристики должен иметь объект.

+ +

Создание реальных объектов

+ +

Из нашего класса мы можем создать экземпляры объектов — объекты, содержащие данные и функциональные возможности, определённые в классе. Из нашего класса Person мы теперь можем создавать модели реальных людей:

+ +

+ +

Когда экземпляр объекта создаётся из класса, для его создания выполняется функция-конструктор класса. Этот процесс создания экземпляра объекта из класса называется создание экземпляра (instantiation) — из класса создаётся экземпляр объекта.

+ +

Специализированные классы

+ +

В нашем случае нам не нужны все люди — нам требуются учителя и ученики, которые являются более конкретными типами людей. В ООП мы можем создавать новые классы на основе других классов — эти новые дочерние классы могут быть созданы для наследования данных и характеристик родительского класса, так чтобы можно было использовать функциональные возможности, общие для всех типов объекта, вместо того чтобы дублировать их. Когда функциональность различается между классами, можно по мере необходимости определять специализированные функции непосредственно на них.

+ +

+ +

Это действительно полезно — преподаватели и студенты имеют много общих характеристик, таких как имя, пол и возраст, и удобно определить их только один раз. Вы можете также задать одну и ту же характеристику отдельно в разных классах, поскольку каждое определение этой характеристики будет находиться в отдельном пространстве имён. Например, приветствие студента может быть в форме "Yo, I'm [firstName]" (например Yo, I'm Sam), в то время как учитель может использовать что-то более формальное, такое как "Hello, my name is [Prefix] [lastName], and I teach [Subject]." (например Hello, My name is Mr Griffiths, and I teach Chemistry).

+ +
+

Примечание: Если вам интересно, существует специальный термин Polymorphism (Полиморфизм) - это забавное слово, обозначающее реализацию той же функциональности для нескольких типов объекта. 

+
+ +

Теперь вы можете создавать экземпляры объекта из дочерних классов. Например:

+ +

+ +

Далее мы рассмотрим, как ООП теорию можно применить на практике в JavaScript.

+ +

Конструкторы и экземпляры объектов

+ +

JavaScript использует специальные функции, называемые функциями конструктора (constructor functions) для определения объектов и их свойств. Они полезны, потому что вы часто будете сталкиваться с ситуациями, в которых не известно, сколько объектов вы будете создавать; конструкторы позволяют создать столько объектов, сколько нужно эффективным способом, прикреплением данных и функций для объектов по мере необходимости.

+ +

Рассмотрим создание классов через конструкторы и создание экземпляров объектов из них в JavaScript. Прежде всего, мы хотели бы, чтобы вы создали новую локальную копию файла oojs.html, который мы видели в нашей первой статье «Объекты».

+ +

Простой пример

+ +
    +
  1. Давайте рассмотрим как можно определить человека с нормальной функцией. Добавьте эту функцию в элемент script: + +
    function createNewPerson(name) {
    +  const obj = {};
    +  obj.name = name;
    +  obj.greeting = function() {
    +    alert('Hi! I\'m ' + this.name + '.');
    +  };
    +  return obj;
    +}
    +
  2. +
  3. Теперь вы можете создать нового человека, вызвав эту функцию - попробуйте следующие строки в консоли JavaScript браузера: +
    const salva = createNewPerson('Salva');
    +salva.name;
    +salva.greeting();
    + Это работает достаточно хорошо, но код излишне многословен; если мы знаем, что хотим создать объект, зачем нам явно создавать новый пустой объект и возвращать его? К счастью, JavaScript предоставляет нам удобный способ в виде функций-конструкторов - давайте сделаем это сейчас!
  4. +
  5. Замените предыдущую функцию следующей: +
    function Person(name) {
    +  this.name = name;
    +  this.greeting = function() {
    +    alert('Hi! I\'m ' + this.name + '.');
    +  };
    +}
    +
  6. +
+ +

Функция-конструктор - это JavaScript версия класса. Вы заметите, что в нем есть все признаки, которые вы ожидаете от функции, хотя он ничего не возвращает и явно не создаёт объект - он в основном просто определяет свойства и методы. Вы также увидите, что ключевое слово this также используется здесь, - это в основном говорит о том, что всякий раз, когда создаётся один из этих экземпляров объектов, свойство имени объекта будет равно значению name, переданному вызову конструктора, и метод greeting() будет использовать значение имени, переданное также вызову конструктора.

+ +
+

Примечание: Имя функции конструктора обычно начинается с заглавной буквы - это соглашение используется для упрощения распознавания функций конструктора в коде.

+
+ +

Итак, как мы вызываем конструктор для создания некоторых объектов?

+ +
    +
  1. Добавьте следующие строки под предыдущим добавлением кода: +
    let person1 = new Person('Bob');
    +let person2 = new Person('Sarah');
    +
  2. +
  3. Сохраните код и перезагрузите его в браузере и попробуйте ввести следующие строки в консоль JS: +
    person1.name
    +person1.greeting()
    +person2.name
    +person2.greeting()
    +
  4. +
+ +

Круто! Теперь, как вы видите, у нас есть два новых объекта на странице, каждый из которых хранится в отдельном пространстве имён - при доступе к их свойствам и методам вы должны начинать вызовы с person1 или person2; функциональность, содержащаяся внутри, аккуратно упакована, поэтому она не будет конфликтовать с другими функциями. Тем не менее, у них есть одно и то же свойство name и greeting(). Обратите внимание, что они используют своё собственное значение name, которое было присвоено им, когда они были созданы; это одна из причин, почему очень важно использовать this, таким образом они будут использовать свои собственные значения, а не какие-либо другие.

+ +

Давайте снова посмотрим на вызовы конструктора:

+ +
let person1 = new Person('Bob');
+let person2 = new Person('Sarah');
+ +

В каждом случае ключевое слово new используется, чтобы сообщить браузеру, что мы хотим создать экземпляр нового объекта, за которым следует имя функции с её необходимыми параметрами, содержащимися в круглых скобках, и результат сохраняется в переменной - очень похоже на то, как вызывается стандартная функция. Каждый экземпляр создаётся в соответствии с этим определением:

+ +
function Person(name) {
+  this.name = name;
+  this.greeting = function() {
+    alert('Hi! I\'m ' + this.name + '.');
+  };
+}
+ +

После создания новых объектов переменные person1 и person2 содержат следующие объекты:

+ +
{
+  name: 'Bob',
+  greeting: function() {
+    alert('Hi! I\'m ' + this.name + '.');
+  }
+}
+
+{
+  name: 'Sarah',
+  greeting: function() {
+    alert('Hi! I\'m ' + this.name + '.');
+  }
+}
+ +

Обратите внимание, что когда мы вызываем нашу функцию-конструктор, мы определяем greeting() каждый раз, что не является идеальным. Чтобы этого избежать, вместо этого мы можем определить функции на прототипе, о которых мы поговорим позже.

+ +

Создавая наш готовый конструктор

+ +

Пример, рассмотренный выше, был лишь наглядным примером, чтобы вы поняли суть. Теперь, давайте создадим нашу конечную функцию-конструктор Person().

+ +
    +
  1. Замените весь предыдущий код новой функцией конструктора - это, в принципе, тот же самое что и в наглядном примере, но несколько сложнее: +
    function Person(first, last, age, gender, interests) {
    +  this.name = {
    +    first : first,
    +    last: last
    +  };
    +  this.age = age;
    +  this.gender = gender;
    +  this.interests = interests;
    +  this.bio = function() {
    +    alert(this.name.first + ' ' + this.name.last + ' is ' + this.age + ' years old. He likes ' + this.interests[0] + ' and ' + this.interests[1] + '.');
    +  };
    +  this.greeting = function() {
    +    alert('Hi! I\'m ' + this.name.first + '.');
    +  };
    +};
    +
  2. +
  3. Теперь добавьте следующую строку ниже, чтобы создать экземпляр объекта из него: +
    let person1 = new Person('Bob', 'Smith', 32, 'male', ['music', 'skiing']);
    +
  4. +
+ +

Как вы могли заметить, вы можете получить доступ к свойствам и методам, как это было ранее, - попробуйте использовать их в консоли JS:

+ +
person1['age']
+person1.interests[1]
+person1.bio()
+// etc.
+ +
Примечание: Если у вас возникли проблемы с работой кода, попробуйте сравнить его с нашей версией - см. oojs-class-finished.html (также смотрите, как он работает в прямом эфире).
+ +

Дальнейшие упражнения

+ +

Для начала, попробуйте добавить ещё пару собственных строк создания объекта и попробуйте получить и установить элементы полученных экземпляров объектов.

+ +

Кроме того, есть несколько проблем с нашим методом bio() - вывод всегда включает местоимение «He» ("Он" в пер. с англ.), даже если ваш человек является женщиной или какой-либо другой предпочтительной гендерной классификацией. И bio будет включать только два интереса, даже если в массиве interests указано больше. Можете ли вы решить, как исправить это в определении класса (конструкторе)? Вы можете поместить любой код, который вам нравится внутри конструктора (вам, вероятно, понадобятся несколько условий и цикл). Подумайте о том, как предложения должны быть структурированы по-разному в зависимости от пола и в зависимости от того, имеет ли число перечисленных интересов 1, 2 или более 2.

+ +
+

Примечание: Если у вас возникли трудности с решением задачи, мы предоставили ответ в нашем репозитории GitHub (см. это в действии) — но сначала попробуйте написать сами!

+
+ +

Другие способы создания экземпляров объектов

+ +

До сих пор мы видели два разных способа создания экземпляра объекта - объявление объектного литерала и использование функции конструктора (см. выше).

+ +

Это имеет смысл, но есть и другие способы - мы бы хотели ознакомить вас с ними на случай, если вы встретите их в своих путешествиях по Сети.

+ +

Конструктор Object ()

+ +

Прежде всего, вы можете использовать конструктор Object() для создания нового объекта. Да, даже общие объекты имеют конструктор, который генерирует пустой объект.

+ +
    +
  1. Попробуйте ввести это в консоль JavaScript вашего браузера: +
    let person1 = new Object();
    +
  2. +
  3. Это сохраняет ссылку на пустой объект в переменную person1. Затем вы можете добавить свойства и методы к этому объекту с использованием точечной или скобочной нотации по желанию; попробуйте эти примеры в консоли: +
    person1.name = 'Chris';
    +person1['age'] = 38;
    +person1.greeting = function() {
    +  alert('Hi! I\'m ' + this.name + '.');
    +};
    +
  4. +
  5. Вы также можете передать литерал объекта конструктору Object() в качестве параметра, чтобы заполнить его свойствами / методами. Попробуйте это в консоли JS: +
    let person1 = new Object({
    +  name: 'Chris',
    +  age: 38,
    +  greeting: function() {
    +    alert('Hi! I\'m ' + this.name + '.');
    +  }
    +});
    +
  6. +
+ +

Использование метода create()

+ +

Конструкторы могут помочь вам определить порядок кода - вы можете создать конструктор в одном месте, а затем создавать экземпляры по мере необходимости, и их происхождение будет понятным.

+ +

Однако некоторые люди предпочитают создавать экземпляры объектов без предварительного создания конструкторов, особенно если они создают только несколько экземпляров объекта. JavaScript имеет встроенный метод create(), который позволяет вам это делать. С его помощью вы можете создать новый объект на основе любого существующего объекта.

+ +
    +
  1. Закончив упражнение из предыдущего раздела, загруженное в браузер, попробуйте это в консоли JavaScript: +
    let person2 = Object.create(person1);
    +
  2. +
  3. Теперь попробуйте: +
    person2.name
    +person2.greeting()
    +
  4. +
+ +

Вы увидите, что person2 был создан на основе person1 - он имеет те же свойства и метод, доступные для него.

+ +

Одно ограничение метода create() заключается в том, что IE8 не поддерживает его. Поэтому конструкторы могут быть более эффективными, если вы хотите поддерживать старые браузеры.

+ +

Подробнее мы рассмотрим особенности метода create() немного позже.

+ +

Сводка

+ +

В этой статье представлен упрощённый взгляд на объектно-ориентированную теорию — это ещё не вся история, но она даёт представление о том, с чем мы имеем дело. Кроме того, мы начали рассматривать различные способы создания экземпляров объектов.

+ +

В следующей статье мы рассмотрим прототипы объектов JavaScript.

+ +

{{PreviousMenuNext("Learn/JavaScript/Objects/Basics", "Learn/JavaScript/Objects/Object_prototypes", "Learn/JavaScript/Objects")}}

+ +

В этом модуле

+ + diff --git a/files/ru/learn/javascript/objects/classes_in_javascript/index.html b/files/ru/learn/javascript/objects/classes_in_javascript/index.html new file mode 100644 index 0000000000..ec27663266 --- /dev/null +++ b/files/ru/learn/javascript/objects/classes_in_javascript/index.html @@ -0,0 +1,267 @@ +--- +title: Наследование в JavaScript +slug: Learn/JavaScript/Objects/Classes_in_JavaScript +tags: + - JavaScript + - Наследование + - ООП +translation_of: Learn/JavaScript/Objects/Inheritance +original_slug: Learn/JavaScript/Objects/Inheritance +--- +

+ +

+ + + +
{{LearnSidebar}}
+ +
{{PreviousMenuNext("Learn/JavaScript/Objects/Object_prototypes", "Learn/JavaScript/Objects/JSON", "Learn/JavaScript/Objects")}}
+ +

Теперь, когда объясняется большая часть подробностей OOJS, эта статья показывает, как создавать «дочерние» классы объектов (конструкторы), которые наследуют признаки из своих «родительских» классов. Кроме того, мы дадим некоторые советы о том, когда и где вы можете использовать OOJS , и посмотрим, как классы рассматриваются в современном синтаксисе ECMAScript.

+ + + + + + + + + + + + +
Необходимые знания: +

Базовая компьютерная грамотность, понимание основ HTML и CSS, знакомство с основами JavaScript (см. Первые шаги и Структурные элементы) and основы Объектно-ориентированного JS (см. Введение в объекты).

+
Цель:Понять, как можно реализовать наследование в JavaScript.
+ +

Прототипное наследование

+ +

До сих пор мы видели некоторое наследование в действии - мы видели, как работают прототипы и как элементы наследуются, поднимаясь по цепочке. Но в основном это связано с встроенными функциями браузера. Как создать объект в JavaScript, который наследует от другого объекта?

+ +

Давайте рассмотрим, как это сделать на конкретном примере.

+ +

Начало работы

+ +

Прежде всего сделайте себе локальную копию нашего файла oojs-class-inheritance-start.html (он также работает в режиме реального времени). В файле вы найдёте тот же пример конструктора Person(), который мы использовали на протяжении всего модуля, с небольшим отличием - мы определили внутри конструктора только лишь свойства:

+ +
function Person(first, last, age, gender, interests) {
+  this.name = {
+    first,
+    last
+  };
+  this.age = age;
+  this.gender = gender;
+  this.interests = interests;
+};
+ +

Все методы определены в прототипе конструктора. Например:

+ +
Person.prototype.greeting = function() {
+  alert('Hi! I\'m ' + this.name.first + '.');
+};
+ +
+

Примечание. В исходном коде вы также увидите определённые методы bio() и farewell(). Позже вы увидите, как они могут быть унаследованы другими конструкторами.

+
+ +

Скажем так, мы хотели создать класс Teacher, подобный тому, который мы описали в нашем первоначальном объектно-ориентированном определении, которое наследует всех членов от Person, но также включает в себя:

+ +
    +
  1. Новое свойство, subject - оно будет содержать предмет, который преподаёт учитель.
  2. +
  3. Обновлённый метод greeting(), который звучит немного более формально, чем стандартный метод greeting()— более подходит для учителя, обращающегося к некоторым ученикам в школе.
  4. +
+ +

Определение функции-конструктора Teacher()

+ +

Первое, что нам нужно сделать, это создать конструктор Teacher() - добавьте ниже следующий код:

+ +
function Teacher(first, last, age, gender, interests, subject) {
+  Person.call(this, first, last, age, gender, interests);
+
+  this.subject = subject;
+}
+ +

Это похоже на конструктор Person во многих отношениях, но здесь есть что-то странное, что мы не видели раньше - функцию call(). Эта функция в основном позволяет вам вызывать функцию, определённую где-то в другом месте, но в текущем контексте. Первый параметр указывает значение this, которое вы хотите использовать при выполнении функции, а остальные параметры - те, которые должны быть переданы функции при её вызове.

+ +

Мы хотим, чтобы конструктор Teacher() принимал те же параметры, что и конструктор Person(), от которого он наследуется, поэтому мы указываем их как параметры в вызове call().

+ +

Последняя строка внутри конструктора просто определяет новое свойство subject, которое будут иметь учителя, и которого нет у Person().

+ +

В качестве примечания мы могли бы просто сделать это:

+ +
function Teacher(first, last, age, gender, interests, subject) {
+  this.name = {
+    first,
+    last
+  };
+  this.age = age;
+  this.gender = gender;
+  this.interests = interests;
+  this.subject = subject;
+}
+ +

Но это просто переопределяет свойства заново, а не наследует их от Person(), так что теряется смысл того, что мы пытаемся сделать. Он также занимает больше строк кода.

+ +

Наследование от конструктора без параметров

+ +

Обратите внимание, что если конструктор, от которого вы наследуете, не принимает значения своего свойства из параметров, вам не нужно указывать их в качестве дополнительных аргументов в call(). Так, например, если у вас было что-то действительно простое:

+ +
function Brick() {
+  this.width = 10;
+  this.height = 20;
+}
+ +

Вы можете наследовать свойства width и height, выполнив это (как и другие шаги, описанные ниже, конечно):

+ +
function BlueGlassBrick() {
+  Brick.call(this);
+
+  this.opacity = 0.5;
+  this.color = 'blue';
+}
+ +

Обратите внимание, что мы указали только this внутри call() - никаких других параметров не требуется, поскольку мы не наследуем никаких свойств родителя, которые задаются через параметры.

+ +

Установка Teacher()'s prototype и конструктор ссылок

+ +

Пока все хорошо, но у нас есть проблема. Мы определили новый конструктор и у него есть свойство prototype, которое по умолчанию просто содержит ссылку на саму конструкторскую функцию. Он не содержит методов свойства prototype конструктора Person. Чтобы увидеть это, введите Object.getOwnPropertyNames(Teacher.prototype) в поле ввода текста или в вашу консоль JavaScript. Затем введите его снова, заменив Teacher на Person. Новый конструктор не наследует эти методы. Чтобы увидеть это, сравните выводы в консоль Person.prototype.greeting и Teacher.prototype.greeting. Нам нужно заставить Teacher() наследовать методы, определённые на прототипе Person(). Итак, как мы это делаем?

+ +
    +
  1. Добавьте следующую строку ниже своего предыдущего добавления: +
    Teacher.prototype = Object.create(Person.prototype);
    + Здесь наш друг create() снова приходит на помощь. В этом случае мы используем его для создания нового объекта и делаем его значением Teacher.prototype. Новый объект имеет свой прототип Person.prototype и, следовательно, наследует, если и когда это необходимо, все доступные методы Person.prototype.
  2. +
  3. Нам нужно сделать ещё одну вещь, прежде чем двигаться дальше. После добавления последней строки, Teacher.prototype.constructor стало равным Person(), потому что мы просто устанавливаем Teacher.prototype для ссылки на объект, который наследует его свойства от Person.prototype! Попробуйте сохранить код, загрузите страницу в браузере и введите Teacher.prototype.constructor в консоль для проверки.
  4. +
  5. Это может стать проблемой, поэтому нам нужно сделать это правильно. Вы можете сделать это, вернувшись к исходному коду и добавив следующие строки внизу: +
    Object.defineProperty(Teacher.prototype, 'constructor', {
    +    value: Teacher,
    +    enumerable: false, // false, чтобы данное свойство не появлялось в цикле for in
    +    writable: true });
    +
  6. +
  7. Теперь, если вы сохраните и обновите, введите Teacher.prototype.constructor, чтобы вернуть Teacher(), плюс мы теперь наследуем Person()!
  8. +
+ +

Предоставление Teacher() новой функции greeting()

+ +

Чтобы завершить наш код, нам нужно определить новую функцию greeting() в конструкторе Teacher().

+ +

Самый простой способ сделать это - определить его на прототипе Teacher() - добавить в нижнюю часть кода следующее:

+ +
Teacher.prototype.greeting = function() {
+  var prefix;
+
+  if (this.gender === 'male' || this.gender === 'Male' || this.gender === 'm' || this.gender === 'M') {
+    prefix = 'Mr.';
+  } else if (this.gender === 'female' || this.gender === 'Female' || this.gender === 'f' || this.gender === 'F') {
+    prefix = 'Mrs.';
+  } else {
+    prefix = 'Mx.';
+  }
+
+  alert('Hello. My name is ' + prefix + ' ' + this.name.last + ', and I teach ' + this.subject + '.');
+};
+ +

Это выводит на экран приветствие учителя, в котором используется соответствующий префикс имени для своего пола, разработанный с использованием условного оператора.

+ +

Попробуйте пример

+ +

Теперь, когда вы ввели весь код, попробуйте создать экземпляр объекта из Teacher(), поставив ниже вашего JavaScript-кода (или что-то похожее по вашему выбору):

+ +
var teacher1 = new Teacher('Dave', 'Griffiths', 31, 'male', ['football', 'cookery'], 'mathematics');
+ +

Теперь сохраните, обновите, и попробуйте получить доступ к свойствам и методам вашего нового объекта teacher1, например:

+ +
teacher1.name.first;
+teacher1.interests[0];
+teacher1.bio();
+teacher1.subject;
+teacher1.greeting();
+teacher1.farewell();
+ +

Все должно работать нормально. Запросы в строках 1, 2, 3 и 6 унаследованные от общего конструктора Person() (класса). Запрос в строке 4 обращается к subject, доступному только для более специализированного конструктора (класса) Teacher(). Запрос в строке 5 получил бы доступ к методу greeting(), унаследованному от Person(), но Teacher() имеет свой собственный метод greeting() с тем же именем, поэтому запрос обращается к этому методу.

+ +
+

Примечание. Если вам не удаётся заставить это работать, сравните свой код с нашей готовой версией (см. также рабочее демо).

+
+ +

Методика, которую мы здесь рассмотрели, - это не единственный способ создания наследующих классов в JavaScript, но он работает нормально и это даёт вам представление о том, как реализовать наследование в JavaScript.

+ +

Вам также может быть интересно узнать некоторые из новых функций {{glossary("ECMAScript")}}, которые позволяют нам делать наследование более чисто в JavaScript (см. Classes). Мы не рассматривали их здесь, поскольку они пока не поддерживаются очень широко в браузерах. Все остальные конструкторы кода, которые мы обсуждали в этом наборе статей, поддерживаются ещё в IE9 или ранее и есть способы добиться более ранней поддержки, чем это.

+ +

Обычный способ - использовать библиотеку JavaScript - большинство популярных опций имеют простой набор функций, доступных для выполнения наследования более легко и быстро. CoffeeScript , например, предоставляет класс, расширяет и т.д.

+ +

Дальнейшее упражнение

+ +

В нашем руководстве по Объектно-ориентированному JavaScript для начинающих мы также включили класс Student как концепцию, которая наследует все особенности Person, а также имеет другой метод greeting() от Person, который гораздо более неформален, чем приветствие Teacher. Посмотрите, как выглядит приветствие ученика в этом разделе, и попробуйте реализовать собственный конструктор Student(), который наследует все функции Person() и реализует другую функцию greeting().

+ +
+

Примечание. Если вам не удаётся заставить это работать, сравните свой код с нашей готовой версией (см. также рабочее демо).

+
+ +

Object member summary

+ +

Подводя итог, вы в основном получили три типа свойств / методов, о которых нужно беспокоиться:

+ +
    +
  1. Те, которые определены внутри функции-конструктора, которые присваиваются экземплярам объекта. Их довольно легко заметить - в вашем собственном коде они представляют собой элементы, определённые внутри конструктора, используя строки this.x = x; в встроенном коде браузера они являются членами, доступными только для экземпляров объектов (обычно создаются путём вызова конструктора с использованием ключевого слова new, например var myInstance = new myConstructor ().
  2. +
  3. Те, которые определяются непосредственно самим конструктором, которые доступны только для конструктора. Они обычно доступны только для встроенных объектов браузера и распознаются путём непосредственной привязки к конструктору, а не к экземпляру. Например, Object.keys().
  4. +
  5. Те, которые определены в прототипе конструктора, которые наследуются всеми экземплярами и наследуют классы объектов. К ним относятся любой член, определённый в свойстве прототипа конструктора, например. myConstructor.prototype.x().
  6. +
+ +

Если вы не уверены, что это такое, не беспокойтесь об этом, пока вы ещё учитесь и знание придёт с практикой.

+ +

Когда вы используете наследование в JavaScript?

+ +

В частности, после этой последней статьи вы можете подумать: «У-у-у, это сложно». Ну, ты прав. Прототипы и наследование представляют собой некоторые из самых сложных аспектов JavaScript, но многие возможности и гибкость JavaScript вытекают из его структуры объектов и наследования и стоит понять, как это работает.

+ +

В некотором смысле вы используете наследование все время. Всякий раз, когда вы используете различные функции веб-API или методы/свойства, определённые во встроенном объекте браузера, который вы вызываете в своих строках, массивах и т.д., вы неявно используете наследование.

+ +

Что касается использования наследования в вашем собственном коде, вы, вероятно, не будете часто его использовать, особенно для начала и в небольших проектах. Это пустая трата времени на использование объектов и наследование только ради этого, когда они вам не нужны. Но по мере того, как ваши базы кода становятся больше, вы с большей вероятностью найдёте необходимость в этом. Если вы начинаете создавать несколько объектов с подобными функциями, то создание универсального типа объекта, содержащего все общие функции и наследование этих функций в более специализированных типах объектов, может быть удобным и полезным.

+ +
+

Примечание. Из-за того, как работает JavaScript, с цепочкой прототипов и т.д., совместное использование функций между объектами часто называется делегированием. Специализированные объекты делегируют функциональность универсальному типу объекта.

+
+ +

При использовании наследования вам рекомендуется не иметь слишком много уровней наследования и тщательно отслеживать, где вы определяете свои методы и свойства. Можно начать писать код, который временно изменяет прототипы встроенных объектов браузера, но вы не должны этого делать, если у вас нет действительно веской причины. Слишком много наследования могут привести к бесконечной путанице и бесконечной боли при попытке отладки такого кода.

+ +

В конечном счёте, объекты - это ещё одна форма повторного использования кода, например функций или циклов, со своими конкретными ролями и преимуществами. Если вы обнаруживаете, что создаёте кучу связанных переменных и функций и хотите отслеживать их все вместе и аккуратно их упаковывать, объект является хорошей идеей. Объекты также очень полезны, когда вы хотите передать коллекцию данных из одного места в другое. Обе эти вещи могут быть достигнуты без использования конструкторов или наследования. Если вам нужен только один экземпляр объекта, вам лучше всего использовать литерал объекта и вам, разумеется, не нужно наследование.

+ +

Резюме

+ +

В этой статье мы рассмотрели оставшуюся часть основной теории и синтаксиса OOJS, которые, как мы думаем, вам следует знать сейчас. На этом этапе вы должны понимать основы JavaScript, ООП, прототипы и прототипное наследование, как создавать классы (конструкторы) и экземпляры объектов, добавлять функции в классы и создавать подклассы, которые наследуются от других классов.

+ +

В следующей статье мы рассмотрим, как работать с JavaScript Object Notation (JSON), общим форматом обмена данными, написанным с использованием объектов JavaScript.

+ +

See also

+ + + +

{{PreviousMenuNext("Learn/JavaScript/Objects/Object_prototypes", "Learn/JavaScript/Objects/JSON", "Learn/JavaScript/Objects")}}

+ +

В этом модуле

+ + diff --git a/files/ru/learn/javascript/objects/inheritance/index.html b/files/ru/learn/javascript/objects/inheritance/index.html deleted file mode 100644 index f1514a92c6..0000000000 --- a/files/ru/learn/javascript/objects/inheritance/index.html +++ /dev/null @@ -1,267 +0,0 @@ ---- -title: Наследование в JavaScript -slug: Learn/JavaScript/Objects/Inheritance -tags: - - JavaScript - - Наследование - - ООП -translation_of: Learn/JavaScript/Objects/Inheritance -original_slug: Learn/JavaScript/Объекты/Inheritance ---- -

- -

- - - -
{{LearnSidebar}}
- -
{{PreviousMenuNext("Learn/JavaScript/Objects/Object_prototypes", "Learn/JavaScript/Objects/JSON", "Learn/JavaScript/Objects")}}
- -

Теперь, когда объясняется большая часть подробностей OOJS, эта статья показывает, как создавать «дочерние» классы объектов (конструкторы), которые наследуют признаки из своих «родительских» классов. Кроме того, мы дадим некоторые советы о том, когда и где вы можете использовать OOJS , и посмотрим, как классы рассматриваются в современном синтаксисе ECMAScript.

- - - - - - - - - - - - -
Необходимые знания: -

Базовая компьютерная грамотность, понимание основ HTML и CSS, знакомство с основами JavaScript (см. Первые шаги и Структурные элементы) and основы Объектно-ориентированного JS (см. Введение в объекты).

-
Цель:Понять, как можно реализовать наследование в JavaScript.
- -

Прототипное наследование

- -

До сих пор мы видели некоторое наследование в действии - мы видели, как работают прототипы и как элементы наследуются, поднимаясь по цепочке. Но в основном это связано с встроенными функциями браузера. Как создать объект в JavaScript, который наследует от другого объекта?

- -

Давайте рассмотрим, как это сделать на конкретном примере.

- -

Начало работы

- -

Прежде всего сделайте себе локальную копию нашего файла oojs-class-inheritance-start.html (он также работает в режиме реального времени). В файле вы найдёте тот же пример конструктора Person(), который мы использовали на протяжении всего модуля, с небольшим отличием - мы определили внутри конструктора только лишь свойства:

- -
function Person(first, last, age, gender, interests) {
-  this.name = {
-    first,
-    last
-  };
-  this.age = age;
-  this.gender = gender;
-  this.interests = interests;
-};
- -

Все методы определены в прототипе конструктора. Например:

- -
Person.prototype.greeting = function() {
-  alert('Hi! I\'m ' + this.name.first + '.');
-};
- -
-

Примечание. В исходном коде вы также увидите определённые методы bio() и farewell(). Позже вы увидите, как они могут быть унаследованы другими конструкторами.

-
- -

Скажем так, мы хотели создать класс Teacher, подобный тому, который мы описали в нашем первоначальном объектно-ориентированном определении, которое наследует всех членов от Person, но также включает в себя:

- -
    -
  1. Новое свойство, subject - оно будет содержать предмет, который преподаёт учитель.
  2. -
  3. Обновлённый метод greeting(), который звучит немного более формально, чем стандартный метод greeting()— более подходит для учителя, обращающегося к некоторым ученикам в школе.
  4. -
- -

Определение функции-конструктора Teacher()

- -

Первое, что нам нужно сделать, это создать конструктор Teacher() - добавьте ниже следующий код:

- -
function Teacher(first, last, age, gender, interests, subject) {
-  Person.call(this, first, last, age, gender, interests);
-
-  this.subject = subject;
-}
- -

Это похоже на конструктор Person во многих отношениях, но здесь есть что-то странное, что мы не видели раньше - функцию call(). Эта функция в основном позволяет вам вызывать функцию, определённую где-то в другом месте, но в текущем контексте. Первый параметр указывает значение this, которое вы хотите использовать при выполнении функции, а остальные параметры - те, которые должны быть переданы функции при её вызове.

- -

Мы хотим, чтобы конструктор Teacher() принимал те же параметры, что и конструктор Person(), от которого он наследуется, поэтому мы указываем их как параметры в вызове call().

- -

Последняя строка внутри конструктора просто определяет новое свойство subject, которое будут иметь учителя, и которого нет у Person().

- -

В качестве примечания мы могли бы просто сделать это:

- -
function Teacher(first, last, age, gender, interests, subject) {
-  this.name = {
-    first,
-    last
-  };
-  this.age = age;
-  this.gender = gender;
-  this.interests = interests;
-  this.subject = subject;
-}
- -

Но это просто переопределяет свойства заново, а не наследует их от Person(), так что теряется смысл того, что мы пытаемся сделать. Он также занимает больше строк кода.

- -

Наследование от конструктора без параметров

- -

Обратите внимание, что если конструктор, от которого вы наследуете, не принимает значения своего свойства из параметров, вам не нужно указывать их в качестве дополнительных аргументов в call(). Так, например, если у вас было что-то действительно простое:

- -
function Brick() {
-  this.width = 10;
-  this.height = 20;
-}
- -

Вы можете наследовать свойства width и height, выполнив это (как и другие шаги, описанные ниже, конечно):

- -
function BlueGlassBrick() {
-  Brick.call(this);
-
-  this.opacity = 0.5;
-  this.color = 'blue';
-}
- -

Обратите внимание, что мы указали только this внутри call() - никаких других параметров не требуется, поскольку мы не наследуем никаких свойств родителя, которые задаются через параметры.

- -

Установка Teacher()'s prototype и конструктор ссылок

- -

Пока все хорошо, но у нас есть проблема. Мы определили новый конструктор и у него есть свойство prototype, которое по умолчанию просто содержит ссылку на саму конструкторскую функцию. Он не содержит методов свойства prototype конструктора Person. Чтобы увидеть это, введите Object.getOwnPropertyNames(Teacher.prototype) в поле ввода текста или в вашу консоль JavaScript. Затем введите его снова, заменив Teacher на Person. Новый конструктор не наследует эти методы. Чтобы увидеть это, сравните выводы в консоль Person.prototype.greeting и Teacher.prototype.greeting. Нам нужно заставить Teacher() наследовать методы, определённые на прототипе Person(). Итак, как мы это делаем?

- -
    -
  1. Добавьте следующую строку ниже своего предыдущего добавления: -
    Teacher.prototype = Object.create(Person.prototype);
    - Здесь наш друг create() снова приходит на помощь. В этом случае мы используем его для создания нового объекта и делаем его значением Teacher.prototype. Новый объект имеет свой прототип Person.prototype и, следовательно, наследует, если и когда это необходимо, все доступные методы Person.prototype.
  2. -
  3. Нам нужно сделать ещё одну вещь, прежде чем двигаться дальше. После добавления последней строки, Teacher.prototype.constructor стало равным Person(), потому что мы просто устанавливаем Teacher.prototype для ссылки на объект, который наследует его свойства от Person.prototype! Попробуйте сохранить код, загрузите страницу в браузере и введите Teacher.prototype.constructor в консоль для проверки.
  4. -
  5. Это может стать проблемой, поэтому нам нужно сделать это правильно. Вы можете сделать это, вернувшись к исходному коду и добавив следующие строки внизу: -
    Object.defineProperty(Teacher.prototype, 'constructor', {
    -    value: Teacher,
    -    enumerable: false, // false, чтобы данное свойство не появлялось в цикле for in
    -    writable: true });
    -
  6. -
  7. Теперь, если вы сохраните и обновите, введите Teacher.prototype.constructor, чтобы вернуть Teacher(), плюс мы теперь наследуем Person()!
  8. -
- -

Предоставление Teacher() новой функции greeting()

- -

Чтобы завершить наш код, нам нужно определить новую функцию greeting() в конструкторе Teacher().

- -

Самый простой способ сделать это - определить его на прототипе Teacher() - добавить в нижнюю часть кода следующее:

- -
Teacher.prototype.greeting = function() {
-  var prefix;
-
-  if (this.gender === 'male' || this.gender === 'Male' || this.gender === 'm' || this.gender === 'M') {
-    prefix = 'Mr.';
-  } else if (this.gender === 'female' || this.gender === 'Female' || this.gender === 'f' || this.gender === 'F') {
-    prefix = 'Mrs.';
-  } else {
-    prefix = 'Mx.';
-  }
-
-  alert('Hello. My name is ' + prefix + ' ' + this.name.last + ', and I teach ' + this.subject + '.');
-};
- -

Это выводит на экран приветствие учителя, в котором используется соответствующий префикс имени для своего пола, разработанный с использованием условного оператора.

- -

Попробуйте пример

- -

Теперь, когда вы ввели весь код, попробуйте создать экземпляр объекта из Teacher(), поставив ниже вашего JavaScript-кода (или что-то похожее по вашему выбору):

- -
var teacher1 = new Teacher('Dave', 'Griffiths', 31, 'male', ['football', 'cookery'], 'mathematics');
- -

Теперь сохраните, обновите, и попробуйте получить доступ к свойствам и методам вашего нового объекта teacher1, например:

- -
teacher1.name.first;
-teacher1.interests[0];
-teacher1.bio();
-teacher1.subject;
-teacher1.greeting();
-teacher1.farewell();
- -

Все должно работать нормально. Запросы в строках 1, 2, 3 и 6 унаследованные от общего конструктора Person() (класса). Запрос в строке 4 обращается к subject, доступному только для более специализированного конструктора (класса) Teacher(). Запрос в строке 5 получил бы доступ к методу greeting(), унаследованному от Person(), но Teacher() имеет свой собственный метод greeting() с тем же именем, поэтому запрос обращается к этому методу.

- -
-

Примечание. Если вам не удаётся заставить это работать, сравните свой код с нашей готовой версией (см. также рабочее демо).

-
- -

Методика, которую мы здесь рассмотрели, - это не единственный способ создания наследующих классов в JavaScript, но он работает нормально и это даёт вам представление о том, как реализовать наследование в JavaScript.

- -

Вам также может быть интересно узнать некоторые из новых функций {{glossary("ECMAScript")}}, которые позволяют нам делать наследование более чисто в JavaScript (см. Classes). Мы не рассматривали их здесь, поскольку они пока не поддерживаются очень широко в браузерах. Все остальные конструкторы кода, которые мы обсуждали в этом наборе статей, поддерживаются ещё в IE9 или ранее и есть способы добиться более ранней поддержки, чем это.

- -

Обычный способ - использовать библиотеку JavaScript - большинство популярных опций имеют простой набор функций, доступных для выполнения наследования более легко и быстро. CoffeeScript , например, предоставляет класс, расширяет и т.д.

- -

Дальнейшее упражнение

- -

В нашем руководстве по Объектно-ориентированному JavaScript для начинающих мы также включили класс Student как концепцию, которая наследует все особенности Person, а также имеет другой метод greeting() от Person, который гораздо более неформален, чем приветствие Teacher. Посмотрите, как выглядит приветствие ученика в этом разделе, и попробуйте реализовать собственный конструктор Student(), который наследует все функции Person() и реализует другую функцию greeting().

- -
-

Примечание. Если вам не удаётся заставить это работать, сравните свой код с нашей готовой версией (см. также рабочее демо).

-
- -

Object member summary

- -

Подводя итог, вы в основном получили три типа свойств / методов, о которых нужно беспокоиться:

- -
    -
  1. Те, которые определены внутри функции-конструктора, которые присваиваются экземплярам объекта. Их довольно легко заметить - в вашем собственном коде они представляют собой элементы, определённые внутри конструктора, используя строки this.x = x; в встроенном коде браузера они являются членами, доступными только для экземпляров объектов (обычно создаются путём вызова конструктора с использованием ключевого слова new, например var myInstance = new myConstructor ().
  2. -
  3. Те, которые определяются непосредственно самим конструктором, которые доступны только для конструктора. Они обычно доступны только для встроенных объектов браузера и распознаются путём непосредственной привязки к конструктору, а не к экземпляру. Например, Object.keys().
  4. -
  5. Те, которые определены в прототипе конструктора, которые наследуются всеми экземплярами и наследуют классы объектов. К ним относятся любой член, определённый в свойстве прототипа конструктора, например. myConstructor.prototype.x().
  6. -
- -

Если вы не уверены, что это такое, не беспокойтесь об этом, пока вы ещё учитесь и знание придёт с практикой.

- -

Когда вы используете наследование в JavaScript?

- -

В частности, после этой последней статьи вы можете подумать: «У-у-у, это сложно». Ну, ты прав. Прототипы и наследование представляют собой некоторые из самых сложных аспектов JavaScript, но многие возможности и гибкость JavaScript вытекают из его структуры объектов и наследования и стоит понять, как это работает.

- -

В некотором смысле вы используете наследование все время. Всякий раз, когда вы используете различные функции веб-API или методы/свойства, определённые во встроенном объекте браузера, который вы вызываете в своих строках, массивах и т.д., вы неявно используете наследование.

- -

Что касается использования наследования в вашем собственном коде, вы, вероятно, не будете часто его использовать, особенно для начала и в небольших проектах. Это пустая трата времени на использование объектов и наследование только ради этого, когда они вам не нужны. Но по мере того, как ваши базы кода становятся больше, вы с большей вероятностью найдёте необходимость в этом. Если вы начинаете создавать несколько объектов с подобными функциями, то создание универсального типа объекта, содержащего все общие функции и наследование этих функций в более специализированных типах объектов, может быть удобным и полезным.

- -
-

Примечание. Из-за того, как работает JavaScript, с цепочкой прототипов и т.д., совместное использование функций между объектами часто называется делегированием. Специализированные объекты делегируют функциональность универсальному типу объекта.

-
- -

При использовании наследования вам рекомендуется не иметь слишком много уровней наследования и тщательно отслеживать, где вы определяете свои методы и свойства. Можно начать писать код, который временно изменяет прототипы встроенных объектов браузера, но вы не должны этого делать, если у вас нет действительно веской причины. Слишком много наследования могут привести к бесконечной путанице и бесконечной боли при попытке отладки такого кода.

- -

В конечном счёте, объекты - это ещё одна форма повторного использования кода, например функций или циклов, со своими конкретными ролями и преимуществами. Если вы обнаруживаете, что создаёте кучу связанных переменных и функций и хотите отслеживать их все вместе и аккуратно их упаковывать, объект является хорошей идеей. Объекты также очень полезны, когда вы хотите передать коллекцию данных из одного места в другое. Обе эти вещи могут быть достигнуты без использования конструкторов или наследования. Если вам нужен только один экземпляр объекта, вам лучше всего использовать литерал объекта и вам, разумеется, не нужно наследование.

- -

Резюме

- -

В этой статье мы рассмотрели оставшуюся часть основной теории и синтаксиса OOJS, которые, как мы думаем, вам следует знать сейчас. На этом этапе вы должны понимать основы JavaScript, ООП, прототипы и прототипное наследование, как создавать классы (конструкторы) и экземпляры объектов, добавлять функции в классы и создавать подклассы, которые наследуются от других классов.

- -

В следующей статье мы рассмотрим, как работать с JavaScript Object Notation (JSON), общим форматом обмена данными, написанным с использованием объектов JavaScript.

- -

See also

- - - -

{{PreviousMenuNext("Learn/JavaScript/Objects/Object_prototypes", "Learn/JavaScript/Objects/JSON", "Learn/JavaScript/Objects")}}

- -

В этом модуле

- - diff --git a/files/ru/learn/javascript/objects/object-oriented_js/index.html b/files/ru/learn/javascript/objects/object-oriented_js/index.html deleted file mode 100644 index 409aa367f2..0000000000 --- a/files/ru/learn/javascript/objects/object-oriented_js/index.html +++ /dev/null @@ -1,287 +0,0 @@ ---- -title: Объектно-ориентированный JavaScript для начинающих -slug: Learn/JavaScript/Objects/Object-oriented_JS -tags: - - Constructor - - Create - - JavaScript - - OOJS - - Object - - Новичку - - ООП - - экземпляр -translation_of: Learn/JavaScript/Objects/Object-oriented_JS -original_slug: Learn/JavaScript/Объекты/Object-oriented_JS ---- -
{{LearnSidebar}}
- -
{{PreviousMenuNext("Learn/JavaScript/Objects/Basics", "Learn/JavaScript/Objects/Object_prototypes", "Learn/JavaScript/Objects")}}
- -

Разобравшись с основами, сосредоточимся на объектно-ориентированном JavaScript (OOJS) — данная статья даёт базовое представление о теории объектно-ориентированного программирования (ООП), далее рассмотрено как JavaScript эмулирует классы объектов с помощью функции-конструктора и как создаются экземпляры объектов.

- - - - - - - - - - - - -
Необходимые знания: -

Базовая компьютерная грамотность, базовое понимание HTML и CSS, знакомство с основами JavaScript (см. Первые шаги и Cструктурные элементы JavaScript) и основы OOJS (см. Введение в объекты).

-
Цель:Понять основную теорию объектно-ориентированного программирования, как это относится к JavaScript («все является объектом») и как создавать конструкторы и экземпляры объектов.
- -

Объектно-ориентированное программирование: основы

- -

Начнём с упрощённого высокоуровневого представления о том, что такое объектно-ориентированное программирование (ООП). Мы говорим упрощённого, потому что ООП может быстро стать очень сложным, и если сейчас дать полный курс, вероятно, можно запутать больше, чем помочь. Основная идея ООП заключается в том, что мы используем объекты для отображения моделей из реального мира в наших программах и/или упрощения доступа к функциям, которые в противном случае было бы трудно или невозможно использовать.

- -

Объекты могут содержать данные и код, представляющие информацию о том, что вы пытаетесь смоделировать, а также о том, какие у этих объектов должны быть функциональные возможности или поведение. Данные объекта (а часто так же и функции) могут быть точно сохранены (официальный термин "инкапсулированы") внутри пакета объекта, упрощая структуру и доступ к ним. Пакету объекта может быть присвоено определённое имя, на которое можно сослаться и которое иногда называют пространством имён. Объекты также широко используются в качестве хранилищ данных, которые могут быть легко отправлены по сети.

- -

Определение шаблона объекта

- -

Рассмотрим простую программу, которая отображает информацию об учениках и учителях в школе. Здесь мы рассмотрим теорию ООП в целом, а не в контексте какого-либо конкретного языка программирования.

- -

Вернёмся к объекту Person из нашей статьи Основы объектов, который определяет общие сведения и функциональные возможности человека. Есть много вещей, которые вы можете узнать о человеке (его адрес, рост, размер обуви, профиль ДНК, номер паспорта, значимые черты личности ...), но в данном случае нас интересует только имя, возраст, пол и интересы, а также мы хотим иметь возможность написать краткую информацию о нём, основываясь на этих данных, и сделать так, чтобы он поздоровался. Это известно как абстракция — создание простой модели более сложной сущности, которая представляет её наиболее важные аспекты таким образом, чтобы с ней было удобно работать для выполнения целей нашей программы.

- -

- -

В некоторых языках ООП, это общее определение типа объекта называется class (JavaScript использует другой механизм и терминологию, как вы увидите ниже) — это на самом деле не объект, а шаблон, который определяет, какие характеристики должен иметь объект.

- -

Создание реальных объектов

- -

Из нашего класса мы можем создать экземпляры объектов — объекты, содержащие данные и функциональные возможности, определённые в классе. Из нашего класса Person мы теперь можем создавать модели реальных людей:

- -

- -

Когда экземпляр объекта создаётся из класса, для его создания выполняется функция-конструктор класса. Этот процесс создания экземпляра объекта из класса называется создание экземпляра (instantiation) — из класса создаётся экземпляр объекта.

- -

Специализированные классы

- -

В нашем случае нам не нужны все люди — нам требуются учителя и ученики, которые являются более конкретными типами людей. В ООП мы можем создавать новые классы на основе других классов — эти новые дочерние классы могут быть созданы для наследования данных и характеристик родительского класса, так чтобы можно было использовать функциональные возможности, общие для всех типов объекта, вместо того чтобы дублировать их. Когда функциональность различается между классами, можно по мере необходимости определять специализированные функции непосредственно на них.

- -

- -

Это действительно полезно — преподаватели и студенты имеют много общих характеристик, таких как имя, пол и возраст, и удобно определить их только один раз. Вы можете также задать одну и ту же характеристику отдельно в разных классах, поскольку каждое определение этой характеристики будет находиться в отдельном пространстве имён. Например, приветствие студента может быть в форме "Yo, I'm [firstName]" (например Yo, I'm Sam), в то время как учитель может использовать что-то более формальное, такое как "Hello, my name is [Prefix] [lastName], and I teach [Subject]." (например Hello, My name is Mr Griffiths, and I teach Chemistry).

- -
-

Примечание: Если вам интересно, существует специальный термин Polymorphism (Полиморфизм) - это забавное слово, обозначающее реализацию той же функциональности для нескольких типов объекта. 

-
- -

Теперь вы можете создавать экземпляры объекта из дочерних классов. Например:

- -

- -

Далее мы рассмотрим, как ООП теорию можно применить на практике в JavaScript.

- -

Конструкторы и экземпляры объектов

- -

JavaScript использует специальные функции, называемые функциями конструктора (constructor functions) для определения объектов и их свойств. Они полезны, потому что вы часто будете сталкиваться с ситуациями, в которых не известно, сколько объектов вы будете создавать; конструкторы позволяют создать столько объектов, сколько нужно эффективным способом, прикреплением данных и функций для объектов по мере необходимости.

- -

Рассмотрим создание классов через конструкторы и создание экземпляров объектов из них в JavaScript. Прежде всего, мы хотели бы, чтобы вы создали новую локальную копию файла oojs.html, который мы видели в нашей первой статье «Объекты».

- -

Простой пример

- -
    -
  1. Давайте рассмотрим как можно определить человека с нормальной функцией. Добавьте эту функцию в элемент script: - -
    function createNewPerson(name) {
    -  const obj = {};
    -  obj.name = name;
    -  obj.greeting = function() {
    -    alert('Hi! I\'m ' + this.name + '.');
    -  };
    -  return obj;
    -}
    -
  2. -
  3. Теперь вы можете создать нового человека, вызвав эту функцию - попробуйте следующие строки в консоли JavaScript браузера: -
    const salva = createNewPerson('Salva');
    -salva.name;
    -salva.greeting();
    - Это работает достаточно хорошо, но код излишне многословен; если мы знаем, что хотим создать объект, зачем нам явно создавать новый пустой объект и возвращать его? К счастью, JavaScript предоставляет нам удобный способ в виде функций-конструкторов - давайте сделаем это сейчас!
  4. -
  5. Замените предыдущую функцию следующей: -
    function Person(name) {
    -  this.name = name;
    -  this.greeting = function() {
    -    alert('Hi! I\'m ' + this.name + '.');
    -  };
    -}
    -
  6. -
- -

Функция-конструктор - это JavaScript версия класса. Вы заметите, что в нем есть все признаки, которые вы ожидаете от функции, хотя он ничего не возвращает и явно не создаёт объект - он в основном просто определяет свойства и методы. Вы также увидите, что ключевое слово this также используется здесь, - это в основном говорит о том, что всякий раз, когда создаётся один из этих экземпляров объектов, свойство имени объекта будет равно значению name, переданному вызову конструктора, и метод greeting() будет использовать значение имени, переданное также вызову конструктора.

- -
-

Примечание: Имя функции конструктора обычно начинается с заглавной буквы - это соглашение используется для упрощения распознавания функций конструктора в коде.

-
- -

Итак, как мы вызываем конструктор для создания некоторых объектов?

- -
    -
  1. Добавьте следующие строки под предыдущим добавлением кода: -
    let person1 = new Person('Bob');
    -let person2 = new Person('Sarah');
    -
  2. -
  3. Сохраните код и перезагрузите его в браузере и попробуйте ввести следующие строки в консоль JS: -
    person1.name
    -person1.greeting()
    -person2.name
    -person2.greeting()
    -
  4. -
- -

Круто! Теперь, как вы видите, у нас есть два новых объекта на странице, каждый из которых хранится в отдельном пространстве имён - при доступе к их свойствам и методам вы должны начинать вызовы с person1 или person2; функциональность, содержащаяся внутри, аккуратно упакована, поэтому она не будет конфликтовать с другими функциями. Тем не менее, у них есть одно и то же свойство name и greeting(). Обратите внимание, что они используют своё собственное значение name, которое было присвоено им, когда они были созданы; это одна из причин, почему очень важно использовать this, таким образом они будут использовать свои собственные значения, а не какие-либо другие.

- -

Давайте снова посмотрим на вызовы конструктора:

- -
let person1 = new Person('Bob');
-let person2 = new Person('Sarah');
- -

В каждом случае ключевое слово new используется, чтобы сообщить браузеру, что мы хотим создать экземпляр нового объекта, за которым следует имя функции с её необходимыми параметрами, содержащимися в круглых скобках, и результат сохраняется в переменной - очень похоже на то, как вызывается стандартная функция. Каждый экземпляр создаётся в соответствии с этим определением:

- -
function Person(name) {
-  this.name = name;
-  this.greeting = function() {
-    alert('Hi! I\'m ' + this.name + '.');
-  };
-}
- -

После создания новых объектов переменные person1 и person2 содержат следующие объекты:

- -
{
-  name: 'Bob',
-  greeting: function() {
-    alert('Hi! I\'m ' + this.name + '.');
-  }
-}
-
-{
-  name: 'Sarah',
-  greeting: function() {
-    alert('Hi! I\'m ' + this.name + '.');
-  }
-}
- -

Обратите внимание, что когда мы вызываем нашу функцию-конструктор, мы определяем greeting() каждый раз, что не является идеальным. Чтобы этого избежать, вместо этого мы можем определить функции на прототипе, о которых мы поговорим позже.

- -

Создавая наш готовый конструктор

- -

Пример, рассмотренный выше, был лишь наглядным примером, чтобы вы поняли суть. Теперь, давайте создадим нашу конечную функцию-конструктор Person().

- -
    -
  1. Замените весь предыдущий код новой функцией конструктора - это, в принципе, тот же самое что и в наглядном примере, но несколько сложнее: -
    function Person(first, last, age, gender, interests) {
    -  this.name = {
    -    first : first,
    -    last: last
    -  };
    -  this.age = age;
    -  this.gender = gender;
    -  this.interests = interests;
    -  this.bio = function() {
    -    alert(this.name.first + ' ' + this.name.last + ' is ' + this.age + ' years old. He likes ' + this.interests[0] + ' and ' + this.interests[1] + '.');
    -  };
    -  this.greeting = function() {
    -    alert('Hi! I\'m ' + this.name.first + '.');
    -  };
    -};
    -
  2. -
  3. Теперь добавьте следующую строку ниже, чтобы создать экземпляр объекта из него: -
    let person1 = new Person('Bob', 'Smith', 32, 'male', ['music', 'skiing']);
    -
  4. -
- -

Как вы могли заметить, вы можете получить доступ к свойствам и методам, как это было ранее, - попробуйте использовать их в консоли JS:

- -
person1['age']
-person1.interests[1]
-person1.bio()
-// etc.
- -
Примечание: Если у вас возникли проблемы с работой кода, попробуйте сравнить его с нашей версией - см. oojs-class-finished.html (также смотрите, как он работает в прямом эфире).
- -

Дальнейшие упражнения

- -

Для начала, попробуйте добавить ещё пару собственных строк создания объекта и попробуйте получить и установить элементы полученных экземпляров объектов.

- -

Кроме того, есть несколько проблем с нашим методом bio() - вывод всегда включает местоимение «He» ("Он" в пер. с англ.), даже если ваш человек является женщиной или какой-либо другой предпочтительной гендерной классификацией. И bio будет включать только два интереса, даже если в массиве interests указано больше. Можете ли вы решить, как исправить это в определении класса (конструкторе)? Вы можете поместить любой код, который вам нравится внутри конструктора (вам, вероятно, понадобятся несколько условий и цикл). Подумайте о том, как предложения должны быть структурированы по-разному в зависимости от пола и в зависимости от того, имеет ли число перечисленных интересов 1, 2 или более 2.

- -
-

Примечание: Если у вас возникли трудности с решением задачи, мы предоставили ответ в нашем репозитории GitHub (см. это в действии) — но сначала попробуйте написать сами!

-
- -

Другие способы создания экземпляров объектов

- -

До сих пор мы видели два разных способа создания экземпляра объекта - объявление объектного литерала и использование функции конструктора (см. выше).

- -

Это имеет смысл, но есть и другие способы - мы бы хотели ознакомить вас с ними на случай, если вы встретите их в своих путешествиях по Сети.

- -

Конструктор Object ()

- -

Прежде всего, вы можете использовать конструктор Object() для создания нового объекта. Да, даже общие объекты имеют конструктор, который генерирует пустой объект.

- -
    -
  1. Попробуйте ввести это в консоль JavaScript вашего браузера: -
    let person1 = new Object();
    -
  2. -
  3. Это сохраняет ссылку на пустой объект в переменную person1. Затем вы можете добавить свойства и методы к этому объекту с использованием точечной или скобочной нотации по желанию; попробуйте эти примеры в консоли: -
    person1.name = 'Chris';
    -person1['age'] = 38;
    -person1.greeting = function() {
    -  alert('Hi! I\'m ' + this.name + '.');
    -};
    -
  4. -
  5. Вы также можете передать литерал объекта конструктору Object() в качестве параметра, чтобы заполнить его свойствами / методами. Попробуйте это в консоли JS: -
    let person1 = new Object({
    -  name: 'Chris',
    -  age: 38,
    -  greeting: function() {
    -    alert('Hi! I\'m ' + this.name + '.');
    -  }
    -});
    -
  6. -
- -

Использование метода create()

- -

Конструкторы могут помочь вам определить порядок кода - вы можете создать конструктор в одном месте, а затем создавать экземпляры по мере необходимости, и их происхождение будет понятным.

- -

Однако некоторые люди предпочитают создавать экземпляры объектов без предварительного создания конструкторов, особенно если они создают только несколько экземпляров объекта. JavaScript имеет встроенный метод create(), который позволяет вам это делать. С его помощью вы можете создать новый объект на основе любого существующего объекта.

- -
    -
  1. Закончив упражнение из предыдущего раздела, загруженное в браузер, попробуйте это в консоли JavaScript: -
    let person2 = Object.create(person1);
    -
  2. -
  3. Теперь попробуйте: -
    person2.name
    -person2.greeting()
    -
  4. -
- -

Вы увидите, что person2 был создан на основе person1 - он имеет те же свойства и метод, доступные для него.

- -

Одно ограничение метода create() заключается в том, что IE8 не поддерживает его. Поэтому конструкторы могут быть более эффективными, если вы хотите поддерживать старые браузеры.

- -

Подробнее мы рассмотрим особенности метода create() немного позже.

- -

Сводка

- -

В этой статье представлен упрощённый взгляд на объектно-ориентированную теорию — это ещё не вся история, но она даёт представление о том, с чем мы имеем дело. Кроме того, мы начали рассматривать различные способы создания экземпляров объектов.

- -

В следующей статье мы рассмотрим прототипы объектов JavaScript.

- -

{{PreviousMenuNext("Learn/JavaScript/Objects/Basics", "Learn/JavaScript/Objects/Object_prototypes", "Learn/JavaScript/Objects")}}

- -

В этом модуле

- - diff --git a/files/zh-cn/_redirects.txt b/files/zh-cn/_redirects.txt index 1a0110161e..46bba1f59e 100644 --- a/files/zh-cn/_redirects.txt +++ b/files/zh-cn/_redirects.txt @@ -1096,6 +1096,8 @@ /zh-CN/docs/Learn/HTML/Multimedia_and_embedding/在网页中添加矢量图形 /zh-CN/docs/Learn/HTML/Multimedia_and_embedding/Adding_vector_graphics_to_the_Web /zh-CN/docs/Learn/JavaScript/First_steps/变量 /zh-CN/docs/Learn/JavaScript/First_steps/Variables /zh-CN/docs/Learn/JavaScript/First_steps/第一点 /zh-CN/docs/Learn/JavaScript/First_steps/A_first_splash +/zh-CN/docs/Learn/JavaScript/Objects/Inheritance /zh-CN/docs/Learn/JavaScript/Objects/Classes_in_JavaScript +/zh-CN/docs/Learn/JavaScript/Objects/Object-oriented_JS /zh-CN/docs/conflicting/Learn/JavaScript/Objects/Classes_in_JavaScript /zh-CN/docs/Learn/JavaScript/Objects/向“弹跳球”演示程序添加新功能 /zh-CN/docs/Learn/JavaScript/Objects/Adding_bouncing_balls_features /zh-CN/docs/Learn/JavaScript/Objects/测试你的技能:面向对象的Javascript /zh-CN/docs/Learn/JavaScript/Objects/Test_your_skills:_Object-oriented_JavaScript /zh-CN/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/介绍 /zh-CN/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Introduction diff --git a/files/zh-cn/_wikihistory.json b/files/zh-cn/_wikihistory.json index 316c67967b..3b0798204d 100644 --- a/files/zh-cn/_wikihistory.json +++ b/files/zh-cn/_wikihistory.json @@ -4716,7 +4716,7 @@ "supermanbin" ] }, - "Learn/JavaScript/Objects/Inheritance": { + "Learn/JavaScript/Objects/Classes_in_JavaScript": { "modified": "2020-07-30T15:02:15.810Z", "contributors": [ "Damoness", @@ -4753,31 +4753,6 @@ "VtanSen" ] }, - "Learn/JavaScript/Objects/Object-oriented_JS": { - "modified": "2020-11-05T13:41:11.779Z", - "contributors": [ - "ceociocto", - "Jepson", - "ChongyouXu", - "九千鸦", - "Catherine-G", - "voidxiaobei", - "oceanMIH", - "WindLo", - "real-Row", - "GKilyar", - "Ray-Eldath", - "HYdragon", - "GloryChou", - "1013940326", - "Viki-Zhang", - "Leivy", - "breakair", - "zhangyangcisco", - "cylmemory", - "keyulin" - ] - }, "Learn/JavaScript/Objects/Object_building_practice": { "modified": "2020-07-16T22:32:33.084Z", "contributors": [ @@ -46762,6 +46737,31 @@ "xcffl" ] }, + "conflicting/Learn/JavaScript/Objects/Classes_in_JavaScript": { + "modified": "2020-11-05T13:41:11.779Z", + "contributors": [ + "ceociocto", + "Jepson", + "ChongyouXu", + "九千鸦", + "Catherine-G", + "voidxiaobei", + "oceanMIH", + "WindLo", + "real-Row", + "GKilyar", + "Ray-Eldath", + "HYdragon", + "GloryChou", + "1013940326", + "Viki-Zhang", + "Leivy", + "breakair", + "zhangyangcisco", + "cylmemory", + "keyulin" + ] + }, "conflicting/Learn/Server-side/Django": { "modified": "2019-03-23T23:11:53.165Z", "contributors": [ diff --git a/files/zh-cn/conflicting/learn/javascript/objects/classes_in_javascript/index.html b/files/zh-cn/conflicting/learn/javascript/objects/classes_in_javascript/index.html new file mode 100644 index 0000000000..5b6fa5b218 --- /dev/null +++ b/files/zh-cn/conflicting/learn/javascript/objects/classes_in_javascript/index.html @@ -0,0 +1,268 @@ +--- +title: 适合初学者的JavaScript面向对象 +slug: conflicting/Learn/JavaScript/Objects/Classes_in_JavaScript +translation_of: Learn/JavaScript/Objects/Object-oriented_JS +original_slug: Learn/JavaScript/Objects/Object-oriented_JS +--- +
{{LearnSidebar}}
+ +
{{PreviousMenuNext("Learn/JavaScript/Objects/Basics", "Learn/JavaScript/Objects/Object_prototypes", "Learn/JavaScript/Objects")}}
+ +

学完基础后, 现在我们集中于面向对象的 JavaScript (OOJS) — 本文首先提出了面向对象编程(OOP) 理论的基本观点,然后探索如何通过构造函数模拟对象类,以及如何创建对象.

+ + + + + + + + + + + + +
预备知识:计算机基础知识, 了解 HTML 和 CSS, 熟悉 JavaScrpit 基础知识 (查看 First stepsBuilding blocks) 和 OOJS 基础 (查看 Introduction to objects).
目标:掌握面向对象程序的基本理论, 这涉及到 JavaScript  的("万物皆对象"), 以及如何创建构造器和对象.
+ +

从零开始面向对象的程序设计

+ +

首先,我们从高维度且简化的角度看看 面向对象的程序(Object-oriented programming ,OOP)是什么。我们将简单描述OOP,因为OOP该概念已变得很复杂,如果完整地描述OOP将使读者难以理解。OOP 的基本思想是:在程序里,我们通过使用对象去构建现实世界的模型,把原本很难(或不可能)被使用的功能,简单化并提供出来,以供访问。

+ +

对象可以包含相关的数据和代码,这些数据和代码用于表示 你所建造的模型是什么样子,以及拥有什么样的行为或功能。对象包(object package,或者叫命名空间 namespace)存储(官方用语:封装)着对象的数据(常常还包括函数),使数据的组织和访问变得更容易了;对象也常用作 数据存储体(data stores),用于在网络上运输数据,十分便捷。

+ +

定义一个对象模板

+ +

让我们来考虑一个简单的程序,它可以显示一个学校的学生和老师的信息.在这里我们不讨论任何程序语言,我们只讨论 OOP 思想.

+ +

首先,我们可以回到上一节拿到定义好属性和方法的Person对象。对于一个人(person)来说,我们能在他们身上获取到很多信息(他们的住址,身高,鞋码,基因图谱,护照信息,显著的性格特征等等),然而,我们仅仅需要他们的名字,年龄,性别,兴趣 这些信息,然后,我们会基于他们的这些信息写一个简短的介绍关于他们自己,在最后我们还需要教会他们打招呼。以上的方式被称为抽象-为了我们编程的目标而利用事物的一些重要特性去把复杂的事物简单化

+ +

+ +

在一些面向对象的语言中,我们用类(class)的概念去描述一个对象(您在下面就能看到JavaScript使用了一个完全不同的术语)-类并不完全是一个对象,它更像是一个定义对象特质的模板。

+ +

创造一个真正的对象

+ +

从上面我们创建的class中, 我们能够基于它创建出一些对象 - 一些拥有class中属性及方法的对象。基于我们的Person类,我们可以创建出许许多多的真实的人:

+ +

+ +

当一个对象需要从类中创建出来时,类的构造函数就会运行来创建这个实例。这种创建对象的过程我们称之为实例化-实例对象被类实例化。

+ +

具体的对象

+ +

在这个例子里,我们不想要泛指的人,我们想要像老师和学生这样类型更为具体的人。在 OOP 里,我们可以创建基于其它类的新类,这些新的子类可以继承它们父类的数据和功能。比起复制来说这样能够使用父对象共有的功能。如果类之间的功能不同,你可以根据需要定义专用的特征。

+ +

+ +

这是非常有用的,老师和学生具有一些相同的特征比如姓名、性别、年龄,因此只需要定义这些特征一次就可以了。您可以在不同的类里分开定义这些相同的特征,这样该特征会有一个不同的命名空间。比如,一个学生的 greeting 可以是 "Yo, I'm [firstName]" (例子 Yo, I'm Sam),老师的可能会正式一些,比如"Hello, my name is [Prefix] [lastName]" (例子 Hello, My name is Mr Griffiths)。

+ +
+

注:多态——这个高大上的词正是用来描述多个对象拥有实现共同方法的能力。

+
+ +

现在可以根据子类创建对象。如:

+ +

下面我们来看看 OOP 理论如何应用到 JavaScript 实践中去的。

+ +

构建函数和对象

+ +

有些人认为 JavaScript 不是真正的面向对象的语言,比如它没有像许多面向对象的语言一样有用于创建class类的声明。JavaScript 用一种称为构建函数的特殊函数来定义对象和它们的特征。构建函数非常有用,因为很多情况下您不知道实际需要多少个对象(实例)。构建函数提供了创建您所需对象(实例)的有效方法,将对象的数据和特征函数按需联结至相应对象。

+ +

不像“经典”的面向对象的语言,从构建函数创建的新实例的特征并非全盘复制,而是通过一个叫做原形链的参考链链接过去的。(参见 Object prototypes),所以这并非真正的实例,严格的讲, JavaScript 在对象间使用和其它语言的共享机制不同。

+ +
+

注: “经典”的面向对象的语言并非好事,就像上面提到的,OOP 可能很快就变得非常复杂,JavaScript 找到了在不变的特别复杂的情况下利用面向对象的优点的方法。

+
+ +

让我们来看看 JavaScript 如何通过构建函数对象来创建类。首先,请先复制一个新的前文提到的oojs.html

+ +

一个简单的例子

+ +
    +
  1. 让我们看看如何通过一个普通的函数定义一个”人“。在您的文件中添加以下代码: +
    function createNewPerson(name) {
    +  var obj = {};
    +  obj.name = name;
    +  obj.greeting = function () {
    +    alert('Hi! I\'m ' + this.name + '.');
    +  }
    +  return obj;
    +}
    +
  2. +
  3. 您现在可以通过调用这个函数创建一个新的叫 salva 的人,在您浏览器的JavaScript console 试试 : +
    var salva = createNewPerson('salva');
    +salva.name;
    +salva.greeting();
    + 上述代码运行良好,但是有点冗长;如果我们知道如何创建一个对象,就没有必要创建一个新的空对象并且返回它。幸好 JavaScript 通过构建函数提供了一个便捷的方法,方法如下:
  4. +
  5. 将之前的代码用如下代码代替: +
    function Person(name) {
    +  this.name = name;
    +  this.greeting = function() {
    +    alert('Hi! I\'m ' + this.name + '.');
    +  };
    +}
    +
  6. +
+ +

这个构建函数是 JavaScript 版本的类。您会发现,它只定义了对象的属性和方法,除了没有明确创建一个对象和返回任何值和之外,它有了您期待的函数所拥有的全部功能。这里使用了this关键词,即无论是该对象的哪个实例被这个构建函数创建,它的 name 属性就是传递到构建函数形参name的值,它的 greeting() 方法中也将使用相同的传递到构建函数形参name的值。

+ +
+

注: 一个构建函数通常是大写字母开头,这样便于区分构建函数和普通函数。

+
+ +

那如何调用构建函数创建新的实例呢?

+ +
    +
  1. 将下面的代码加在您之前的代码下面: +
    var person1 = new Person('Bob');
    +var person2 = new Person('Sarah');
    +
  2. +
  3. 保存并刷新浏览器,在 console 里输入如下代码: +
    person1.name
    +person1.greeting()
    +person2.name
    +person2.greeting()
    +
  4. +
+ +

酷!您现在看到页面上有两个对象,每一个保存在不同的命名空间里,当您访问它们的属性和方法时,您需要使用person1或者person2来调用它们。尽管它们有着相同的name属性和 greeting()方法它们是各自独立的,所以相互的功能不会冲突。注意它们使用的是自己的 name 值,这也是使用 this 关键字的原因,它们使用的从实参传入形参的自己的值,而不是其它的什么值。

+ +

再看看这个构造对象的语法:

+ +
var person1 = new Person('Bob');
+var person2 = new Person('Sarah');
+ +

上述代码中,关键字 new 跟着一个含参函数,用于告知浏览器我们想要创建一个对象,非常类似函数调用,并把结果保存到变量中。每个示例类都是根据下面的方式定义的。

+ +
function Person(name) {
+  this.name = name;
+  this.greeting = function() {
+    alert('Hi! I\'m ' + this.name + '.');
+  };
+}
+
+ +

当新的对象被创立, 变量person1person2有效地包含了以下值:

+ +
{
+  name : 'Bob',
+  greeting : function() {
+    alert('Hi! I\'m ' + this.name + '.');
+  }
+}
+
+{
+  name : 'Sarah',
+  greeting : function() {
+    alert('Hi! I\'m ' + this.name + '.');
+  }
+}
+ +

值得注意的是每次当我们调用构造函数时,我们都会重新定义一遍 greeting(),这不是个理想的方法。为了避免这样,我们可以在原型里定义函数,接下来我们会讲到。

+ +

创建我们最终的构造函数

+ +

上面的例子仅仅是简单地介绍如何开始。让我们现在开始创建Person()构造函数。

+ +
    +
  1. 移除掉您之前写的所有代码, 用如下构造函数替代 —— 实现原理上,这与我们之前的例子并无二致, 只是变得稍稍复杂了些: +
    function Person(first, last, age, gender, interests) {
    +  this.name = {
    +    'first': first,
    +    'last': last
    +  };
    +  this.age = age;
    +  this.gender = gender;
    +  this.interests = interests;
    +  this.bio = function() {
    +    alert(this.name.first + ' ' + this.name.last + ' is ' + this.age + ' years old. He likes ' + this.interests[0] + ' and ' + this.interests[1] + '.');
    +  };
    +  this.greeting = function() {
    +    alert('Hi! I\'m ' + this.name.first + '.');
    +  };
    +};
    +
  2. +
  3. 接下来加上这样一行代码, 用来创建它的一个对象: +
    var person1 = new Person('Bob', 'Smith', 32, 'male', ['music', 'skiing']);
    +
  4. +
+ +

这样,您就可以像我们定义第一个对象一样访问它的属性和方法了:

+ +
person1['age']
+person1.interests[1]
+person1.bio()
+// etc.
+ +
+

注: 如果您对这一部分有疑问, 尝试将您的代码与我们的版本做比较 —— 戳链接: oojs-class-finished.html (或者: 查看它的实现).

+
+ +

进一步的练习

+ +

首先, 尝试着写几行代码创建您自己的对象, 接着,尝试getting与setting对象中的成员。

+ +

此外, 我们的bio()方法里仍有一些问题 —— 尽管您创建的Person是女性,或者是些别的性别类型,输出里的代词都总是 "He"。 而且, 纵然您有更多的兴趣列举在interests数组中, bio只会展示您的两个兴趣。 您能想出如何在类型定义(构造函数)中解决这个问题吗? 您可以按照您喜欢的方式编写构造函数(您可能需要一些条件判断和循环)。 考虑下语句如何根据性别、兴趣列表中兴趣的数目异构。

+ +
+

注:如果您觉得困难, 我们在我们的GitHub仓库里作了回答(查看它的实现) ——但首先请您尝试着自己写出来。

+
+ +

创建对象的其他方式

+ +

到现在为止,我们了解到了两种不同的创建对象的方式 —— 声明一个对象的语法, 与使用构造函数(回顾上面)。

+ +

这些方法都是很有用的, 但仍有其他的方法 —— 我们希望您能熟悉这些,以免您在Web世界的旅行中碰到它们。

+ +

Object()构造函数

+ +

首先, 您能使用Object()构造函数来创建一个新对象。 是的, 一般对象都有构造函数,它创建了一个空的对象。

+ +
    +
  1. 尝试在您浏览器中的Javascript控制台中输入以下代码: +
    var person1 = new Object();
    +
  2. +
  3. 这样就在person1变量中存储了一个空对象。然后, 可以根据需要, 使用点或括号表示法向此对象添加属性和方法;试试这个例子: +
    person1.name = 'Chris';
    +person1['age'] = 38;
    +person1.greeting = function() {
    +  alert('Hi! I\'m ' + this.name + '.');
    +}
    +
  4. +
  5. 还可以将对象文本传递给Object() 构造函数作为参数, 以便用属性/方法填充它。请尝试以下操作: +
    var person1 = new Object({
    +  name : 'Chris',
    +  age : 38,
    +  greeting : function() {
    +    alert('Hi! I\'m ' + this.name + '.');
    +  }
    +});
    +
  6. +
+ +

使用create()方法

+ +

JavaScript有个内嵌的方法create(), 它允许您基于现有对象创建新的对象。

+ +
    +
  1. 在 JavaScript 控制台中尝试此操作: +
    var person2 = Object.create(person1);
    +
  2. +
  3. 现在尝试这个: +
    person2.name
    +person2.greeting()
    +
  4. +
+ +

您可以看到,person2是基于person1创建的, 它们具有相同的属性和方法。这非常有用, 因为它允许您创建新的对象而无需定义构造函数。缺点是比起构造函数,浏览器在更晚的时候才支持create()方法(IE9,  IE8 或甚至以前相比), 加上一些人认为构造函数让您的代码看上去更整洁 —— 您可以在一个地方创建您的构造函数, 然后根据需要创建实例, 这让您能很清楚地知道它们来自哪里。

+ +

但是, 如果您不太担心对旧浏览器的支持, 并且您只需要一个对象的一些副本, 那么创建一个构造函数可能会让您的代码显得过度繁杂。这取决于您的个人爱好。有些人发现create() 更容易理解和使用。

+ +

稍后我们将更详细地探讨create() 的效果。

+ +

总结

+ +

这篇文章简单地介绍了一些面向对象原理 —— 这些描述还不够完整, 但它让您知道我们在这里处理什么。此外, 我们已经开始研究 javascript与 "经典 OOP"的关联与区别, 如何使用构造函数实现 javascript 中的类, 以及生成对象的不同方法。

+ +

在下一篇文章中, 我们将探讨 JavaScript 对象原型。

+ +

{{PreviousMenuNext("Learn/JavaScript/Objects/Basics", "Learn/JavaScript/Objects/Object_prototypes", "Learn/JavaScript/Objects")}}

diff --git a/files/zh-cn/learn/javascript/objects/classes_in_javascript/index.html b/files/zh-cn/learn/javascript/objects/classes_in_javascript/index.html new file mode 100644 index 0000000000..635b1a9574 --- /dev/null +++ b/files/zh-cn/learn/javascript/objects/classes_in_javascript/index.html @@ -0,0 +1,435 @@ +--- +title: JavaScript 中的继承 +slug: Learn/JavaScript/Objects/Classes_in_JavaScript +tags: + - JavaScript + - OOJS + - 原型 + - 对象 + - 继承 + - 面向对象JS +translation_of: Learn/JavaScript/Objects/Inheritance +original_slug: Learn/JavaScript/Objects/Inheritance +--- +
{{LearnSidebar}}
+ +
{{PreviousMenuNext("Learn/JavaScript/Objects/Object_prototypes", "Learn/JavaScript/Objects/JSON", "Learn/JavaScript/Objects")}}
+ +

了解了 OOJS 的大多数细节之后,本文将介绍如何创建“子”对象类别(构造器)并从“父”类别中继承功能。此外,我们还会针对何时何处使用 OOJS 给出建议。

+ + + + + + + + + + + + +
预备知识:基本的计算机素养,对 HTML 和 CSS 有基本的理解,熟悉 JavaScript 基础(参见 First stepsBuilding blocks)以及面向对象的JavaScript (OOJS) 基础(参见 Introduction to objects)。
目标:理解在 JavaScript 中如何实现继承。
+ +

原型式的继承

+ +

到目前为止我们已经了解了一些关于原型链的实现方式以及成员变量是如何通过它来实现继承,但是之前涉及到的大部分都是浏览器内置函数(比如 StringDateNumber 和 Array),那么我们如何创建一个继承自另一对象的JavaScript对象呢?

+ +

正如前面课程所提到的,有些人认为JavaScript并不是真正的面向对象语言,在经典的面向对象语言中,您可能倾向于定义类对象,然后您可以简单地定义哪些类继承哪些类(参考C++ inheritance里的一些简单的例子),JavaScript使用了另一套实现方式,继承的对象函数并不是通过复制而来,而是通过原型链继承(通常被称为 原型式继承 —— prototypal inheritance

+ +

让我们通过具体的例子来解释上述概念

+ +

开始

+ +

首先,将oojs-class-inheritance-start.html文件复制到您本地(也可以 在线运行 ),其中您能看到一个只定义了一些属性的Person()构造器,与之前通过模块来实现所有功能的Person的构造器类似。

+ +
function Person(first, last, age, gender, interests) {
+  this.name = {
+    first,
+    last
+  };
+  this.age = age;
+  this.gender = gender;
+  this.interests = interests;
+};
+
+ +

所有的方法都定义在构造器的原型上,比如:

+ +
Person.prototype.greeting = function() {
+  alert('Hi! I\'m ' + this.name.first + '.');
+};
+
+ +
+

注意:在源代码中,你可以看到已定义的bio()farewell()方法。随后,你将看到它们被其他的构造器所继承。

+
+ +

比如我们想要创建一个Teacher类,就像我们前面在面向对象概念解释时用的那个一样。这个类会继承Person的所有成员,同时也包括:

+ +
    +
  1. 一个新的属性,subject——这个属性包含了教师教授的学科。
  2. +
  3. 一个被更新的greeting()方法,这个方法打招呼听起来比一般的greeting()方法更正式一点——对于一个教授一些学生的老师来说。
  4. +
+ +

定义 Teacher() 构造器函数

+ +

我们要做的第一件事是创建一个Teacher()构造器——将下面的代码加入到现有代码之下:

+ +
function Teacher(first, last, age, gender, interests, subject) {
+  Person.call(this, first, last, age, gender, interests);
+
+  this.subject = subject;
+}
+
+ +

这在很多方面看起来都和Person的构造器很像,但是这里有一些我们从没见过的奇怪玩意——call()函数。基本上,这个函数允许您调用一个在这个文件里别处定义的函数。第一个参数指明了在您运行这个函数时想对“this”指定的值,也就是说,您可以重新指定您调用的函数里所有“this”指向的对象。其他的变量指明了所有目标函数运行时接受的参数。

+ +
+

注:在这个例子里我们在创建一个新的对象实例时同时指派了继承的所有属性,但是注意您需要在构造器里将它们作为参数来指派,即使实例不要求它们被作为参数指派(比如也许您在创建对象的时候已经得到了一个设置为任意值的属性)

+
+ +

所以在这个例子里,我们很有效的在Teacher()构造函数里运行了Person()构造函数(见上文),得到了和在Teacher()里定义的一样的属性,但是用的是传送给Teacher(),而不是Person()的值(我们简单使用这里的this作为传给call()this,意味着this指向Teacher()函数)。

+ +

在构造器里的最后一行代码简单地定义了一个新的subject属性,这将是教师会有的,而一般人没有的属性。

+ +

顺便提一下,我们本也可以这么做:

+ +
function Teacher(first, last, age, gender, interests, subject) {
+  this.name = {
+    first,
+    last
+  };
+  this.age = age;
+  this.gender = gender;
+  this.interests = interests;
+  this.subject = subject;
+}
+
+ +

但是这只是重新定义了一遍属性,并不是将他们从Person()中继承过来的,所以这违背了我们的初衷。这样写也会需要更长的代码。

+ +

从无参构造函数继承

+ +

请注意,如果您继承的构造函数不从传入的参数中获取其属性值,则不需要在call()中为其指定其他参数。所以,例如,如果您有一些相当简单的东西:

+ +
function Brick() {
+  this.width = 10;
+  this.height = 20;
+}
+ +

您可以这样继承widthheight属性(以及下面描述的其他步骤):

+ +
function BlueGlassBrick() {
+  Brick.call(this);
+
+  this.opacity = 0.5;
+  this.color = 'blue';
+}
+ +

请注意,我们仅传入了thiscall()中 - 不需要其他参数,因为我们不会继承通过参数设置的父级的任何属性。

+ +

设置 Teacher() 的原型和构造器引用

+ +

All is good so far, but we have a problem. We have defined a new constructor, and it has a prototype property, which by default just contains an object with a reference to the constructor function itself. It does not contain the methods of the Person constructor's prototype property. To see this, enter Object.getOwnPropertyNames(Teacher.prototype) into either the text input field or your JavaScript console. Then enter it again, replacing Teacher with Person. Nor does the new constructor inherit those methods. To see this, compare the outputs of Person.prototype.greeting and Teacher.prototype.greeting. We need to get Teacher() to inherit the methods defined on Person()'s prototype. So how do we do that?

+ +
    +
  1. Add the following line below your previous addition: +
    Teacher.prototype = Object.create(Person.prototype);
    + Here our friend create() comes to the rescue again. In this case we are using it to create a new object and make it the value of Teacher.prototype. The new object has Person.prototype as its prototype and will therefore inherit, if and when needed, all the methods available on Person.prototype.
  2. +
  3. We need to do one more thing before we move on. After adding the last line, Teacher.prototype's constructor property is now equal to Person(), because we just set Teacher.prototype to reference an object that inherits its properties from Person.prototype! Try saving your code, loading the page in a browser, and entering Teacher.prototype.constructor into the console to verify.
  4. +
  5. This can become a problem, so we need to set this right. You can do so by going back to your source code and adding the following line at the bottom: +
    Object.defineProperty(Teacher.prototype, 'constructor', {
    +    value: Teacher,
    +    enumerable: false, // so that it does not appear in 'for in' loop
    +    writable: true });
    +
  6. +
  7. Now if you save and refresh, entering Teacher.prototype.constructor should return Teacher(), as desired, plus we are now inheriting from Person()!
  8. +
+ +

向 Teacher() 添加一个新的greeting()函数

+ +

为了完善代码,您还需在构造函数Teacher()上定义一个新的函数greeting()。最简单的方法是在Teacher的原型上定义它—把以下代码添加到您代码的底部:

+ +
Teacher.prototype.greeting = function() {
+  var prefix;
+
+  if(this.gender === 'male' || this.gender === 'Male' || this.gender === 'm' || this.gender === 'M') {
+    prefix = 'Mr.';
+  } else if(this.gender === 'female' || this.gender === 'Female' || this.gender === 'f' || this.gender === 'F') {
+    prefix = 'Mrs.';
+  } else {
+    prefix = 'Mx.';
+  }
+
+  alert('Hello. My name is ' + prefix + ' ' + this.name.last + ', and I teach ' + this.subject + '.');
+};
+ +

这样就会出现老师打招呼的弹窗,老师打招呼会使用条件结构判断性别从而使用正确的称呼。

+ +

范例尝试

+ +

现在我们来键入代码,将下面的代码放到您的 JavaScript 代码下面从而来创建一个 Teacher() 对象实例。

+ +
var teacher1 = new Teacher('Dave', 'Griffiths', 31, 'male', ['football', 'cookery'], 'mathematics');
+ +

当您保存代码并刷新的时候,试一下您的老师实例的属性和方法:

+ +
teacher1.name.first;
+teacher1.interests[0];
+teacher1.bio();
+teacher1.subject;
+teacher1.greeting();
+ +

前面三个进入到从Person()的构造器 继承的属性和方法,后面两个则是只有Teacher()的构造器才有的属性和方法。

+ +
+

注:如果您在这里遇到了问题,请对比您的代码与我们的完成版本(或查看可运行的在线示例)。

+
+ +

我们在这里讲述的技巧并不是 JavaScript 中创建继承类的唯一方式,但是这个技巧也还不错,非常好地告诉了您如何在 JavaScript 中实行继承操作。

+ +

您可能对在 JavaScript中使用其他方法来实行继承会感兴趣(参见 Classes)。我们没有覆盖那些内容,因为并不是每种浏览器都会支持这些方法。我们在这一系列文章中介绍的所有其他方法都会被 IE9 支持或者更老的浏览器支持,也有一些方法可以支持更老的浏览器。

+ +

一个常用的方法是使用 JavaScript 语言库——最热门的一些库提供一些方法让我们更快更好地实行继承。比如 CoffeeScript 就提供一些类和扩展。

+ +

更多练习

+ +

在我们的 OOP theory section 模块中, 我们也将学生类作为一个概念,继承了 Person 所有的属性和方法,也有一个不同的打招呼的方法(比老师的打招呼轻松随意一些)。您可以自己尝试一下如何实现。

+ +
+

注:如果你编写时遇到困难,代码无法运行,那么可以查看我们的完成版本(也可查看 可运行的在线示例)。

+
+ +

对象成员总结

+ +

总结一下,你需要在实践中考虑到下面四类属性或方法

+ +
    +
  1. 那些定义在构造器函数中,用于给予对象实例的属性或方法。它们都很容易发现——在您自己的代码中,它们是构造函数中使用this.x = x类型的行;在浏览器内已经构建好的代码中,它们是可用于对象实例的成员(这些对象实例通常使用new关键字调用构造函数来创建,例如var myInstance = new myConstructor())。
  2. +
  3. 那些直接在构造函数上定义,仅在构造函数上可用的属性或方法。它们通常仅在浏览器的内置对象中可用,并通过被直接链接到构造函数来识别,而不是实例。例如Object.keys()它们一般被称作静态属性或静态方法
  4. +
  5. 那些在构造函数原型上定义,由所有实例和对象类继承的属性或方法。它们包括在构造函数的原型属性上定义的任何成员,如myConstructor.prototype.x()
  6. +
  7. Those available on an object instance, which can either be an object created when a constructor is instantiated like we saw above (so for example let teacher1 = new Teacher( 'Chris' ); and then teacher1.name), or an object literal (let teacher1 = { name : 'Chris' } and then teacher1.name).
  8. +
+ +

如果您现在觉得一团浆糊,别担心——您现在还处于学习阶段,不断练习才会慢慢熟悉这些知识。

+ +

ECMAScript 2015 Classes

+ +

ECMAScript 2015 introduces class syntax to JavaScript as a way to write reusable classes using easier, cleaner syntax, which is more similar to classes in C++ or Java. In this section we'll convert the Person and Teacher examples from prototypal inheritance to classes, to show you how it's done.

+ +
+

Note: This modern way of writing classes is supported in all modern browsers, but it is still worth knowing about the underlying prototypal inheritance in case you work on a project that requires supporting a browser that doesn't support this syntax (most notably Internet Explorer).

+
+ +

Let's look at a rewritten version of the Person example, class-style:

+ +
class Person {
+  constructor(first, last, age, gender, interests) {
+    this.name = {
+      first,
+      last
+    };
+    this.age = age;
+    this.gender = gender;
+    this.interests = interests;
+  }
+
+  greeting() {
+    console.log(`Hi! I'm ${this.name.first}`);
+  };
+
+  farewell() {
+    console.log(`${this.name.first} has left the building. Bye for now!`);
+  };
+}
+
+ +

The class statement indicates that we are creating a new class. Inside this block, we define all the features of the class:

+ + + +

We can now instantiate object instances using the new operator, in just the same way as we did before:

+ +
let han = new Person('Han', 'Solo', 25, 'male', ['Smuggling']);
+han.greeting();
+// Hi! I'm Han
+
+let leia = new Person('Leia', 'Organa', 19, 'female', ['Government']);
+leia.farewell();
+// Leia has left the building. Bye for now
+
+ +
+

Note: Under the hood, your classes are being converted into Prototypal Inheritance models — this is just syntactic sugar. But I'm sure you'll agree that it's easier to write.

+
+ +

Inheritance with class syntax

+ +

Above we created a class to represent a person. They have a series of attributes that are common to all people; in this section we'll create our specialized Teacher class, making it inherit from Person using modern class syntax. This is called creating a subclass or subclassing.

+ +

To create a subclass we use the extends keyword to tell JavaScript the class we want to base our class on,

+ +
class Teacher extends Person {
+  constructor(subject, grade) {
+    this.subject = subject;
+    this.grade = grade;
+  }
+}
+ +

but there's a little catch.

+ +

Unlike old-school constructor functions where the new operator does the initialization of this to a newly-allocated object, this isn't automatically initialized for a class defined by the extends keyword, i.e the sub-classes.

+ +

Therefore running the above code will give an error:

+ +
Uncaught ReferenceError: Must call super constructor in derived class before
+accessing 'this' or returning from derived constructor
+ +

For sub-classes, the this initialization to a newly allocated object is always dependant on the parent class constructor, i.e the constructor function of the class from which you're extending.

+ +

Here we are extending the Person class — the Teacher sub-class is an extension of the Person class. So for Teacher, the this initialization is done by the Person constructor.

+ +

To call the parent constructor we have to use the super() operator, like so:

+ +
class Teacher extends Person {
+  constructor(subject, grade) {
+    super(); // Now 'this' is initialized by calling the parent constructor.
+    this.subject = subject;
+    this.grade = grade;
+  }
+}
+ +

There is no point having a sub-class if it doesn't inherit properties from the parent class.
+It is good then, that the super() operator also accepts arguments for the parent constructor.

+ +

Looking back to our Person constructor, we can see it has the following block of code in its constructor method:

+ +
 constructor(first, last, age, gender, interests) {
+   this.name = {
+     first,
+     last
+   };
+   this.age = age;
+   this.gender = gender;
+   this.interests = interests;
+} 
+ +

Since the super() operator is actually the parent class constructor, passing it the necessary arguments of the Parent class constructor will also initialize the parent class properties in our sub-class, thereby inheriting it:

+ +
class Teacher extends Person {
+  constructor(first, last, age, gender, interests, subject, grade) {
+    super(first, last, age, gender, interests);
+
+    // subject and grade are specific to Teacher
+    this.subject = subject;
+    this.grade = grade;
+  }
+}
+
+ +

Now when we instantiate Teacher object instances, we can call methods and properties defined on both Teacherand Person as we'd expect:

+ +
let snape = new Teacher('Severus', 'Snape', 58, 'male', ['Potions'], 'Dark arts', 5);
+snape.greeting(); // Hi! I'm Severus.
+snape.farewell(); // Severus has left the building. Bye for now.
+snape.age // 58
+snape.subject; // Dark arts
+
+ +

Like we did with Teachers, we could create other subclasses of Person to make them more specialized without modifying the base class.

+ +
+

Note: You can find this example on GitHub as es2015-class-inheritance.html (see it live also).

+
+ +

Getters and Setters

+ +

There may be times when we want to change the values of an attribute in the classes we create or we don't know what the final value of an attribute will be. Using the Teacher example, we may not know what subject the teacher will teach before we create them, or their subject may change between terms.

+ +

We can handle such situations with getters and setters.

+ +

Let's enhance the Teacher class with getters and setters. The class starts the same as it was the last time we looked at it.

+ +

Getters and setters work in pairs. A getter returns the current value of the variable and its corresponding setter changes the value of the variable to the one it defines.

+ +

The modified Teacher class looks like this:

+ +
class Teacher extends Person {
+  constructor(first, last, age, gender, interests, subject, grade) {
+    super(first, last, age, gender, interests);
+    // subject and grade are specific to Teacher
+    this._subject = subject;
+    this.grade = grade;
+  }
+
+  get subject() {
+    return this._subject;
+  }
+
+  set subject(newSubject) {
+    this._subject = newSubject;
+  }
+}
+
+ +

In our class above we have a getter and setter for the subject property. We use _ to create a separate value in which to store our name property. Without using this convention, we would get errors every time we called get or set. At this point:

+ + + +

The example below shows the two features in action:

+ +
// Check the default value
+console.log(snape.subject) // Returns "Dark arts"
+
+// Change the value
+snape.subject = "Balloon animals" // Sets _subject to "Balloon animals"
+
+// Check it again and see if it matches the new value
+console.log(snape.subject) // Returns "Balloon animals"
+
+ +
+

Note: You can find this example on GitHub as es2015-getters-setters.html (see it live also).

+
+ +
+

Note: Getters and setters can be very useful at times, for example when you want to run some code every time a property is requested or set. For simple cases, however, plain property access without a getter or setter will do just fine.

+
+ +

何时在 JavaScript 中使用继承?

+ +

特别是在读完这段文章内容之后,您也许会想 "天啊,这实在是太复杂了". 是的,您是对的,原型和继承代表了JavaScript这门语言里最复杂的一些方面,但是JavaScript的强大和灵活性正是来自于它的对象体系和继承方式,这很值得花时间去好好理解下它是如何工作的。

+ +

在某种程度上来说,您一直都在使用继承 - 无论您是使用WebAPI的不同特性还是调用字符串、数组等浏览器内置对象的方法和属性的时候,您都在隐式地使用继承。

+ +

就在自己代码中使用继承而言,您可能不会使用的非常频繁,特别是在小型项目中或者刚开始学习时 - 因为当您不需要对象和继承的时候,仅仅为了使用而使用它们只是在浪费时间而已。但是随着您的代码量的增大,您会越来越发现它的必要性。如果您开始创建一系列拥有相似特性的对象时,那么创建一个包含所有共有功能的通用对象,然后在更特殊的对象类型中继承这些特性,将会变得更加方便有用。

+ +
+

注: 考虑到JavaScript的工作方式,由于原型链等特性的存在,在不同对象之间功能的共享通常被叫做 委托 - 特殊的对象将功能委托给通用的对象类型完成。这也许比将其称之为继承更为贴切,因为“被继承”了的功能并没有被拷贝到正在“进行继承”的对象中,相反它仍存在于通用的对象中。

+
+ +

在使用继承时,建议您不要使用过多层次的继承,并仔细追踪定义方法和属性的位置。很有可能您的代码会临时修改了浏览器内置对象的原型,但您不应该这么做,除非您有足够充分的理由。过多的继承会在调试代码时给您带来无尽的混乱和痛苦。

+ +

总之,对象是另一种形式的代码重用,就像函数和循环一样,有他们特定的角色和优点。如果您发现自己创建了一堆相关的变量和函数,还想一起追踪它们并将其灵活打包的话,对象是个不错的主意。对象在您打算把一个数据集合从一个地方传递到另一个地方的时候非常有用。这些都可以在不使用构造器和继承的情况下完成。如果您只是需要一个单一的对象实例,也许使用对象常量会好些,您当然不需要使用继承。

+ +

总结

+ +

这篇文章覆盖了剩余的 OOJS 理论的核心知识和我们认为您应该知道的语法,这个时候您应该理解了 JavaScript 中的对象和 OOP 基础,原型和原型继承机制,如何创建类(constructors)和对象实例,为类增加功能,通过从其他类继承而创建新的子类。

+ +

下一篇文章我们将学习如何运用 JavaScript Object Notation (JSON), 一种使用 JavaScript 对象写的数据传输格式。

+ +

参见

+ + + +

{{PreviousMenuNext("Learn/JavaScript/Objects/Object_prototypes", "Learn/JavaScript/Objects/JSON", "Learn/JavaScript/Objects")}}

diff --git a/files/zh-cn/learn/javascript/objects/inheritance/index.html b/files/zh-cn/learn/javascript/objects/inheritance/index.html deleted file mode 100644 index eca486d343..0000000000 --- a/files/zh-cn/learn/javascript/objects/inheritance/index.html +++ /dev/null @@ -1,434 +0,0 @@ ---- -title: JavaScript 中的继承 -slug: Learn/JavaScript/Objects/Inheritance -tags: - - JavaScript - - OOJS - - 原型 - - 对象 - - 继承 - - 面向对象JS -translation_of: Learn/JavaScript/Objects/Inheritance ---- -
{{LearnSidebar}}
- -
{{PreviousMenuNext("Learn/JavaScript/Objects/Object_prototypes", "Learn/JavaScript/Objects/JSON", "Learn/JavaScript/Objects")}}
- -

了解了 OOJS 的大多数细节之后,本文将介绍如何创建“子”对象类别(构造器)并从“父”类别中继承功能。此外,我们还会针对何时何处使用 OOJS 给出建议。

- - - - - - - - - - - - -
预备知识:基本的计算机素养,对 HTML 和 CSS 有基本的理解,熟悉 JavaScript 基础(参见 First stepsBuilding blocks)以及面向对象的JavaScript (OOJS) 基础(参见 Introduction to objects)。
目标:理解在 JavaScript 中如何实现继承。
- -

原型式的继承

- -

到目前为止我们已经了解了一些关于原型链的实现方式以及成员变量是如何通过它来实现继承,但是之前涉及到的大部分都是浏览器内置函数(比如 StringDateNumber 和 Array),那么我们如何创建一个继承自另一对象的JavaScript对象呢?

- -

正如前面课程所提到的,有些人认为JavaScript并不是真正的面向对象语言,在经典的面向对象语言中,您可能倾向于定义类对象,然后您可以简单地定义哪些类继承哪些类(参考C++ inheritance里的一些简单的例子),JavaScript使用了另一套实现方式,继承的对象函数并不是通过复制而来,而是通过原型链继承(通常被称为 原型式继承 —— prototypal inheritance

- -

让我们通过具体的例子来解释上述概念

- -

开始

- -

首先,将oojs-class-inheritance-start.html文件复制到您本地(也可以 在线运行 ),其中您能看到一个只定义了一些属性的Person()构造器,与之前通过模块来实现所有功能的Person的构造器类似。

- -
function Person(first, last, age, gender, interests) {
-  this.name = {
-    first,
-    last
-  };
-  this.age = age;
-  this.gender = gender;
-  this.interests = interests;
-};
-
- -

所有的方法都定义在构造器的原型上,比如:

- -
Person.prototype.greeting = function() {
-  alert('Hi! I\'m ' + this.name.first + '.');
-};
-
- -
-

注意:在源代码中,你可以看到已定义的bio()farewell()方法。随后,你将看到它们被其他的构造器所继承。

-
- -

比如我们想要创建一个Teacher类,就像我们前面在面向对象概念解释时用的那个一样。这个类会继承Person的所有成员,同时也包括:

- -
    -
  1. 一个新的属性,subject——这个属性包含了教师教授的学科。
  2. -
  3. 一个被更新的greeting()方法,这个方法打招呼听起来比一般的greeting()方法更正式一点——对于一个教授一些学生的老师来说。
  4. -
- -

定义 Teacher() 构造器函数

- -

我们要做的第一件事是创建一个Teacher()构造器——将下面的代码加入到现有代码之下:

- -
function Teacher(first, last, age, gender, interests, subject) {
-  Person.call(this, first, last, age, gender, interests);
-
-  this.subject = subject;
-}
-
- -

这在很多方面看起来都和Person的构造器很像,但是这里有一些我们从没见过的奇怪玩意——call()函数。基本上,这个函数允许您调用一个在这个文件里别处定义的函数。第一个参数指明了在您运行这个函数时想对“this”指定的值,也就是说,您可以重新指定您调用的函数里所有“this”指向的对象。其他的变量指明了所有目标函数运行时接受的参数。

- -
-

注:在这个例子里我们在创建一个新的对象实例时同时指派了继承的所有属性,但是注意您需要在构造器里将它们作为参数来指派,即使实例不要求它们被作为参数指派(比如也许您在创建对象的时候已经得到了一个设置为任意值的属性)

-
- -

所以在这个例子里,我们很有效的在Teacher()构造函数里运行了Person()构造函数(见上文),得到了和在Teacher()里定义的一样的属性,但是用的是传送给Teacher(),而不是Person()的值(我们简单使用这里的this作为传给call()this,意味着this指向Teacher()函数)。

- -

在构造器里的最后一行代码简单地定义了一个新的subject属性,这将是教师会有的,而一般人没有的属性。

- -

顺便提一下,我们本也可以这么做:

- -
function Teacher(first, last, age, gender, interests, subject) {
-  this.name = {
-    first,
-    last
-  };
-  this.age = age;
-  this.gender = gender;
-  this.interests = interests;
-  this.subject = subject;
-}
-
- -

但是这只是重新定义了一遍属性,并不是将他们从Person()中继承过来的,所以这违背了我们的初衷。这样写也会需要更长的代码。

- -

从无参构造函数继承

- -

请注意,如果您继承的构造函数不从传入的参数中获取其属性值,则不需要在call()中为其指定其他参数。所以,例如,如果您有一些相当简单的东西:

- -
function Brick() {
-  this.width = 10;
-  this.height = 20;
-}
- -

您可以这样继承widthheight属性(以及下面描述的其他步骤):

- -
function BlueGlassBrick() {
-  Brick.call(this);
-
-  this.opacity = 0.5;
-  this.color = 'blue';
-}
- -

请注意,我们仅传入了thiscall()中 - 不需要其他参数,因为我们不会继承通过参数设置的父级的任何属性。

- -

设置 Teacher() 的原型和构造器引用

- -

All is good so far, but we have a problem. We have defined a new constructor, and it has a prototype property, which by default just contains an object with a reference to the constructor function itself. It does not contain the methods of the Person constructor's prototype property. To see this, enter Object.getOwnPropertyNames(Teacher.prototype) into either the text input field or your JavaScript console. Then enter it again, replacing Teacher with Person. Nor does the new constructor inherit those methods. To see this, compare the outputs of Person.prototype.greeting and Teacher.prototype.greeting. We need to get Teacher() to inherit the methods defined on Person()'s prototype. So how do we do that?

- -
    -
  1. Add the following line below your previous addition: -
    Teacher.prototype = Object.create(Person.prototype);
    - Here our friend create() comes to the rescue again. In this case we are using it to create a new object and make it the value of Teacher.prototype. The new object has Person.prototype as its prototype and will therefore inherit, if and when needed, all the methods available on Person.prototype.
  2. -
  3. We need to do one more thing before we move on. After adding the last line, Teacher.prototype's constructor property is now equal to Person(), because we just set Teacher.prototype to reference an object that inherits its properties from Person.prototype! Try saving your code, loading the page in a browser, and entering Teacher.prototype.constructor into the console to verify.
  4. -
  5. This can become a problem, so we need to set this right. You can do so by going back to your source code and adding the following line at the bottom: -
    Object.defineProperty(Teacher.prototype, 'constructor', {
    -    value: Teacher,
    -    enumerable: false, // so that it does not appear in 'for in' loop
    -    writable: true });
    -
  6. -
  7. Now if you save and refresh, entering Teacher.prototype.constructor should return Teacher(), as desired, plus we are now inheriting from Person()!
  8. -
- -

向 Teacher() 添加一个新的greeting()函数

- -

为了完善代码,您还需在构造函数Teacher()上定义一个新的函数greeting()。最简单的方法是在Teacher的原型上定义它—把以下代码添加到您代码的底部:

- -
Teacher.prototype.greeting = function() {
-  var prefix;
-
-  if(this.gender === 'male' || this.gender === 'Male' || this.gender === 'm' || this.gender === 'M') {
-    prefix = 'Mr.';
-  } else if(this.gender === 'female' || this.gender === 'Female' || this.gender === 'f' || this.gender === 'F') {
-    prefix = 'Mrs.';
-  } else {
-    prefix = 'Mx.';
-  }
-
-  alert('Hello. My name is ' + prefix + ' ' + this.name.last + ', and I teach ' + this.subject + '.');
-};
- -

这样就会出现老师打招呼的弹窗,老师打招呼会使用条件结构判断性别从而使用正确的称呼。

- -

范例尝试

- -

现在我们来键入代码,将下面的代码放到您的 JavaScript 代码下面从而来创建一个 Teacher() 对象实例。

- -
var teacher1 = new Teacher('Dave', 'Griffiths', 31, 'male', ['football', 'cookery'], 'mathematics');
- -

当您保存代码并刷新的时候,试一下您的老师实例的属性和方法:

- -
teacher1.name.first;
-teacher1.interests[0];
-teacher1.bio();
-teacher1.subject;
-teacher1.greeting();
- -

前面三个进入到从Person()的构造器 继承的属性和方法,后面两个则是只有Teacher()的构造器才有的属性和方法。

- -
-

注:如果您在这里遇到了问题,请对比您的代码与我们的完成版本(或查看可运行的在线示例)。

-
- -

我们在这里讲述的技巧并不是 JavaScript 中创建继承类的唯一方式,但是这个技巧也还不错,非常好地告诉了您如何在 JavaScript 中实行继承操作。

- -

您可能对在 JavaScript中使用其他方法来实行继承会感兴趣(参见 Classes)。我们没有覆盖那些内容,因为并不是每种浏览器都会支持这些方法。我们在这一系列文章中介绍的所有其他方法都会被 IE9 支持或者更老的浏览器支持,也有一些方法可以支持更老的浏览器。

- -

一个常用的方法是使用 JavaScript 语言库——最热门的一些库提供一些方法让我们更快更好地实行继承。比如 CoffeeScript 就提供一些类和扩展。

- -

更多练习

- -

在我们的 OOP theory section 模块中, 我们也将学生类作为一个概念,继承了 Person 所有的属性和方法,也有一个不同的打招呼的方法(比老师的打招呼轻松随意一些)。您可以自己尝试一下如何实现。

- -
-

注:如果你编写时遇到困难,代码无法运行,那么可以查看我们的完成版本(也可查看 可运行的在线示例)。

-
- -

对象成员总结

- -

总结一下,你需要在实践中考虑到下面四类属性或方法

- -
    -
  1. 那些定义在构造器函数中,用于给予对象实例的属性或方法。它们都很容易发现——在您自己的代码中,它们是构造函数中使用this.x = x类型的行;在浏览器内已经构建好的代码中,它们是可用于对象实例的成员(这些对象实例通常使用new关键字调用构造函数来创建,例如var myInstance = new myConstructor())。
  2. -
  3. 那些直接在构造函数上定义,仅在构造函数上可用的属性或方法。它们通常仅在浏览器的内置对象中可用,并通过被直接链接到构造函数来识别,而不是实例。例如Object.keys()它们一般被称作静态属性或静态方法
  4. -
  5. 那些在构造函数原型上定义,由所有实例和对象类继承的属性或方法。它们包括在构造函数的原型属性上定义的任何成员,如myConstructor.prototype.x()
  6. -
  7. Those available on an object instance, which can either be an object created when a constructor is instantiated like we saw above (so for example let teacher1 = new Teacher( 'Chris' ); and then teacher1.name), or an object literal (let teacher1 = { name : 'Chris' } and then teacher1.name).
  8. -
- -

如果您现在觉得一团浆糊,别担心——您现在还处于学习阶段,不断练习才会慢慢熟悉这些知识。

- -

ECMAScript 2015 Classes

- -

ECMAScript 2015 introduces class syntax to JavaScript as a way to write reusable classes using easier, cleaner syntax, which is more similar to classes in C++ or Java. In this section we'll convert the Person and Teacher examples from prototypal inheritance to classes, to show you how it's done.

- -
-

Note: This modern way of writing classes is supported in all modern browsers, but it is still worth knowing about the underlying prototypal inheritance in case you work on a project that requires supporting a browser that doesn't support this syntax (most notably Internet Explorer).

-
- -

Let's look at a rewritten version of the Person example, class-style:

- -
class Person {
-  constructor(first, last, age, gender, interests) {
-    this.name = {
-      first,
-      last
-    };
-    this.age = age;
-    this.gender = gender;
-    this.interests = interests;
-  }
-
-  greeting() {
-    console.log(`Hi! I'm ${this.name.first}`);
-  };
-
-  farewell() {
-    console.log(`${this.name.first} has left the building. Bye for now!`);
-  };
-}
-
- -

The class statement indicates that we are creating a new class. Inside this block, we define all the features of the class:

- - - -

We can now instantiate object instances using the new operator, in just the same way as we did before:

- -
let han = new Person('Han', 'Solo', 25, 'male', ['Smuggling']);
-han.greeting();
-// Hi! I'm Han
-
-let leia = new Person('Leia', 'Organa', 19, 'female', ['Government']);
-leia.farewell();
-// Leia has left the building. Bye for now
-
- -
-

Note: Under the hood, your classes are being converted into Prototypal Inheritance models — this is just syntactic sugar. But I'm sure you'll agree that it's easier to write.

-
- -

Inheritance with class syntax

- -

Above we created a class to represent a person. They have a series of attributes that are common to all people; in this section we'll create our specialized Teacher class, making it inherit from Person using modern class syntax. This is called creating a subclass or subclassing.

- -

To create a subclass we use the extends keyword to tell JavaScript the class we want to base our class on,

- -
class Teacher extends Person {
-  constructor(subject, grade) {
-    this.subject = subject;
-    this.grade = grade;
-  }
-}
- -

but there's a little catch.

- -

Unlike old-school constructor functions where the new operator does the initialization of this to a newly-allocated object, this isn't automatically initialized for a class defined by the extends keyword, i.e the sub-classes.

- -

Therefore running the above code will give an error:

- -
Uncaught ReferenceError: Must call super constructor in derived class before
-accessing 'this' or returning from derived constructor
- -

For sub-classes, the this initialization to a newly allocated object is always dependant on the parent class constructor, i.e the constructor function of the class from which you're extending.

- -

Here we are extending the Person class — the Teacher sub-class is an extension of the Person class. So for Teacher, the this initialization is done by the Person constructor.

- -

To call the parent constructor we have to use the super() operator, like so:

- -
class Teacher extends Person {
-  constructor(subject, grade) {
-    super(); // Now 'this' is initialized by calling the parent constructor.
-    this.subject = subject;
-    this.grade = grade;
-  }
-}
- -

There is no point having a sub-class if it doesn't inherit properties from the parent class.
-It is good then, that the super() operator also accepts arguments for the parent constructor.

- -

Looking back to our Person constructor, we can see it has the following block of code in its constructor method:

- -
 constructor(first, last, age, gender, interests) {
-   this.name = {
-     first,
-     last
-   };
-   this.age = age;
-   this.gender = gender;
-   this.interests = interests;
-} 
- -

Since the super() operator is actually the parent class constructor, passing it the necessary arguments of the Parent class constructor will also initialize the parent class properties in our sub-class, thereby inheriting it:

- -
class Teacher extends Person {
-  constructor(first, last, age, gender, interests, subject, grade) {
-    super(first, last, age, gender, interests);
-
-    // subject and grade are specific to Teacher
-    this.subject = subject;
-    this.grade = grade;
-  }
-}
-
- -

Now when we instantiate Teacher object instances, we can call methods and properties defined on both Teacherand Person as we'd expect:

- -
let snape = new Teacher('Severus', 'Snape', 58, 'male', ['Potions'], 'Dark arts', 5);
-snape.greeting(); // Hi! I'm Severus.
-snape.farewell(); // Severus has left the building. Bye for now.
-snape.age // 58
-snape.subject; // Dark arts
-
- -

Like we did with Teachers, we could create other subclasses of Person to make them more specialized without modifying the base class.

- -
-

Note: You can find this example on GitHub as es2015-class-inheritance.html (see it live also).

-
- -

Getters and Setters

- -

There may be times when we want to change the values of an attribute in the classes we create or we don't know what the final value of an attribute will be. Using the Teacher example, we may not know what subject the teacher will teach before we create them, or their subject may change between terms.

- -

We can handle such situations with getters and setters.

- -

Let's enhance the Teacher class with getters and setters. The class starts the same as it was the last time we looked at it.

- -

Getters and setters work in pairs. A getter returns the current value of the variable and its corresponding setter changes the value of the variable to the one it defines.

- -

The modified Teacher class looks like this:

- -
class Teacher extends Person {
-  constructor(first, last, age, gender, interests, subject, grade) {
-    super(first, last, age, gender, interests);
-    // subject and grade are specific to Teacher
-    this._subject = subject;
-    this.grade = grade;
-  }
-
-  get subject() {
-    return this._subject;
-  }
-
-  set subject(newSubject) {
-    this._subject = newSubject;
-  }
-}
-
- -

In our class above we have a getter and setter for the subject property. We use _ to create a separate value in which to store our name property. Without using this convention, we would get errors every time we called get or set. At this point:

- - - -

The example below shows the two features in action:

- -
// Check the default value
-console.log(snape.subject) // Returns "Dark arts"
-
-// Change the value
-snape.subject = "Balloon animals" // Sets _subject to "Balloon animals"
-
-// Check it again and see if it matches the new value
-console.log(snape.subject) // Returns "Balloon animals"
-
- -
-

Note: You can find this example on GitHub as es2015-getters-setters.html (see it live also).

-
- -
-

Note: Getters and setters can be very useful at times, for example when you want to run some code every time a property is requested or set. For simple cases, however, plain property access without a getter or setter will do just fine.

-
- -

何时在 JavaScript 中使用继承?

- -

特别是在读完这段文章内容之后,您也许会想 "天啊,这实在是太复杂了". 是的,您是对的,原型和继承代表了JavaScript这门语言里最复杂的一些方面,但是JavaScript的强大和灵活性正是来自于它的对象体系和继承方式,这很值得花时间去好好理解下它是如何工作的。

- -

在某种程度上来说,您一直都在使用继承 - 无论您是使用WebAPI的不同特性还是调用字符串、数组等浏览器内置对象的方法和属性的时候,您都在隐式地使用继承。

- -

就在自己代码中使用继承而言,您可能不会使用的非常频繁,特别是在小型项目中或者刚开始学习时 - 因为当您不需要对象和继承的时候,仅仅为了使用而使用它们只是在浪费时间而已。但是随着您的代码量的增大,您会越来越发现它的必要性。如果您开始创建一系列拥有相似特性的对象时,那么创建一个包含所有共有功能的通用对象,然后在更特殊的对象类型中继承这些特性,将会变得更加方便有用。

- -
-

注: 考虑到JavaScript的工作方式,由于原型链等特性的存在,在不同对象之间功能的共享通常被叫做 委托 - 特殊的对象将功能委托给通用的对象类型完成。这也许比将其称之为继承更为贴切,因为“被继承”了的功能并没有被拷贝到正在“进行继承”的对象中,相反它仍存在于通用的对象中。

-
- -

在使用继承时,建议您不要使用过多层次的继承,并仔细追踪定义方法和属性的位置。很有可能您的代码会临时修改了浏览器内置对象的原型,但您不应该这么做,除非您有足够充分的理由。过多的继承会在调试代码时给您带来无尽的混乱和痛苦。

- -

总之,对象是另一种形式的代码重用,就像函数和循环一样,有他们特定的角色和优点。如果您发现自己创建了一堆相关的变量和函数,还想一起追踪它们并将其灵活打包的话,对象是个不错的主意。对象在您打算把一个数据集合从一个地方传递到另一个地方的时候非常有用。这些都可以在不使用构造器和继承的情况下完成。如果您只是需要一个单一的对象实例,也许使用对象常量会好些,您当然不需要使用继承。

- -

总结

- -

这篇文章覆盖了剩余的 OOJS 理论的核心知识和我们认为您应该知道的语法,这个时候您应该理解了 JavaScript 中的对象和 OOP 基础,原型和原型继承机制,如何创建类(constructors)和对象实例,为类增加功能,通过从其他类继承而创建新的子类。

- -

下一篇文章我们将学习如何运用 JavaScript Object Notation (JSON), 一种使用 JavaScript 对象写的数据传输格式。

- -

参见

- - - -

{{PreviousMenuNext("Learn/JavaScript/Objects/Object_prototypes", "Learn/JavaScript/Objects/JSON", "Learn/JavaScript/Objects")}}

diff --git a/files/zh-cn/learn/javascript/objects/object-oriented_js/index.html b/files/zh-cn/learn/javascript/objects/object-oriented_js/index.html deleted file mode 100644 index ddd1f3d2d5..0000000000 --- a/files/zh-cn/learn/javascript/objects/object-oriented_js/index.html +++ /dev/null @@ -1,267 +0,0 @@ ---- -title: 适合初学者的JavaScript面向对象 -slug: Learn/JavaScript/Objects/Object-oriented_JS -translation_of: Learn/JavaScript/Objects/Object-oriented_JS ---- -
{{LearnSidebar}}
- -
{{PreviousMenuNext("Learn/JavaScript/Objects/Basics", "Learn/JavaScript/Objects/Object_prototypes", "Learn/JavaScript/Objects")}}
- -

学完基础后, 现在我们集中于面向对象的 JavaScript (OOJS) — 本文首先提出了面向对象编程(OOP) 理论的基本观点,然后探索如何通过构造函数模拟对象类,以及如何创建对象.

- - - - - - - - - - - - -
预备知识:计算机基础知识, 了解 HTML 和 CSS, 熟悉 JavaScrpit 基础知识 (查看 First stepsBuilding blocks) 和 OOJS 基础 (查看 Introduction to objects).
目标:掌握面向对象程序的基本理论, 这涉及到 JavaScript  的("万物皆对象"), 以及如何创建构造器和对象.
- -

从零开始面向对象的程序设计

- -

首先,我们从高维度且简化的角度看看 面向对象的程序(Object-oriented programming ,OOP)是什么。我们将简单描述OOP,因为OOP该概念已变得很复杂,如果完整地描述OOP将使读者难以理解。OOP 的基本思想是:在程序里,我们通过使用对象去构建现实世界的模型,把原本很难(或不可能)被使用的功能,简单化并提供出来,以供访问。

- -

对象可以包含相关的数据和代码,这些数据和代码用于表示 你所建造的模型是什么样子,以及拥有什么样的行为或功能。对象包(object package,或者叫命名空间 namespace)存储(官方用语:封装)着对象的数据(常常还包括函数),使数据的组织和访问变得更容易了;对象也常用作 数据存储体(data stores),用于在网络上运输数据,十分便捷。

- -

定义一个对象模板

- -

让我们来考虑一个简单的程序,它可以显示一个学校的学生和老师的信息.在这里我们不讨论任何程序语言,我们只讨论 OOP 思想.

- -

首先,我们可以回到上一节拿到定义好属性和方法的Person对象。对于一个人(person)来说,我们能在他们身上获取到很多信息(他们的住址,身高,鞋码,基因图谱,护照信息,显著的性格特征等等),然而,我们仅仅需要他们的名字,年龄,性别,兴趣 这些信息,然后,我们会基于他们的这些信息写一个简短的介绍关于他们自己,在最后我们还需要教会他们打招呼。以上的方式被称为抽象-为了我们编程的目标而利用事物的一些重要特性去把复杂的事物简单化

- -

- -

在一些面向对象的语言中,我们用类(class)的概念去描述一个对象(您在下面就能看到JavaScript使用了一个完全不同的术语)-类并不完全是一个对象,它更像是一个定义对象特质的模板。

- -

创造一个真正的对象

- -

从上面我们创建的class中, 我们能够基于它创建出一些对象 - 一些拥有class中属性及方法的对象。基于我们的Person类,我们可以创建出许许多多的真实的人:

- -

- -

当一个对象需要从类中创建出来时,类的构造函数就会运行来创建这个实例。这种创建对象的过程我们称之为实例化-实例对象被类实例化。

- -

具体的对象

- -

在这个例子里,我们不想要泛指的人,我们想要像老师和学生这样类型更为具体的人。在 OOP 里,我们可以创建基于其它类的新类,这些新的子类可以继承它们父类的数据和功能。比起复制来说这样能够使用父对象共有的功能。如果类之间的功能不同,你可以根据需要定义专用的特征。

- -

- -

这是非常有用的,老师和学生具有一些相同的特征比如姓名、性别、年龄,因此只需要定义这些特征一次就可以了。您可以在不同的类里分开定义这些相同的特征,这样该特征会有一个不同的命名空间。比如,一个学生的 greeting 可以是 "Yo, I'm [firstName]" (例子 Yo, I'm Sam),老师的可能会正式一些,比如"Hello, my name is [Prefix] [lastName]" (例子 Hello, My name is Mr Griffiths)。

- -
-

注:多态——这个高大上的词正是用来描述多个对象拥有实现共同方法的能力。

-
- -

现在可以根据子类创建对象。如:

- -

下面我们来看看 OOP 理论如何应用到 JavaScript 实践中去的。

- -

构建函数和对象

- -

有些人认为 JavaScript 不是真正的面向对象的语言,比如它没有像许多面向对象的语言一样有用于创建class类的声明。JavaScript 用一种称为构建函数的特殊函数来定义对象和它们的特征。构建函数非常有用,因为很多情况下您不知道实际需要多少个对象(实例)。构建函数提供了创建您所需对象(实例)的有效方法,将对象的数据和特征函数按需联结至相应对象。

- -

不像“经典”的面向对象的语言,从构建函数创建的新实例的特征并非全盘复制,而是通过一个叫做原形链的参考链链接过去的。(参见 Object prototypes),所以这并非真正的实例,严格的讲, JavaScript 在对象间使用和其它语言的共享机制不同。

- -
-

注: “经典”的面向对象的语言并非好事,就像上面提到的,OOP 可能很快就变得非常复杂,JavaScript 找到了在不变的特别复杂的情况下利用面向对象的优点的方法。

-
- -

让我们来看看 JavaScript 如何通过构建函数对象来创建类。首先,请先复制一个新的前文提到的oojs.html

- -

一个简单的例子

- -
    -
  1. 让我们看看如何通过一个普通的函数定义一个”人“。在您的文件中添加以下代码: -
    function createNewPerson(name) {
    -  var obj = {};
    -  obj.name = name;
    -  obj.greeting = function () {
    -    alert('Hi! I\'m ' + this.name + '.');
    -  }
    -  return obj;
    -}
    -
  2. -
  3. 您现在可以通过调用这个函数创建一个新的叫 salva 的人,在您浏览器的JavaScript console 试试 : -
    var salva = createNewPerson('salva');
    -salva.name;
    -salva.greeting();
    - 上述代码运行良好,但是有点冗长;如果我们知道如何创建一个对象,就没有必要创建一个新的空对象并且返回它。幸好 JavaScript 通过构建函数提供了一个便捷的方法,方法如下:
  4. -
  5. 将之前的代码用如下代码代替: -
    function Person(name) {
    -  this.name = name;
    -  this.greeting = function() {
    -    alert('Hi! I\'m ' + this.name + '.');
    -  };
    -}
    -
  6. -
- -

这个构建函数是 JavaScript 版本的类。您会发现,它只定义了对象的属性和方法,除了没有明确创建一个对象和返回任何值和之外,它有了您期待的函数所拥有的全部功能。这里使用了this关键词,即无论是该对象的哪个实例被这个构建函数创建,它的 name 属性就是传递到构建函数形参name的值,它的 greeting() 方法中也将使用相同的传递到构建函数形参name的值。

- -
-

注: 一个构建函数通常是大写字母开头,这样便于区分构建函数和普通函数。

-
- -

那如何调用构建函数创建新的实例呢?

- -
    -
  1. 将下面的代码加在您之前的代码下面: -
    var person1 = new Person('Bob');
    -var person2 = new Person('Sarah');
    -
  2. -
  3. 保存并刷新浏览器,在 console 里输入如下代码: -
    person1.name
    -person1.greeting()
    -person2.name
    -person2.greeting()
    -
  4. -
- -

酷!您现在看到页面上有两个对象,每一个保存在不同的命名空间里,当您访问它们的属性和方法时,您需要使用person1或者person2来调用它们。尽管它们有着相同的name属性和 greeting()方法它们是各自独立的,所以相互的功能不会冲突。注意它们使用的是自己的 name 值,这也是使用 this 关键字的原因,它们使用的从实参传入形参的自己的值,而不是其它的什么值。

- -

再看看这个构造对象的语法:

- -
var person1 = new Person('Bob');
-var person2 = new Person('Sarah');
- -

上述代码中,关键字 new 跟着一个含参函数,用于告知浏览器我们想要创建一个对象,非常类似函数调用,并把结果保存到变量中。每个示例类都是根据下面的方式定义的。

- -
function Person(name) {
-  this.name = name;
-  this.greeting = function() {
-    alert('Hi! I\'m ' + this.name + '.');
-  };
-}
-
- -

当新的对象被创立, 变量person1person2有效地包含了以下值:

- -
{
-  name : 'Bob',
-  greeting : function() {
-    alert('Hi! I\'m ' + this.name + '.');
-  }
-}
-
-{
-  name : 'Sarah',
-  greeting : function() {
-    alert('Hi! I\'m ' + this.name + '.');
-  }
-}
- -

值得注意的是每次当我们调用构造函数时,我们都会重新定义一遍 greeting(),这不是个理想的方法。为了避免这样,我们可以在原型里定义函数,接下来我们会讲到。

- -

创建我们最终的构造函数

- -

上面的例子仅仅是简单地介绍如何开始。让我们现在开始创建Person()构造函数。

- -
    -
  1. 移除掉您之前写的所有代码, 用如下构造函数替代 —— 实现原理上,这与我们之前的例子并无二致, 只是变得稍稍复杂了些: -
    function Person(first, last, age, gender, interests) {
    -  this.name = {
    -    'first': first,
    -    'last': last
    -  };
    -  this.age = age;
    -  this.gender = gender;
    -  this.interests = interests;
    -  this.bio = function() {
    -    alert(this.name.first + ' ' + this.name.last + ' is ' + this.age + ' years old. He likes ' + this.interests[0] + ' and ' + this.interests[1] + '.');
    -  };
    -  this.greeting = function() {
    -    alert('Hi! I\'m ' + this.name.first + '.');
    -  };
    -};
    -
  2. -
  3. 接下来加上这样一行代码, 用来创建它的一个对象: -
    var person1 = new Person('Bob', 'Smith', 32, 'male', ['music', 'skiing']);
    -
  4. -
- -

这样,您就可以像我们定义第一个对象一样访问它的属性和方法了:

- -
person1['age']
-person1.interests[1]
-person1.bio()
-// etc.
- -
-

注: 如果您对这一部分有疑问, 尝试将您的代码与我们的版本做比较 —— 戳链接: oojs-class-finished.html (或者: 查看它的实现).

-
- -

进一步的练习

- -

首先, 尝试着写几行代码创建您自己的对象, 接着,尝试getting与setting对象中的成员。

- -

此外, 我们的bio()方法里仍有一些问题 —— 尽管您创建的Person是女性,或者是些别的性别类型,输出里的代词都总是 "He"。 而且, 纵然您有更多的兴趣列举在interests数组中, bio只会展示您的两个兴趣。 您能想出如何在类型定义(构造函数)中解决这个问题吗? 您可以按照您喜欢的方式编写构造函数(您可能需要一些条件判断和循环)。 考虑下语句如何根据性别、兴趣列表中兴趣的数目异构。

- -
-

注:如果您觉得困难, 我们在我们的GitHub仓库里作了回答(查看它的实现) ——但首先请您尝试着自己写出来。

-
- -

创建对象的其他方式

- -

到现在为止,我们了解到了两种不同的创建对象的方式 —— 声明一个对象的语法, 与使用构造函数(回顾上面)。

- -

这些方法都是很有用的, 但仍有其他的方法 —— 我们希望您能熟悉这些,以免您在Web世界的旅行中碰到它们。

- -

Object()构造函数

- -

首先, 您能使用Object()构造函数来创建一个新对象。 是的, 一般对象都有构造函数,它创建了一个空的对象。

- -
    -
  1. 尝试在您浏览器中的Javascript控制台中输入以下代码: -
    var person1 = new Object();
    -
  2. -
  3. 这样就在person1变量中存储了一个空对象。然后, 可以根据需要, 使用点或括号表示法向此对象添加属性和方法;试试这个例子: -
    person1.name = 'Chris';
    -person1['age'] = 38;
    -person1.greeting = function() {
    -  alert('Hi! I\'m ' + this.name + '.');
    -}
    -
  4. -
  5. 还可以将对象文本传递给Object() 构造函数作为参数, 以便用属性/方法填充它。请尝试以下操作: -
    var person1 = new Object({
    -  name : 'Chris',
    -  age : 38,
    -  greeting : function() {
    -    alert('Hi! I\'m ' + this.name + '.');
    -  }
    -});
    -
  6. -
- -

使用create()方法

- -

JavaScript有个内嵌的方法create(), 它允许您基于现有对象创建新的对象。

- -
    -
  1. 在 JavaScript 控制台中尝试此操作: -
    var person2 = Object.create(person1);
    -
  2. -
  3. 现在尝试这个: -
    person2.name
    -person2.greeting()
    -
  4. -
- -

您可以看到,person2是基于person1创建的, 它们具有相同的属性和方法。这非常有用, 因为它允许您创建新的对象而无需定义构造函数。缺点是比起构造函数,浏览器在更晚的时候才支持create()方法(IE9,  IE8 或甚至以前相比), 加上一些人认为构造函数让您的代码看上去更整洁 —— 您可以在一个地方创建您的构造函数, 然后根据需要创建实例, 这让您能很清楚地知道它们来自哪里。

- -

但是, 如果您不太担心对旧浏览器的支持, 并且您只需要一个对象的一些副本, 那么创建一个构造函数可能会让您的代码显得过度繁杂。这取决于您的个人爱好。有些人发现create() 更容易理解和使用。

- -

稍后我们将更详细地探讨create() 的效果。

- -

总结

- -

这篇文章简单地介绍了一些面向对象原理 —— 这些描述还不够完整, 但它让您知道我们在这里处理什么。此外, 我们已经开始研究 javascript与 "经典 OOP"的关联与区别, 如何使用构造函数实现 javascript 中的类, 以及生成对象的不同方法。

- -

在下一篇文章中, 我们将探讨 JavaScript 对象原型。

- -

{{PreviousMenuNext("Learn/JavaScript/Objects/Basics", "Learn/JavaScript/Objects/Object_prototypes", "Learn/JavaScript/Objects")}}

diff --git a/files/zh-tw/_redirects.txt b/files/zh-tw/_redirects.txt index 69b77abb4b..d962490e46 100644 --- a/files/zh-tw/_redirects.txt +++ b/files/zh-tw/_redirects.txt @@ -440,6 +440,8 @@ /zh-TW/docs/Learn/JavaScript/First_steps/有用的字符串方法 /zh-TW/docs/Learn/JavaScript/First_steps/Useful_string_methods /zh-TW/docs/Learn/JavaScript/First_steps/變數 /zh-TW/docs/Learn/JavaScript/First_steps/Variables /zh-TW/docs/Learn/JavaScript/First_steps/陣列 /zh-TW/docs/Learn/JavaScript/First_steps/Arrays +/zh-TW/docs/Learn/JavaScript/Objects/Inheritance /zh-TW/docs/Learn/JavaScript/Objects/Classes_in_JavaScript +/zh-TW/docs/Learn/JavaScript/Objects/Object-oriented_JS /zh-TW/docs/conflicting/Learn/JavaScript/Objects/Classes_in_JavaScript /zh-TW/docs/Learn/JavaScript/如何運用 /zh-TW/docs/Learn/JavaScript/Howto /zh-TW/docs/Learn/JavaScript/第一步 /zh-TW/docs/Learn/JavaScript/First_steps /zh-TW/docs/Learn/Performance/多媒體 /zh-TW/docs/Learn/Performance/Multimedia diff --git a/files/zh-tw/_wikihistory.json b/files/zh-tw/_wikihistory.json index 3b23303592..d0f838a37b 100644 --- a/files/zh-tw/_wikihistory.json +++ b/files/zh-tw/_wikihistory.json @@ -1668,7 +1668,7 @@ "MashKao" ] }, - "Learn/JavaScript/Objects/Inheritance": { + "Learn/JavaScript/Objects/Classes_in_JavaScript": { "modified": "2020-07-16T22:32:17.083Z", "contributors": [ "tangerine1202", @@ -1684,17 +1684,6 @@ "MashKao" ] }, - "Learn/JavaScript/Objects/Object-oriented_JS": { - "modified": "2020-07-16T22:32:09.821Z", - "contributors": [ - "DahisC", - "tangerine1202", - "iigmir", - "cjchng", - "roycrxtw", - "MashKao" - ] - }, "Learn/JavaScript/Objects/Object_building_practice": { "modified": "2020-07-16T22:32:33.523Z", "contributors": [ @@ -7979,6 +7968,17 @@ "sailplaneTW" ] }, + "conflicting/Learn/JavaScript/Objects/Classes_in_JavaScript": { + "modified": "2020-07-16T22:32:09.821Z", + "contributors": [ + "DahisC", + "tangerine1202", + "iigmir", + "cjchng", + "roycrxtw", + "MashKao" + ] + }, "conflicting/Learn/Server-side/Django": { "modified": "2019-03-23T23:33:19.493Z", "contributors": [ diff --git a/files/zh-tw/conflicting/learn/javascript/objects/classes_in_javascript/index.html b/files/zh-tw/conflicting/learn/javascript/objects/classes_in_javascript/index.html new file mode 100644 index 0000000000..3b5bb0b688 --- /dev/null +++ b/files/zh-tw/conflicting/learn/javascript/objects/classes_in_javascript/index.html @@ -0,0 +1,278 @@ +--- +title: 初學者應知道的物件導向 JavaScript +slug: conflicting/Learn/JavaScript/Objects/Classes_in_JavaScript +translation_of: Learn/JavaScript/Objects/Object-oriented_JS +original_slug: Learn/JavaScript/Objects/Object-oriented_JS +--- +
{{LearnSidebar}}
+ +
{{PreviousMenuNext("Learn/JavaScript/Objects/Basics", "Learn/JavaScript/Objects/Object_prototypes", "Learn/JavaScript/Objects")}}
+ +

在看完了基本概念之後,接著要說明物件導向 JavaScript (OOJS)。本文將概述物件導向程式設計 (OOP) 的理論,另說明 JavaScript 是如何透過建構子函式來模擬物件類別,又是如何建立物件實體 (Instance)。

+ + + + + + + + + + + + +
必備條件:基本的電腦素養、已初步了解 HTML 與 CSS、熟悉 JavaScript (參閱〈First steps〉與〈Building blocks〉以及 OOJS 基礎概念 (參閱〈物件基礎概念〉。
主旨:了解物件導向程式設計的基本理論、其與 JavaScript (幾乎所有東西都是物件) 之間的關係、應如何寫出建構子與物件實體。
+ +

基本物件導向程式設計

+ +

最先,讓我們從簡單、高層次的視角來看物件導向程式設計 (Object-oriented programming;OOP) 最基本的概念。我們說簡單是因為 OOP 很容易變得複雜,就算現在就設法完整解釋,也可能讓大家越來越混亂。OOP 基本概念是:採用物件(objects)來模塑真實的實物世界:也就是在程式中的呈現是透過 objects 來塑造其模型,且\或提供簡單方式存取其「難以或不可能採用的功能」。

+ +

物件可裝載相關的資料與程式碼,資料部分是你塑造某個模型的資訊,而程式碼部分則用是操作行為(Method)實現。Object data  -- 函式部分通常也使用 ---可工整地儲存 (正式一點應該是封裝 Encapsulated) 在物件包裹(這個包裹有特定的稱呼方式,有時候即所謂的命名空間 Namespace) ,使其能輕鬆地建構性存取。物件也常作為「資料儲存 (Datastore),促成簡易實現跨網傳送。

+ +

定義物件範本

+ +

我們先找個簡單程式,如同學校裡用來顯示師生資訊的程式。本文只是要了解一般的 OOP 理論,並非以特定程式語言為出發點。

+ +

我們先拿第一篇物件文章中的「Person」物件類型為範例,其中定義了一個人的一般資料與功能。其實有很多資訊可助你了解一個人 (像是住址、身高、鞋子尺寸、DNA、護照號碼、明顯的人格特質等......),但我們這裡只列出了姓名、年齡、性別、興趣。我們另希望根據這些資料寫出簡介,初步了解這個人。上述這些即所謂的「抽象 (Abstraction)」。為某個複雜東西建立簡單的模型,藉以代表其最重要的概念或特質,且該模型建立方式極易於搭配我們的程式設計用途。

+ +

+ +

建立實際物件

+ +

我們可從這個「類別」建立物件實體 (Object instance) — 即該物件包含了類別中所定義的資料與功能。而「Person」類別可設定出幾個實際的人物:

+ +

+ +

在根據類別建立物件實體時,就是執行類別的「建構子 (Constructor) 函式」所建立。而這個「根據類別來建立物件實體」的過程即稱為「實體化 (Instantiation)」。物件實體就是從類別實體化而來。

+ +

特殊類別

+ +

如果我們不要一般人物,而想建立老師與學生這類比較特定類型的人物。在 OOP 中,我們可根據其他類別建立新的類別。新的子類別則可繼承 (Inherit) 母類別的資料與程式碼特性。你可重複使用所有物件類型共有的功能,而不需再複製之。若功能需與類別有所差異,則可直接於其上定義特殊功能。

+ +

+ +

這樣很方便。因為老師與學生也同樣使用了如姓名、性別、年齡等的共通特性,所以只要定義這些特性 1 次即可。你也可以分別在不同的類別中定義相同特性,各個特性的定義就置於不同的命名空間中。舉例來說,學生的打招呼方式可以是「Yo, I'm [firstName]」;老師的招呼方式就正式一點,如「Hello, my name is [Prefix] [lastName]」。

+ +
+

注意:所謂的「多型 (Polymorphism)」,即是用多個物件類別建置相同功能。

+
+ +

現在你可根據子類別來建立物件實例了。例如:

+ +

+ +

本文後面將講解應如何將 OOP 理論實際用於 JavaScript 中。

+ +

建構子與物件實例

+ +

JavaScript 使用稱為建構子函式(constructor function)的特殊函式,定義物件與功能。開發者常常會不知道到底需建立多少物件,這時建構子可讓你高效率建立所需物件,並依需要為其添加資料與函式。

+ +

在新的物件實例透過建構子函式產生後,其核心將透過一種稱為原型鏈(Prototype chain,由原型定義,可參閱 Object prototypes)的參照鏈連在一起。

+ +

接著就在 JS 中,透過建構子建立類別及其物件實例。首先請你先在本端磁碟中再另外複製一份前面文章提過的 oojs.html 檔案。

+ +

簡易範例

+ +
    +
  1. 先看看該如何用一般函式定義一個人。將下列函式加到 script 元素裡面: + +
    function createNewPerson(name) {
    +  var obj = {};
    +  obj.name = name;
    +  obj.greeting = function () {
    +    alert('Hi! I\'m ' + this.name + '.');
    +  }
    +  return obj;
    +}
    +
  2. +
  3. 呼叫此函式之後即可建立新的 1 個人,另在瀏覽器的 JavaScript 主控台中測試下列程式碼: +
    var salva = createNewPerson('salva');
    +salva.name;
    +salva.greeting();
    + 目前為止沒什麼問題,但有點囉嗦。如果早知道要建立物件的話,又何必要建立新的空白物件再回傳呢?幸好 JavaScript 透過建構子函式提供了方便的捷徑。現在就來試試看!
  4. +
  5. 用下列程式碼替代之前的函式: +
    function Person(name) {
    +  this.name = name;
    +  this.greeting = function() {
    +    alert('Hi! I\'m ' + this.name + '.');
    +  };
    +}
    +
  6. +
+ +

建構子也就是 JavaScript 版本的「類別」之一。你可以注意到,除了無法回傳任何數值或明確建立物件之外,建構子其實已具備函式中的所有功能,並已基本定義了屬性與函式 (Method)。你也能看到這裡同樣用了「this」關鍵字,即不論何時建立了這裡的任一物件實例,物件的「name」屬性均同等於「傳送至建構子呼叫的名稱值」;且 greeting() 函式也會使用相同「傳送至建構子呼叫的名稱值」。

+ +
+

注意:建構子函式名稱往往以大寫字母起頭,如此可方便你在程式碼中找出建構子函式。

+
+ +

我們又該如何呼叫建構子以建立物件呢?

+ +
    +
  1. 將下列程式碼加到目前的程式碼之下: +
    var person1 = new Person('Bob');
    +var person2 = new Person('Sarah');
    +
  2. +
  3. 儲存程式碼並在瀏覽器中重新載入,試著將下列程式碼輸入到文字輸入畫面中: +
    person1.name
    +person1.greeting()
    +person2.name
    +person2.greeting()
    +
  4. +
+ +

現在你應該能在頁面上看到兩組新物件,且各自以不同的命名空間儲存。若要存取其屬性與函式,就要以 person1person2 開始呼叫。這些物件均完整封包,不致與其他功能衝突;但仍具備相同的 name 屬性與 greeting() 函式。另請注意,物件均使用當初建立時所各自指派的 name 值;這也是「this」如此重要的原因之一,以確保物件可使用自己的值而不致混淆其他數值。

+ +

再看一次建構子呼叫:

+ +
var person1 = new Person('Bob');
+var person2 = new Person('Sarah');
+ +

這裡用了「new」關鍵字告知瀏覽器「我們要建立新的物件實例」,並接著在函式名稱之後的括號內傳入函式所需要的參數,並將結果儲存於變數之中 — 相當類似普通函式被呼叫的方式 。各個實例均根據此定義所建立:

+ +
function Person(name) {
+  this.name = name;
+  this.greeting = function() {
+    alert('Hi! I\'m ' + this.name + '.');
+  };
+}
+ +

在建立新的物件之後,person1person2 的變數將有效率地納入下列物件:

+ +
{
+  name : 'Bob',
+  greeting : function() {
+    alert('Hi! I\'m ' + this.name + '.');
+  }
+}
+
+{
+  name : 'Sarah',
+  greeting : function() {
+    alert('Hi! I\'m ' + this.name + '.');
+  }
+}
+ +

剛剛說的「有效率」,是因為實際功能仍定義於類別中,而非物件實例之中。這情況與我們稍早談過的物件實字 (Object literal) 相反。

+ +

建立完整的建構子

+ +

上面不過是入門的簡單範例。接著繼續建立最後的 Person() 建構子。

+ +
    +
  1. 將截至目前為止的程式碼移除,加入下列取代用的建構子。原則上與簡易範例完全一樣,只是比較複雜一點: +
    function Person(first, last, age, gender, interests) {
    +  this.name = {
    +    first,
    +    last
    +  };
    +  this.age = age;
    +  this.gender = gender;
    +  this.interests = interests;
    +  this.bio = function() {
    +    alert(this.name.first + ' ' + this.name.last + ' is ' + this.age + ' years old. He likes ' + this.interests[0] + ' and ' + this.interests[1] + '.');
    +  };
    +  this.greeting = function() {
    +    alert('Hi! I\'m ' + this.name.first + '.');
    +  };
    +};
    +
  2. +
  3. 再接著加入下列程式碼,就可建立物件實例: +
    var person1 = new Person('Bob', 'Smith', 32, 'male', ['music', 'skiing']);
    +
  4. +
+ +

現在可存取我們為第一個物件所定義的屬性與函式:

+ +
person1['age']
+person1.interests[1]
+person1.bio()
+// etc.
+ +
+

注意:如果你到這裡有點吃力,請先比較自己與我們的程式碼。可參閱 oojs-class-finished.html (也可看實際執行的情況)。

+
+ +

進階習題

+ +

在開始之前,先試著自己添加更多物件建立程式碼,再針對產生的物件實例取得並設定其成員。

+ +

此外,原來的 bio() 函式其實有點問題。即使你的「人」是女性,其輸出一定會有「He」這個代名詞。而且即使 interests 陣列中列出超過 2 個以上的「興趣」,這個 bio 函式也只會有 2 個興趣。你能試著在類別定義 (建構子) 中修正這個問題嗎?你可在建構子中放入任何你喜歡的程式碼 (但可能會需要幾個 conditionals 搭配 1 個迴圈)。想想應如何根據性別以及列出的興趣 (1 或 2 個以上),建構出不同的程式碼。

+ +
+

注意:如果你卡在這裡,我們也在 GitHub repo 上提供了解答 (立刻觀看)。先試著自己解決問題吧!

+
+ +

建立物件實例的其他方法

+ +

目前解釋了 2 種建立物件實例的方法 — 宣告物件實字,以及使用建構子函式。

+ +

當然還有別的方法,但我們希望你先熟悉此 2 種方法,以免你日後的 Web 旅程上會再遇到。

+ +

Object() 建構子

+ +

首先可使用 Object() 建構子建立新的物件。沒錯,即使是泛型物件 (Generic object) 也具備建構子,可用以產生空白物件。

+ +
    +
  1. 將下列輸入瀏覽器的 JavaScript 主控台內: +
    var person1 = new Object();
    +
  2. +
  3. 如此會在 person1 變數中儲存 1 組空白物件。接著可透過點 (dot-) 或括弧記法 (bracket notation) 為此物件新增屬性與函式。如下列範例: +
    person1.name = 'Chris';
    +person1['age'] = 38;
    +person1.greeting = function() {
    +  alert('Hi! I\'m ' + this.name + '.');
    +}
    +
  4. +
  5. 你可將一組物件實字傳送給 Object() 建構子作為參數,藉以預先填入屬性\函式。如下所示: +
    var person1 = new Object({
    +  name : 'Chris',
    +  age : 38,
    +  greeting : function() {
    +    alert('Hi! I\'m ' + this.name + '.');
    +  }
    +});
    +
  6. +
+ +

使用 create() 函式

+ +

建構子可以幫助你保持程式的可讀性 — 你可以將建構子建立在同一個地方,並根據需求從這些建構子中建立物件實例,這樣做可以讓你清楚地得知它們的來源。

+ +

不過,有些人偏好建立物件實例,而不先做建構子,尤其是他們的物件不會用很多實例時。JavaScript 有個稱作 create() 的內建函式能讓你這麼做。有了它,你就能根據現有物件,建立新的物件。

+ +
    +
  1. 在 JavaScript 主控台裡測試: +
    var person2 = Object.create(person1);
    +
  2. +
  3. 再測試以下: +
    person2.name
    +person2.greeting()
    +
  4. +
+ +

你會看到 person2 是根據 person1 所建立:它具備了相同的屬性以及可用的函式。

+ +

create() 的其中一個限制,就是 IE8 並不支援。因此,如果你需要支援舊版瀏覽器,建構子會比較有用。

+ +

我們後續會再說明 create() 的效果。

+ +

總結

+ +

本文簡略說明了 OO 理論,雖然還沒全部講完,至少也讓你初步了解到本文所要闡述的重點。此外,我們已經開始說明 JavaScript 與 OO 之間的關係、其與「傳統 OO」之間的差異、使用建構子於 JavaScript 中實作類別的方法,以及其他產生物件實例的方式。

+ +

下一篇文章將說明 JavaScript 物件原型。

+ +

{{PreviousMenuNext("Learn/JavaScript/Objects/Basics", "Learn/JavaScript/Objects/Object_prototypes", "Learn/JavaScript/Objects")}}

+ +

In this module

+ + diff --git a/files/zh-tw/learn/javascript/objects/classes_in_javascript/index.html b/files/zh-tw/learn/javascript/objects/classes_in_javascript/index.html new file mode 100644 index 0000000000..fdbd246ba2 --- /dev/null +++ b/files/zh-tw/learn/javascript/objects/classes_in_javascript/index.html @@ -0,0 +1,211 @@ +--- +title: JavaScript 中的「繼承」 +slug: Learn/JavaScript/Objects/Classes_in_JavaScript +translation_of: Learn/JavaScript/Objects/Inheritance +original_slug: Learn/JavaScript/Objects/Inheritance +--- +
{{LearnSidebar}}
+ +
{{PreviousMenuNext("Learn/JavaScript/Objects/Object_prototypes", "Learn/JavaScript/Objects/JSON", "Learn/JavaScript/Objects")}}
+ +

在解釋過大部分的 OOJS 細節之後,本文將說明該如何建立「子」物件類別 (建構子),並從其「母」類別繼承功能。此外,也將建議開發者應於何時、於何處使用 OOJS。

+ + + + + + + + + + + + +
必備條件:基本的電腦素養、已了解 HTML 與 CSS 基本概念、熟悉 JavaScript 基礎 (可參閱〈First steps〉與〈Building blocks〉) 與 OOJS 的基礎 (可參閱〈Introduction to objects〉)。
主旨:了解應如何建構 JavaScript 中的繼承。
+ +

原型繼承

+ +

目前為止,我們看過幾個繼承的實作範例:原型鍊的運作方式,以及繼承的成員如何形成原型鍊。但這些大部分都與內建的瀏覽器函式有關。那我們又該如何在 JavaScript 建立物件且由其他物件繼承而來呢?

+ +

如稍早的系列文章中所述,某些人認為 JavaScript 並非真正的物件導向 (OO) 語言。在「典型 OO」中,你必須定義特定的類別物件,才能定義哪些類別所要繼承的類別 (可參閱〈C++ inheritance〉了解簡易範例)。JavaScript 則使用不同的系統 —「繼承」的物件並不會一併複製功能過來,而是透過原型鍊連接其所繼承的功能,亦即所謂的原型繼承 (Prototypal inheritance)。

+ +

就透過範例了解此一概念吧。

+ +

入門

+ +

首先將 oojs-class-inheritance-start.html 檔案 (亦可參考實際執行) 複製到本端磁碟。可在裡面找到本課程一直沿用的 Person() 建構子範例,但這裡有些許不同,也就是該建構子只定義了屬性:

+ +
function Person(first, last, age, gender, interests) {
+  this.name = {
+    first,
+    last
+  };
+  this.age = age;
+  this.gender = gender;
+  this.interests = interests;
+};
+ +

這些函式均已定義於建構子的原型之上,例如:

+ +
Person.prototype.greeting = function() {
+  alert('Hi! I\'m ' + this.name.first + '.');
+};
+ +

假設要建立一個像前面 OO 定義中說過的 Teacher 類別,且除了繼承 Person 的所有成員,還要包含:

+ +
    +
  1. 新的 subject 屬性,可包含老師所傳授的科目。
  2. +
  3. 更新過的 greeting() 函式,要比標準的 greeting() 函式更正式一點,更適合老師在校對學生使用。
  4. +
+ +

定義 Teacher() 建構子函式

+ +

首先必須建立 Teacher() 建構子,將下列程式碼加到現有程式碼之下:

+ +
function Teacher(first, last, age, gender, interests, subject) {
+  Person.call(this, first, last, age, gender, interests);
+
+  this.subject = subject;
+}
+ +

這看起來和 Person 建構子有許多地方類似,但比較奇怪的就是之前沒看過的 call() 函式。此函式基本上可讓你呼叫在其他地方定義的函數,而非目前內文 (context) 中定義的函式。當執行函式時,第一個參數用來指定你要使用 this 值,其他參數則指定應該傳送到該函式的參數。

+ +
+

注意:我們此範例中建立新的物件實例時,會指定所要繼承的屬性。但必須注意的是,即使實例不需將屬性指定為參數,你還是必須在建構子中將屬性指定為參數  (在建立物件時,你可能獲得設定為隨意值的屬性)。

+
+ +

所以這裡可在 Teacher() 建構子函式之內有效執行 Person() 建構子函式 (如上述),藉以在 Teacher() 之內定義相同的屬性,但使用的是傳送到 Teacher() 的屬性值,而非 Person() 的屬性值 (我們將 this 值設為簡單的「this」並傳送到 call(),代表這個 thisTeacher() 的函式)。

+ +

建構子的最後一行則定義了新的 subject 屬性,代表只有老師具備,一般人不會有。

+ +

我們也可以單純這樣做:

+ +
function Teacher(first, last, age, gender, interests, subject) {
+  this.name = {
+    first,
+    last
+  };
+  this.age = age;
+  this.gender = gender;
+  this.interests = interests;
+  this.subject = subject;
+}
+ +

但這樣只是重新定義了新的屬性,而不是繼承自 Person() 而來,所以無法達到我們預設的目標,也需要更多程式碼才能達成。

+ +

設定 Teacher() 的原型與建構子參考

+ +

到目前為止發現一個問題:我們定義了新的建構子,但預設只有 1 個空白的 prototype 屬性。接著要讓 Teacher() 繼承 Person() 原型中所定義的函式,該怎麼做呢?

+ +
    +
  1. 繼續在現有程式碼下方加入: +
    Teacher.prototype = Object.create(Person.prototype);
    + 這裡再次用好朋友 create() 解救。我們透過 create() 並搭配等同於 Person.prototype 的原型,建立新的 prototype 屬性值 (它本身就是物件,包含屬性與函式) ,並將之設定為 Teacher.prototype 的值。也就是說 Teacher.prototype 現在會繼承 Person.prototype 上的所有可用函式。
  2. +
  3. 此外,基於我們的繼承方式,Teacher() prototype 的建構子屬性目前設定為 Person()。可參閱 Stack Overflow post 進一步了解原因。可先儲存自己的程式碼、在瀏覽器中載入頁面,再將下列程式碼輸入至 JavaScript 主控台以驗證: +
    Teacher.prototype.constructor
    +
  4. +
  5. 這樣可能會產生問題,所以要設定正確。你可回到自己的原始碼並在最下方加入下列程式碼: +
    Teacher.prototype.constructor = Teacher;
    +
  6. +
  7. 如果儲存並重新整理之後,只要輸入 Teacher.prototype.constructor 應該就會回傳 Teacher()
  8. +
+ +

給 Teacher() 新的 greeting() 函式

+ +

接著必須在 Teacher() 建構子上定義新的 greeting() 函式。

+ +

最簡單的方法就是在 Teacher() 的原型上定義此函式。將下列加入程式碼最底部:

+ +
Teacher.prototype.greeting = function() {
+  var prefix;
+
+  if(this.gender === 'male' || this.gender === 'Male' || this.gender === 'm' || this.gender === 'M') {
+    prefix = 'Mr.';
+  } else if(this.gender === 'female' || this.gender === 'Female' || this.gender === 'f' || this.gender === 'F') {
+    prefix = 'Mrs.';
+  } else {
+    prefix = 'Mx.';
+  }
+
+  alert('Hello. My name is ' + prefix + ' ' + this.name.last + ', and I teach ' + this.subject + '.');
+};
+ +

這樣就會顯示老師的問候語,也會針對老師的性別使用合適的稱呼,可用於條件陳述式。

+ +

簡易範例

+ +

現在你已經輸入所有程式碼了,可以試著用Teacher() 建立物件實例。將下列 (或是你想用的類似程式碼) 加入現有程式碼的底部:

+ +
var teacher1 = new Teacher('Dave', 'Griffiths', 31, 'male', ['football', 'cookery'], 'mathematics');
+ +

儲存並重新整理之後,試著存取新 teacher1 物件的屬性語函式,例如:

+ +
teacher1.name.first;
+teacher1.interests[0];
+teacher1.bio();
+teacher1.subject;
+teacher1.greeting();
+ +

這樣應該運作無虞。前 3 行的存取成員即繼承自一般 Person() 建構子 (類別)。最後 2 行的存取成員只能用於特定的 Teacher() 建構子 (類別) 之上。

+ +
+

注意:如果你無法進行到現有進度,可比較自己與完整版本 (亦可看實際執行的情形) 的程式碼。

+
+ +

這裡所提的技巧,當然不是在 JavaScript 建立繼承類別的唯一方法,但足以堪用。並可讓你了解應如何於 JavaScript 實作繼承。

+ +

你可能也想看看某些新的 {{glossary("ECMAScript")}} 功能,可更簡潔的在 JavaScript 中繼承 (參閱 Classes)。但由於這些功能尚未廣泛支援其他瀏覽器,這裡先略過不提。本系列文章中提到的其他程式碼,均可回溯支援到 IE9 或更早版本。當然還是有辦法支援更舊的版本。

+ +

一般方法就是使用 JavaScript 函式庫,且最常見的就是簡單集結可用的功能,更快、更輕鬆的完成繼承。例如 CoffeeScript 即提供了 class、extends 等等。

+ +

進階習題

+ +

〈OOP 理論〉段落中,我們也納入了 Student 類別並繼承了 Person 的所有功能,此外也提供不同的 greeting() 函式,且較 Teacher 的問候語沒那麼正式。在看了該段落中的學生問候語之後,可試著實作自己的 Student() 建構子,並繼承 Person(), 的所有功能,再實作不同的 greeting() 函式。

+ +
+

注意:如果你無法進行到現有進度,可參考完成版本 (亦可看實際執行的情形)。

+
+ +

物件成員摘要

+ +

簡單來說,你基本上需要考量 3 種類型的屬性\函式:

+ +
    +
  1. 已於建構子函式中定義,會交給物件實體的屬性\函式。這應該很容易處理。在你自己的程式碼中,就是透過 this.x = x 類別行並在建構子中定義的成員;在瀏覽器程式碼中,就是僅限物件實體可用的成員 (一般是透過 new 關鍵字並呼叫建構子所建立,例如 var myInstance = new myConstructor())。
  2. +
  3. 直接在建構子上定義,並僅能用於該建構子的屬性\函式。這類屬性\函式往往只能用於內建瀏覽器物件之上,並直接「鍊接」至建構子 (而非實例) 以利識別,例如 Object.keys()
  4. +
  5. 定義於建構子原型上的屬性\函式,交由所有的實例繼承,亦繼承了物件類別。這類屬性\函式包含建構子原型屬性之上定義的所有成員,例如 myConstructor.prototype.x()
  6. +
+ +

如果你不確定到底屬於上述的哪一個,也別擔心。現在你還在學習階段,往後定會熟能生巧。

+ +

在 JavaScript 中使用繼承的時機?

+ +

看到這裡,你心裡應該覺得很複雜。沒錯。「原型」與「繼承」可說是 JavaScript 最複雜的概念之二,但許多 JavaScript 的強大功能與彈性,均來自於其物件結構與繼承,也值得你了解運作方式。

+ +

不論是使用 WebAPI 的多樣功能,或是你在字串、陣列等所呼叫的函式\屬性 (定義於內建瀏覽器物件之上),你都可以不斷繼承下去。

+ +

在自己的程式碼裡,特別是剛接觸或小型專案時,你用繼承的頻率可能沒那麼高。若沒真正需要,只是「為使用而使用」繼承,老實說只是浪費時間。但隨著程式碼規模越來越大,你就會找到使用的時間。如果你發現自己開始建立類似功能的多個物件時,就可先建立通用的物件類型,來概括所有共用的功能,並在特定物件類型中繼承這些功能,既方便又有效率。

+ +
+

注意:基於 JavaScript 運作的方式 (如原型鍊等),物件之間的功能共享一般稱為委託 (Delegation)」,即特定物件將功能委託至通用物件類型。「委託」其實比繼承更精確一點。因為「所繼承的功能」並不會複製到「進行繼承的物件」之上,卻是保留在通用物件之中。

+
+ +

當使用繼承時,建議你不要設定太多層的繼承關係,並時時留意你所定義的函式與屬性。在開始寫程式碼時,你可能會暫時修改內建瀏覽器物件的原型,除非你真的需要,否則儘量別這麼做。太多繼承可能連你自己都搞混,而且一旦需要除錯就會痛苦萬分。

+ +

最後,物件可說是另一種形式的程式碼再利用,如同函式或迴圈一般,且有其專屬的角色與優點。如果你正建立一堆相關變數與函式,並要全部一起追蹤、封裝,就可以透過物件。你也能以物件方式傳送整個資料集合。而且上述兩種情況均不需使用建構子或繼承就能夠達成。如果你只需要某一物件的單一實作,那麼單純使用物件就好,完全不需要繼承。

+ +

摘要

+ +

本文為大家溫習了 OOJS 核心理論和語法。現在你應該了解 JavaScript 物件與 OOP 的基本概念、原型及原型繼承、建立類別 (建構子) 與物件實例的方法、為類別新增功能、建立從其他類別繼承而來的子類別。

+ +

下篇文章就要來看看該如何搭配 JavaScript Object Notation (JSON),使用 JavaScript 物件的常見資料交換格式。

+ +

另可參閱

+ + + +

{{PreviousMenuNext("Learn/JavaScript/Objects/Object_prototypes", "Learn/JavaScript/Objects/JSON", "Learn/JavaScript/Objects")}}

diff --git a/files/zh-tw/learn/javascript/objects/inheritance/index.html b/files/zh-tw/learn/javascript/objects/inheritance/index.html deleted file mode 100644 index f9db84dae8..0000000000 --- a/files/zh-tw/learn/javascript/objects/inheritance/index.html +++ /dev/null @@ -1,210 +0,0 @@ ---- -title: JavaScript 中的「繼承」 -slug: Learn/JavaScript/Objects/Inheritance -translation_of: Learn/JavaScript/Objects/Inheritance ---- -
{{LearnSidebar}}
- -
{{PreviousMenuNext("Learn/JavaScript/Objects/Object_prototypes", "Learn/JavaScript/Objects/JSON", "Learn/JavaScript/Objects")}}
- -

在解釋過大部分的 OOJS 細節之後,本文將說明該如何建立「子」物件類別 (建構子),並從其「母」類別繼承功能。此外,也將建議開發者應於何時、於何處使用 OOJS。

- - - - - - - - - - - - -
必備條件:基本的電腦素養、已了解 HTML 與 CSS 基本概念、熟悉 JavaScript 基礎 (可參閱〈First steps〉與〈Building blocks〉) 與 OOJS 的基礎 (可參閱〈Introduction to objects〉)。
主旨:了解應如何建構 JavaScript 中的繼承。
- -

原型繼承

- -

目前為止,我們看過幾個繼承的實作範例:原型鍊的運作方式,以及繼承的成員如何形成原型鍊。但這些大部分都與內建的瀏覽器函式有關。那我們又該如何在 JavaScript 建立物件且由其他物件繼承而來呢?

- -

如稍早的系列文章中所述,某些人認為 JavaScript 並非真正的物件導向 (OO) 語言。在「典型 OO」中,你必須定義特定的類別物件,才能定義哪些類別所要繼承的類別 (可參閱〈C++ inheritance〉了解簡易範例)。JavaScript 則使用不同的系統 —「繼承」的物件並不會一併複製功能過來,而是透過原型鍊連接其所繼承的功能,亦即所謂的原型繼承 (Prototypal inheritance)。

- -

就透過範例了解此一概念吧。

- -

入門

- -

首先將 oojs-class-inheritance-start.html 檔案 (亦可參考實際執行) 複製到本端磁碟。可在裡面找到本課程一直沿用的 Person() 建構子範例,但這裡有些許不同,也就是該建構子只定義了屬性:

- -
function Person(first, last, age, gender, interests) {
-  this.name = {
-    first,
-    last
-  };
-  this.age = age;
-  this.gender = gender;
-  this.interests = interests;
-};
- -

這些函式均已定義於建構子的原型之上,例如:

- -
Person.prototype.greeting = function() {
-  alert('Hi! I\'m ' + this.name.first + '.');
-};
- -

假設要建立一個像前面 OO 定義中說過的 Teacher 類別,且除了繼承 Person 的所有成員,還要包含:

- -
    -
  1. 新的 subject 屬性,可包含老師所傳授的科目。
  2. -
  3. 更新過的 greeting() 函式,要比標準的 greeting() 函式更正式一點,更適合老師在校對學生使用。
  4. -
- -

定義 Teacher() 建構子函式

- -

首先必須建立 Teacher() 建構子,將下列程式碼加到現有程式碼之下:

- -
function Teacher(first, last, age, gender, interests, subject) {
-  Person.call(this, first, last, age, gender, interests);
-
-  this.subject = subject;
-}
- -

這看起來和 Person 建構子有許多地方類似,但比較奇怪的就是之前沒看過的 call() 函式。此函式基本上可讓你呼叫在其他地方定義的函數,而非目前內文 (context) 中定義的函式。當執行函式時,第一個參數用來指定你要使用 this 值,其他參數則指定應該傳送到該函式的參數。

- -
-

注意:我們此範例中建立新的物件實例時,會指定所要繼承的屬性。但必須注意的是,即使實例不需將屬性指定為參數,你還是必須在建構子中將屬性指定為參數  (在建立物件時,你可能獲得設定為隨意值的屬性)。

-
- -

所以這裡可在 Teacher() 建構子函式之內有效執行 Person() 建構子函式 (如上述),藉以在 Teacher() 之內定義相同的屬性,但使用的是傳送到 Teacher() 的屬性值,而非 Person() 的屬性值 (我們將 this 值設為簡單的「this」並傳送到 call(),代表這個 thisTeacher() 的函式)。

- -

建構子的最後一行則定義了新的 subject 屬性,代表只有老師具備,一般人不會有。

- -

我們也可以單純這樣做:

- -
function Teacher(first, last, age, gender, interests, subject) {
-  this.name = {
-    first,
-    last
-  };
-  this.age = age;
-  this.gender = gender;
-  this.interests = interests;
-  this.subject = subject;
-}
- -

但這樣只是重新定義了新的屬性,而不是繼承自 Person() 而來,所以無法達到我們預設的目標,也需要更多程式碼才能達成。

- -

設定 Teacher() 的原型與建構子參考

- -

到目前為止發現一個問題:我們定義了新的建構子,但預設只有 1 個空白的 prototype 屬性。接著要讓 Teacher() 繼承 Person() 原型中所定義的函式,該怎麼做呢?

- -
    -
  1. 繼續在現有程式碼下方加入: -
    Teacher.prototype = Object.create(Person.prototype);
    - 這裡再次用好朋友 create() 解救。我們透過 create() 並搭配等同於 Person.prototype 的原型,建立新的 prototype 屬性值 (它本身就是物件,包含屬性與函式) ,並將之設定為 Teacher.prototype 的值。也就是說 Teacher.prototype 現在會繼承 Person.prototype 上的所有可用函式。
  2. -
  3. 此外,基於我們的繼承方式,Teacher() prototype 的建構子屬性目前設定為 Person()。可參閱 Stack Overflow post 進一步了解原因。可先儲存自己的程式碼、在瀏覽器中載入頁面,再將下列程式碼輸入至 JavaScript 主控台以驗證: -
    Teacher.prototype.constructor
    -
  4. -
  5. 這樣可能會產生問題,所以要設定正確。你可回到自己的原始碼並在最下方加入下列程式碼: -
    Teacher.prototype.constructor = Teacher;
    -
  6. -
  7. 如果儲存並重新整理之後,只要輸入 Teacher.prototype.constructor 應該就會回傳 Teacher()
  8. -
- -

給 Teacher() 新的 greeting() 函式

- -

接著必須在 Teacher() 建構子上定義新的 greeting() 函式。

- -

最簡單的方法就是在 Teacher() 的原型上定義此函式。將下列加入程式碼最底部:

- -
Teacher.prototype.greeting = function() {
-  var prefix;
-
-  if(this.gender === 'male' || this.gender === 'Male' || this.gender === 'm' || this.gender === 'M') {
-    prefix = 'Mr.';
-  } else if(this.gender === 'female' || this.gender === 'Female' || this.gender === 'f' || this.gender === 'F') {
-    prefix = 'Mrs.';
-  } else {
-    prefix = 'Mx.';
-  }
-
-  alert('Hello. My name is ' + prefix + ' ' + this.name.last + ', and I teach ' + this.subject + '.');
-};
- -

這樣就會顯示老師的問候語,也會針對老師的性別使用合適的稱呼,可用於條件陳述式。

- -

簡易範例

- -

現在你已經輸入所有程式碼了,可以試著用Teacher() 建立物件實例。將下列 (或是你想用的類似程式碼) 加入現有程式碼的底部:

- -
var teacher1 = new Teacher('Dave', 'Griffiths', 31, 'male', ['football', 'cookery'], 'mathematics');
- -

儲存並重新整理之後,試著存取新 teacher1 物件的屬性語函式,例如:

- -
teacher1.name.first;
-teacher1.interests[0];
-teacher1.bio();
-teacher1.subject;
-teacher1.greeting();
- -

這樣應該運作無虞。前 3 行的存取成員即繼承自一般 Person() 建構子 (類別)。最後 2 行的存取成員只能用於特定的 Teacher() 建構子 (類別) 之上。

- -
-

注意:如果你無法進行到現有進度,可比較自己與完整版本 (亦可看實際執行的情形) 的程式碼。

-
- -

這裡所提的技巧,當然不是在 JavaScript 建立繼承類別的唯一方法,但足以堪用。並可讓你了解應如何於 JavaScript 實作繼承。

- -

你可能也想看看某些新的 {{glossary("ECMAScript")}} 功能,可更簡潔的在 JavaScript 中繼承 (參閱 Classes)。但由於這些功能尚未廣泛支援其他瀏覽器,這裡先略過不提。本系列文章中提到的其他程式碼,均可回溯支援到 IE9 或更早版本。當然還是有辦法支援更舊的版本。

- -

一般方法就是使用 JavaScript 函式庫,且最常見的就是簡單集結可用的功能,更快、更輕鬆的完成繼承。例如 CoffeeScript 即提供了 class、extends 等等。

- -

進階習題

- -

〈OOP 理論〉段落中,我們也納入了 Student 類別並繼承了 Person 的所有功能,此外也提供不同的 greeting() 函式,且較 Teacher 的問候語沒那麼正式。在看了該段落中的學生問候語之後,可試著實作自己的 Student() 建構子,並繼承 Person(), 的所有功能,再實作不同的 greeting() 函式。

- -
-

注意:如果你無法進行到現有進度,可參考完成版本 (亦可看實際執行的情形)。

-
- -

物件成員摘要

- -

簡單來說,你基本上需要考量 3 種類型的屬性\函式:

- -
    -
  1. 已於建構子函式中定義,會交給物件實體的屬性\函式。這應該很容易處理。在你自己的程式碼中,就是透過 this.x = x 類別行並在建構子中定義的成員;在瀏覽器程式碼中,就是僅限物件實體可用的成員 (一般是透過 new 關鍵字並呼叫建構子所建立,例如 var myInstance = new myConstructor())。
  2. -
  3. 直接在建構子上定義,並僅能用於該建構子的屬性\函式。這類屬性\函式往往只能用於內建瀏覽器物件之上,並直接「鍊接」至建構子 (而非實例) 以利識別,例如 Object.keys()
  4. -
  5. 定義於建構子原型上的屬性\函式,交由所有的實例繼承,亦繼承了物件類別。這類屬性\函式包含建構子原型屬性之上定義的所有成員,例如 myConstructor.prototype.x()
  6. -
- -

如果你不確定到底屬於上述的哪一個,也別擔心。現在你還在學習階段,往後定會熟能生巧。

- -

在 JavaScript 中使用繼承的時機?

- -

看到這裡,你心裡應該覺得很複雜。沒錯。「原型」與「繼承」可說是 JavaScript 最複雜的概念之二,但許多 JavaScript 的強大功能與彈性,均來自於其物件結構與繼承,也值得你了解運作方式。

- -

不論是使用 WebAPI 的多樣功能,或是你在字串、陣列等所呼叫的函式\屬性 (定義於內建瀏覽器物件之上),你都可以不斷繼承下去。

- -

在自己的程式碼裡,特別是剛接觸或小型專案時,你用繼承的頻率可能沒那麼高。若沒真正需要,只是「為使用而使用」繼承,老實說只是浪費時間。但隨著程式碼規模越來越大,你就會找到使用的時間。如果你發現自己開始建立類似功能的多個物件時,就可先建立通用的物件類型,來概括所有共用的功能,並在特定物件類型中繼承這些功能,既方便又有效率。

- -
-

注意:基於 JavaScript 運作的方式 (如原型鍊等),物件之間的功能共享一般稱為委託 (Delegation)」,即特定物件將功能委託至通用物件類型。「委託」其實比繼承更精確一點。因為「所繼承的功能」並不會複製到「進行繼承的物件」之上,卻是保留在通用物件之中。

-
- -

當使用繼承時,建議你不要設定太多層的繼承關係,並時時留意你所定義的函式與屬性。在開始寫程式碼時,你可能會暫時修改內建瀏覽器物件的原型,除非你真的需要,否則儘量別這麼做。太多繼承可能連你自己都搞混,而且一旦需要除錯就會痛苦萬分。

- -

最後,物件可說是另一種形式的程式碼再利用,如同函式或迴圈一般,且有其專屬的角色與優點。如果你正建立一堆相關變數與函式,並要全部一起追蹤、封裝,就可以透過物件。你也能以物件方式傳送整個資料集合。而且上述兩種情況均不需使用建構子或繼承就能夠達成。如果你只需要某一物件的單一實作,那麼單純使用物件就好,完全不需要繼承。

- -

摘要

- -

本文為大家溫習了 OOJS 核心理論和語法。現在你應該了解 JavaScript 物件與 OOP 的基本概念、原型及原型繼承、建立類別 (建構子) 與物件實例的方法、為類別新增功能、建立從其他類別繼承而來的子類別。

- -

下篇文章就要來看看該如何搭配 JavaScript Object Notation (JSON),使用 JavaScript 物件的常見資料交換格式。

- -

另可參閱

- - - -

{{PreviousMenuNext("Learn/JavaScript/Objects/Object_prototypes", "Learn/JavaScript/Objects/JSON", "Learn/JavaScript/Objects")}}

diff --git a/files/zh-tw/learn/javascript/objects/object-oriented_js/index.html b/files/zh-tw/learn/javascript/objects/object-oriented_js/index.html deleted file mode 100644 index 1504f5fc49..0000000000 --- a/files/zh-tw/learn/javascript/objects/object-oriented_js/index.html +++ /dev/null @@ -1,277 +0,0 @@ ---- -title: 初學者應知道的物件導向 JavaScript -slug: Learn/JavaScript/Objects/Object-oriented_JS -translation_of: Learn/JavaScript/Objects/Object-oriented_JS ---- -
{{LearnSidebar}}
- -
{{PreviousMenuNext("Learn/JavaScript/Objects/Basics", "Learn/JavaScript/Objects/Object_prototypes", "Learn/JavaScript/Objects")}}
- -

在看完了基本概念之後,接著要說明物件導向 JavaScript (OOJS)。本文將概述物件導向程式設計 (OOP) 的理論,另說明 JavaScript 是如何透過建構子函式來模擬物件類別,又是如何建立物件實體 (Instance)。

- - - - - - - - - - - - -
必備條件:基本的電腦素養、已初步了解 HTML 與 CSS、熟悉 JavaScript (參閱〈First steps〉與〈Building blocks〉以及 OOJS 基礎概念 (參閱〈物件基礎概念〉。
主旨:了解物件導向程式設計的基本理論、其與 JavaScript (幾乎所有東西都是物件) 之間的關係、應如何寫出建構子與物件實體。
- -

基本物件導向程式設計

- -

最先,讓我們從簡單、高層次的視角來看物件導向程式設計 (Object-oriented programming;OOP) 最基本的概念。我們說簡單是因為 OOP 很容易變得複雜,就算現在就設法完整解釋,也可能讓大家越來越混亂。OOP 基本概念是:採用物件(objects)來模塑真實的實物世界:也就是在程式中的呈現是透過 objects 來塑造其模型,且\或提供簡單方式存取其「難以或不可能採用的功能」。

- -

物件可裝載相關的資料與程式碼,資料部分是你塑造某個模型的資訊,而程式碼部分則用是操作行為(Method)實現。Object data  -- 函式部分通常也使用 ---可工整地儲存 (正式一點應該是封裝 Encapsulated) 在物件包裹(這個包裹有特定的稱呼方式,有時候即所謂的命名空間 Namespace) ,使其能輕鬆地建構性存取。物件也常作為「資料儲存 (Datastore),促成簡易實現跨網傳送。

- -

定義物件範本

- -

我們先找個簡單程式,如同學校裡用來顯示師生資訊的程式。本文只是要了解一般的 OOP 理論,並非以特定程式語言為出發點。

- -

我們先拿第一篇物件文章中的「Person」物件類型為範例,其中定義了一個人的一般資料與功能。其實有很多資訊可助你了解一個人 (像是住址、身高、鞋子尺寸、DNA、護照號碼、明顯的人格特質等......),但我們這裡只列出了姓名、年齡、性別、興趣。我們另希望根據這些資料寫出簡介,初步了解這個人。上述這些即所謂的「抽象 (Abstraction)」。為某個複雜東西建立簡單的模型,藉以代表其最重要的概念或特質,且該模型建立方式極易於搭配我們的程式設計用途。

- -

- -

建立實際物件

- -

我們可從這個「類別」建立物件實體 (Object instance) — 即該物件包含了類別中所定義的資料與功能。而「Person」類別可設定出幾個實際的人物:

- -

- -

在根據類別建立物件實體時,就是執行類別的「建構子 (Constructor) 函式」所建立。而這個「根據類別來建立物件實體」的過程即稱為「實體化 (Instantiation)」。物件實體就是從類別實體化而來。

- -

特殊類別

- -

如果我們不要一般人物,而想建立老師與學生這類比較特定類型的人物。在 OOP 中,我們可根據其他類別建立新的類別。新的子類別則可繼承 (Inherit) 母類別的資料與程式碼特性。你可重複使用所有物件類型共有的功能,而不需再複製之。若功能需與類別有所差異,則可直接於其上定義特殊功能。

- -

- -

這樣很方便。因為老師與學生也同樣使用了如姓名、性別、年齡等的共通特性,所以只要定義這些特性 1 次即可。你也可以分別在不同的類別中定義相同特性,各個特性的定義就置於不同的命名空間中。舉例來說,學生的打招呼方式可以是「Yo, I'm [firstName]」;老師的招呼方式就正式一點,如「Hello, my name is [Prefix] [lastName]」。

- -
-

注意:所謂的「多型 (Polymorphism)」,即是用多個物件類別建置相同功能。

-
- -

現在你可根據子類別來建立物件實例了。例如:

- -

- -

本文後面將講解應如何將 OOP 理論實際用於 JavaScript 中。

- -

建構子與物件實例

- -

JavaScript 使用稱為建構子函式(constructor function)的特殊函式,定義物件與功能。開發者常常會不知道到底需建立多少物件,這時建構子可讓你高效率建立所需物件,並依需要為其添加資料與函式。

- -

在新的物件實例透過建構子函式產生後,其核心將透過一種稱為原型鏈(Prototype chain,由原型定義,可參閱 Object prototypes)的參照鏈連在一起。

- -

接著就在 JS 中,透過建構子建立類別及其物件實例。首先請你先在本端磁碟中再另外複製一份前面文章提過的 oojs.html 檔案。

- -

簡易範例

- -
    -
  1. 先看看該如何用一般函式定義一個人。將下列函式加到 script 元素裡面: - -
    function createNewPerson(name) {
    -  var obj = {};
    -  obj.name = name;
    -  obj.greeting = function () {
    -    alert('Hi! I\'m ' + this.name + '.');
    -  }
    -  return obj;
    -}
    -
  2. -
  3. 呼叫此函式之後即可建立新的 1 個人,另在瀏覽器的 JavaScript 主控台中測試下列程式碼: -
    var salva = createNewPerson('salva');
    -salva.name;
    -salva.greeting();
    - 目前為止沒什麼問題,但有點囉嗦。如果早知道要建立物件的話,又何必要建立新的空白物件再回傳呢?幸好 JavaScript 透過建構子函式提供了方便的捷徑。現在就來試試看!
  4. -
  5. 用下列程式碼替代之前的函式: -
    function Person(name) {
    -  this.name = name;
    -  this.greeting = function() {
    -    alert('Hi! I\'m ' + this.name + '.');
    -  };
    -}
    -
  6. -
- -

建構子也就是 JavaScript 版本的「類別」之一。你可以注意到,除了無法回傳任何數值或明確建立物件之外,建構子其實已具備函式中的所有功能,並已基本定義了屬性與函式 (Method)。你也能看到這裡同樣用了「this」關鍵字,即不論何時建立了這裡的任一物件實例,物件的「name」屬性均同等於「傳送至建構子呼叫的名稱值」;且 greeting() 函式也會使用相同「傳送至建構子呼叫的名稱值」。

- -
-

注意:建構子函式名稱往往以大寫字母起頭,如此可方便你在程式碼中找出建構子函式。

-
- -

我們又該如何呼叫建構子以建立物件呢?

- -
    -
  1. 將下列程式碼加到目前的程式碼之下: -
    var person1 = new Person('Bob');
    -var person2 = new Person('Sarah');
    -
  2. -
  3. 儲存程式碼並在瀏覽器中重新載入,試著將下列程式碼輸入到文字輸入畫面中: -
    person1.name
    -person1.greeting()
    -person2.name
    -person2.greeting()
    -
  4. -
- -

現在你應該能在頁面上看到兩組新物件,且各自以不同的命名空間儲存。若要存取其屬性與函式,就要以 person1person2 開始呼叫。這些物件均完整封包,不致與其他功能衝突;但仍具備相同的 name 屬性與 greeting() 函式。另請注意,物件均使用當初建立時所各自指派的 name 值;這也是「this」如此重要的原因之一,以確保物件可使用自己的值而不致混淆其他數值。

- -

再看一次建構子呼叫:

- -
var person1 = new Person('Bob');
-var person2 = new Person('Sarah');
- -

這裡用了「new」關鍵字告知瀏覽器「我們要建立新的物件實例」,並接著在函式名稱之後的括號內傳入函式所需要的參數,並將結果儲存於變數之中 — 相當類似普通函式被呼叫的方式 。各個實例均根據此定義所建立:

- -
function Person(name) {
-  this.name = name;
-  this.greeting = function() {
-    alert('Hi! I\'m ' + this.name + '.');
-  };
-}
- -

在建立新的物件之後,person1person2 的變數將有效率地納入下列物件:

- -
{
-  name : 'Bob',
-  greeting : function() {
-    alert('Hi! I\'m ' + this.name + '.');
-  }
-}
-
-{
-  name : 'Sarah',
-  greeting : function() {
-    alert('Hi! I\'m ' + this.name + '.');
-  }
-}
- -

剛剛說的「有效率」,是因為實際功能仍定義於類別中,而非物件實例之中。這情況與我們稍早談過的物件實字 (Object literal) 相反。

- -

建立完整的建構子

- -

上面不過是入門的簡單範例。接著繼續建立最後的 Person() 建構子。

- -
    -
  1. 將截至目前為止的程式碼移除,加入下列取代用的建構子。原則上與簡易範例完全一樣,只是比較複雜一點: -
    function Person(first, last, age, gender, interests) {
    -  this.name = {
    -    first,
    -    last
    -  };
    -  this.age = age;
    -  this.gender = gender;
    -  this.interests = interests;
    -  this.bio = function() {
    -    alert(this.name.first + ' ' + this.name.last + ' is ' + this.age + ' years old. He likes ' + this.interests[0] + ' and ' + this.interests[1] + '.');
    -  };
    -  this.greeting = function() {
    -    alert('Hi! I\'m ' + this.name.first + '.');
    -  };
    -};
    -
  2. -
  3. 再接著加入下列程式碼,就可建立物件實例: -
    var person1 = new Person('Bob', 'Smith', 32, 'male', ['music', 'skiing']);
    -
  4. -
- -

現在可存取我們為第一個物件所定義的屬性與函式:

- -
person1['age']
-person1.interests[1]
-person1.bio()
-// etc.
- -
-

注意:如果你到這裡有點吃力,請先比較自己與我們的程式碼。可參閱 oojs-class-finished.html (也可看實際執行的情況)。

-
- -

進階習題

- -

在開始之前,先試著自己添加更多物件建立程式碼,再針對產生的物件實例取得並設定其成員。

- -

此外,原來的 bio() 函式其實有點問題。即使你的「人」是女性,其輸出一定會有「He」這個代名詞。而且即使 interests 陣列中列出超過 2 個以上的「興趣」,這個 bio 函式也只會有 2 個興趣。你能試著在類別定義 (建構子) 中修正這個問題嗎?你可在建構子中放入任何你喜歡的程式碼 (但可能會需要幾個 conditionals 搭配 1 個迴圈)。想想應如何根據性別以及列出的興趣 (1 或 2 個以上),建構出不同的程式碼。

- -
-

注意:如果你卡在這裡,我們也在 GitHub repo 上提供了解答 (立刻觀看)。先試著自己解決問題吧!

-
- -

建立物件實例的其他方法

- -

目前解釋了 2 種建立物件實例的方法 — 宣告物件實字,以及使用建構子函式。

- -

當然還有別的方法,但我們希望你先熟悉此 2 種方法,以免你日後的 Web 旅程上會再遇到。

- -

Object() 建構子

- -

首先可使用 Object() 建構子建立新的物件。沒錯,即使是泛型物件 (Generic object) 也具備建構子,可用以產生空白物件。

- -
    -
  1. 將下列輸入瀏覽器的 JavaScript 主控台內: -
    var person1 = new Object();
    -
  2. -
  3. 如此會在 person1 變數中儲存 1 組空白物件。接著可透過點 (dot-) 或括弧記法 (bracket notation) 為此物件新增屬性與函式。如下列範例: -
    person1.name = 'Chris';
    -person1['age'] = 38;
    -person1.greeting = function() {
    -  alert('Hi! I\'m ' + this.name + '.');
    -}
    -
  4. -
  5. 你可將一組物件實字傳送給 Object() 建構子作為參數,藉以預先填入屬性\函式。如下所示: -
    var person1 = new Object({
    -  name : 'Chris',
    -  age : 38,
    -  greeting : function() {
    -    alert('Hi! I\'m ' + this.name + '.');
    -  }
    -});
    -
  6. -
- -

使用 create() 函式

- -

建構子可以幫助你保持程式的可讀性 — 你可以將建構子建立在同一個地方,並根據需求從這些建構子中建立物件實例,這樣做可以讓你清楚地得知它們的來源。

- -

不過,有些人偏好建立物件實例,而不先做建構子,尤其是他們的物件不會用很多實例時。JavaScript 有個稱作 create() 的內建函式能讓你這麼做。有了它,你就能根據現有物件,建立新的物件。

- -
    -
  1. 在 JavaScript 主控台裡測試: -
    var person2 = Object.create(person1);
    -
  2. -
  3. 再測試以下: -
    person2.name
    -person2.greeting()
    -
  4. -
- -

你會看到 person2 是根據 person1 所建立:它具備了相同的屬性以及可用的函式。

- -

create() 的其中一個限制,就是 IE8 並不支援。因此,如果你需要支援舊版瀏覽器,建構子會比較有用。

- -

我們後續會再說明 create() 的效果。

- -

總結

- -

本文簡略說明了 OO 理論,雖然還沒全部講完,至少也讓你初步了解到本文所要闡述的重點。此外,我們已經開始說明 JavaScript 與 OO 之間的關係、其與「傳統 OO」之間的差異、使用建構子於 JavaScript 中實作類別的方法,以及其他產生物件實例的方式。

- -

下一篇文章將說明 JavaScript 物件原型。

- -

{{PreviousMenuNext("Learn/JavaScript/Objects/Basics", "Learn/JavaScript/Objects/Object_prototypes", "Learn/JavaScript/Objects")}}

- -

In this module

- - -- cgit v1.2.3-54-g00ecf