JavaScript: Objekte erkunden

Das Material, dessen Übersetzung wir heute veröffentlichen, ist dem Studium von Objekten gewidmet - einer der Schlüsselessenzen von JavaScript. Es richtet sich in erster Linie an Anfänger, die ihr Wissen über Objekte optimieren möchten.



Objekte in JavaScript sind dynamische Sammlungen von Eigenschaften, die zusätzlich eine „versteckte“ Eigenschaft enthalten, die ein Prototyp des Objekts ist. Eigenschaften von Objekten sind durch Schlüssel und Werte gekennzeichnet. Beginnen wir die Konversation über JS-Objekte mit Schlüsseln.

Objekteigenschaftsschlüssel


Der Objekteigenschaftsschlüssel ist eine eindeutige Zeichenfolge. Sie können zwei Methoden verwenden, um auf Eigenschaften zuzugreifen: Zugriff über einen Punkt und Angabe des Objektschlüssels in eckigen Klammern. Beim Zugriff auf Eigenschaften über einen Punkt muss der Schlüssel eine gültige JavaScript-Kennung sein. Betrachten Sie ein Beispiel:

let obj = {  message : "A message" } obj.message //"A message" obj["message"] //"A message" 

Beim Versuch, auf eine nicht vorhandene Eigenschaft eines Objekts zuzugreifen, wird keine Fehlermeldung angezeigt, aber der undefined Wert wird zurückgegeben:

 obj.otherProperty //undefined 

Wenn Sie in eckigen Klammern auf Eigenschaften zugreifen, können Sie Schlüssel verwenden, die keine gültigen JavaScript-Bezeichner sind (der Schlüssel kann beispielsweise eine Zeichenfolge sein, die Leerzeichen enthält). Sie können einen beliebigen Wert haben, der in eine Zeichenfolge umgewandelt werden kann:

 let french = {}; french["merci beaucoup"] = "thank you very much"; french["merci beaucoup"]; //"thank you very much" 

Wenn Nicht-String-Werte als Schlüssel verwendet werden, werden sie automatisch in Strings konvertiert (wenn möglich mit der toString() -Methode):

 et obj = {}; //Number obj[1] = "Number 1"; obj[1] === obj["1"]; //true //Object let number1 = { toString : function() { return "1"; } } obj[number1] === obj["1"]; //true 

In diesem Beispiel wird das Objekt number1 als Schlüssel verwendet. Beim Versuch, auf eine Eigenschaft zuzugreifen, wird diese in Zeile 1 konvertiert, und das Ergebnis dieser Konvertierung wird als Schlüssel verwendet.

Objekteigenschaftswerte


Objekteigenschaften können primitive Werte, Objekte oder Funktionen sein.

▍Objekt als Objekteigenschaftswert


Objekte können in anderen Objekten platziert werden. Betrachten Sie ein Beispiel :

 let book = { title : "The Good Parts", author : {   firstName : "Douglas",   lastName : "Crockford" } } book.author.firstName; //"Douglas" 

Ein ähnlicher Ansatz kann zum Erstellen von Namespaces verwendet werden:

 let app = {}; app.authorService = { getAuthors : function() {} }; app.bookService = { getBooks : function() {} }; 

▍ Funktion als Objekteigenschaftswert


Wenn eine Funktion als Objekteigenschaftswert verwendet wird, wird sie normalerweise zu einer Objektmethode. Verwenden Sie in der Methode das this , um auf das aktuelle Objekt zuzugreifen.

Dieses Schlüsselwort kann jedoch je nach Aufruf der Funktion unterschiedliche Bedeutungen haben. Hier können Sie über Situationen lesen, in denen this den Kontext verliert.

Die Dynamik von Objekten


Objekte in JavaScript sind von Natur aus dynamische Entitäten. Sie können ihnen jederzeit Eigenschaften hinzufügen. Gleiches gilt für das Löschen von Eigenschaften:

 let obj = {}; obj.message = "This is a message"; //   obj.otherMessage = "A new message"; //    delete obj.otherMessage; //  

Objekte als assoziative Arrays


Objekte können als assoziative Arrays betrachtet werden. Assoziative Array-Schlüssel sind die Eigenschaftsnamen des Objekts. Um auf den Schlüssel zuzugreifen, müssen Sie nicht alle Eigenschaften betrachten, dh der Vorgang des Zugriffs auf den Schlüssel eines assoziativen Arrays basierend auf einem Objekt wird in O (1) -Zeit ausgeführt.

Objektprototypen


Objekte haben einen "versteckten" Link, __proto__ , der auf ein Prototypobjekt verweist, von dem das Objekt Eigenschaften erbt.

Ein mit einem Objektliteral erstelltes Objekt verfügt beispielsweise über einen Link zu Object.prototype :

 var obj = {}; obj.__proto__ === Object.prototype; //true 

▍ Leere Objekte


Wie wir gerade gesehen haben, ist das "leere" Objekt {} tatsächlich nicht so leer, da es einen Verweis auf Object.prototype . Um ein wirklich leeres Objekt zu erstellen, müssen Sie die folgende Konstruktion verwenden:

 Object.create(null) 

Dadurch wird ein Objekt ohne Prototyp erstellt. Solche Objekte werden normalerweise verwendet, um assoziative Arrays zu erstellen.

▍ Prototypkette


Prototypobjekte können eigene Prototypen haben. Wenn Sie versuchen, auf eine Eigenschaft eines Objekts zuzugreifen, das sich nicht darin befindet, versucht JavaScript, diese Eigenschaft im Prototyp dieses Objekts zu finden. Wenn die gewünschte Eigenschaft nicht vorhanden ist, wird versucht, sie im Prototyp des Prototyps zu finden. Dies wird fortgesetzt, bis die gewünschte Eigenschaft gefunden wurde oder bis das Ende der Prototypkette erreicht ist.

Primitive Typwerte und Objektverpackungen


Mit JavaScript können Sie mit den Werten primitiver Typen als Objekte arbeiten, in dem Sinne, dass Sie mit der Sprache auf deren Eigenschaften und Methoden zugreifen können.

 (1.23).toFixed(1); //"1.2" "text".toUpperCase(); //"TEXT" true.toString(); //"true" 

Darüber hinaus sind die Werte primitiver Typen natürlich keine Objekte.

Um den Zugriff auf die „Eigenschaften“ von Werten primitiver Typen zu organisieren, erstellt JavaScript bei Bedarf Wrapper-Objekte, die, nachdem sie nicht mehr benötigt werden, zerstört werden. Der Prozess zum Erstellen und Zerstören von Wrapper-Objekten wird von der JS-Engine optimiert.

Objekt-Wrapper haben Werte vom numerischen, Zeichenfolgen- und logischen Typ. Objekte der entsprechenden Typen werden durch die Konstruktorfunktionen Number , String und Boolean .

Eingebettete Prototypen


Number-Objekte erben Eigenschaften und Methoden vom Prototyp Number.prototype , dem Nachkommen von Object.prototype :

 var no = 1; no.__proto__ === Number.prototype; //true no.__proto__.__proto__ === Object.prototype; //true 

Der Prototyp von String-Objekten ist String.prototype . Der Prototyp von booleschen Objekten ist Boolean.prototype . Der Prototyp von Arrays (die auch Objekte sind) ist Array.prototype .

Funktionen in JavaScript sind auch Objekte mit einem Prototyp Function.prototype . Funktionen haben Methoden wie bind() , apply() und call() .

Alle Objekte, Funktionen und Objekte, die primitive Object.prototype (mit Ausnahme von null und undefined Werten), erben Eigenschaften und Methoden von Object.prototype . Dies führt dazu, dass beispielsweise alle eine toString() -Methode haben.

Erweitern eingebetteter Objekte mit Polyfills


Mit JavaScript können eingebettete Objekte mithilfe sogenannter Polyfills problemlos um neue Funktionen erweitert werden. Eine Polyfüllung ist ein Code, der Funktionen implementiert, die von keinem Browser unterstützt werden.

▍Verwendung von Polyfills


Beispielsweise gibt es eine Polyfüllung für die Object.assign() -Methode. Sie können dem Object eine neue Funktion hinzufügen, wenn diese nicht verfügbar ist.

Gleiches gilt für das Array.from() , das, wenn sich die from() -Methode nicht im Array Objekt befindet, mit dieser Methode ausgestattet wird.

▍ Polyfill und Prototypen


Mit Hilfe von Polyfills können Prototypen von Objekten um neue Methoden erweitert werden. Mit der String.prototype.trim() für String.prototype.trim() können Sie beispielsweise alle String-Objekte mit der Methode trim() ausstatten:

 let text = "   A text "; text.trim(); //"A text" 

Mit der Array.prototype.find() für Array.prototype.find() können Sie alle Arrays mit der find() -Methode ausstatten. Die Array.prototype.findIndex() für Array.prototype.findIndex() funktioniert auf ähnliche Weise:

 let arr = ["A", "B", "C", "D", "E"]; arr.indexOf("C"); //2 

Einzelvererbung


Mit dem Befehl Object.create() können Sie neue Objekte mit einem bestimmten Prototypobjekt erstellen. Dieser Befehl wird in JavaScript verwendet, um einen einzelnen Vererbungsmechanismus zu implementieren. Betrachten Sie ein Beispiel :

 let bookPrototype = { getFullTitle : function(){   return this.title + " by " + this.author; } } let book = Object.create(bookPrototype); book.title = "JavaScript: The Good Parts"; book.author = "Douglas Crockford"; book.getFullTitle();//JavaScript: The Good Parts by Douglas Crockford 

Mehrfachvererbung


Der Befehl Object.assign() kopiert Eigenschaften von einem oder mehreren Objekten in das Zielobjekt. Es kann verwendet werden, um mehrere Vererbungsschemata zu implementieren. Hier ist ein Beispiel :

 let authorDataService = { getAuthors : function() {} }; let bookDataService = { getBooks : function() {} }; let userDataService = { getUsers : function() {} }; let dataService = Object.assign({}, authorDataService, bookDataService, userDataService ); dataService.getAuthors(); dataService.getBooks(); dataService.getUsers(); 

Unveränderliche Objekte


Mit dem Befehl Object.freeze() können Sie ein Objekt „einfrieren“. Sie können einem solchen Objekt keine neuen Eigenschaften hinzufügen. Eigenschaften können weder gelöscht noch ihre Werte geändert werden. Mit diesem Befehl wird ein Objekt unveränderlich oder unveränderlich:

 "use strict"; let book = Object.freeze({ title : "Functional-Light JavaScript", author : "Kyle Simpson" }); book.title = "Other title";//: Cannot assign to read only property 'title' 

Der Befehl Object.freeze() führt das sogenannte "flache Einfrieren" von Objekten durch. Dies bedeutet, dass in einem "eingefrorenen" Objekt verschachtelte Objekte geändert werden können. Um ein Objekt tief einzufrieren, müssen Sie alle seine Eigenschaften rekursiv einfrieren.

Objekte klonen


Um Klone (Kopien) von Objekten zu erstellen, können Sie den Befehl Object.assign() :

 let book = Object.freeze({ title : "JavaScript Allongé", author : "Reginald Braithwaite" }); let clone = Object.assign({}, book); 

Dieser Befehl führt ein flaches Kopieren von Objekten durch, dh er kopiert nur Eigenschaften der obersten Ebene. Verschachtelte Objekte sind für Originalobjekte und ihre Kopien üblich.

Objektliteral


Objektliterale bieten Entwicklern eine einfache und unkomplizierte Möglichkeit, Objekte zu erstellen:

 let timer = { fn : null, start : function(callback) { this.fn = callback; }, stop : function() {}, } 

Diese Methode zum Erstellen von Objekten hat jedoch Nachteile. Insbesondere sind bei diesem Ansatz alle Eigenschaften des Objekts öffentlich verfügbar, die Methoden des Objekts können neu definiert werden und sie können nicht zum Erstellen neuer Instanzen derselben Objekte verwendet werden:

 timer.fn;//null timer.start = function() { console.log("New implementation"); } 

Object.create () -Methode


Die beiden oben genannten Probleme können durch die gemeinsame Verwendung der Methoden Object.create() und Object.freeze() .

Wir wenden diese Technik auf unser vorheriges Beispiel an. Erstellen Sie zunächst einen eingefrorenen Prototyp timerPrototype , der alle Methoden enthält, die von verschiedenen Instanzen des Objekts benötigt werden. Erstellen Sie anschließend ein Objekt, das ein Nachfolger von timerPrototype :

 let timerPrototype = Object.freeze({ start : function() {}, stop : function() {} }); let timer = Object.create(timerPrototype); timer.__proto__ === timerPrototype; //true 

Wenn der Prototyp vor Änderungen geschützt ist, kann das Objekt, das sein Erbe ist, die im Prototyp definierten Eigenschaften nicht ändern. Jetzt können die Methoden start() und stop() nicht überschrieben werden:

 "use strict"; timer.start = function() { console.log("New implementation"); } //: Cannot assign to read only property 'start' of object 

Mit dem Object.create(timerPrototype) können mehrere Objekte mit demselben Prototyp erstellt werden.

Konstruktorfunktion


JavaScript verfügt über sogenannte Konstruktorfunktionen, die "syntaktischer Zucker" sind, um die oben genannten Schritte zum Erstellen neuer Objekte auszuführen. Betrachten Sie ein Beispiel :

 function Timer(callback){ this.fn = callback; } Timer.prototype = { start : function() {}, stop : function() {} } function getTodos() {} let timer = new Timer(getTodos); 

Sie können jede Funktion als Konstruktor verwenden. Der Konstruktor wird mit dem new Schlüsselwort aufgerufen. Ein Objekt, das mit einer Konstruktorfunktion namens FunctionConstructor , erhält einen Prototyp FunctionConstructor.prototype :

 let timer = new Timer(); timer.__proto__ === Timer.prototype; 

Um eine Änderung des Prototyps zu verhindern, können Sie den Prototyp erneut einfrieren:

 Timer.prototype = Object.freeze({ start : function() {}, stop : function() {} }); 

▍ Stichwort neu


Wenn ein Befehl des Formulars new Timer() ausgeführt wird, werden dieselben Aktionen ausgeführt wie die Funktion newTimer() :

 function newTimer(){ let newObj = Object.create(Timer.prototype); let returnObj = Timer.call(newObj, arguments); if(returnObj) return returnObj;   return newObj; } 

Hier wird ein neues Objekt erstellt, dessen Prototyp Timer.prototype . Anschließend wird die Timer Funktion aufgerufen, mit der die Felder für das neue Objekt festgelegt werden.

Klassenschlüsselwort


Mit ECMAScript 2015 wurde eine neue Methode zur Durchführung der oben genannten Aktionen eingeführt, bei der es sich um eine weitere Charge „syntaktischen Zuckers“ handelt. Wir sprechen über das class und die damit verbundenen zugehörigen Konstrukte. Betrachten Sie ein Beispiel :

 class Timer{ constructor(callback){   this.fn = callback; } start() {} stop() {} } Object.freeze(Timer.prototype); 

Ein Objekt, das mit dem Schlüsselwort class basierend auf einer Klasse namens ClassName , hat den Prototyp ClassName.prototype . Verwenden Sie beim Erstellen eines Objekts basierend auf einer Klasse das new Schlüsselwort:

 let timer= new Timer(); timer.__proto__ === Timer.prototype; 

Die Verwendung von Klassen macht Prototypen nicht unveränderlich. Falls erforderlich, müssen sie auf die gleiche Weise „eingefroren“ werden, wie wir es bereits getan haben:

 Object.freeze(Timer.prototype); 

Prototypbasierte Vererbung


In JavaScript erben Objekte Eigenschaften und Methoden von anderen Objekten. Konstruktorfunktionen und -klassen sind „syntaktischer Zucker“ zum Erstellen von Prototypobjekten, die alle erforderlichen Methoden enthalten. Mit ihnen werden neue Objekte erstellt, die die Erben des Prototyps sind, deren Eigenschaften für eine bestimmte Instanz mithilfe der Konstruktorfunktion oder mithilfe der Klassenmechanismen festgelegt werden.

Es wäre schön, wenn Konstruktorfunktionen und -klassen Prototypen automatisch unveränderlich machen könnten.

Die Stärken der Prototypvererbung sind Speichereinsparungen. Tatsache ist, dass ein Prototyp nur einmal erstellt wird, wonach alle auf seiner Basis erstellten Objekte ihn verwenden.

▍ Das Problem des Fehlens integrierter Verkapselungsmechanismen


Die Prototyp-Vererbungsvorlage verwendet nicht die Trennung der Eigenschaften von Objekten in private und öffentliche. Alle Eigenschaften von Objekten sind öffentlich verfügbar.

Beispielsweise gibt der Befehl Object.keys() ein Array zurück, das alle Eigenschaftsschlüssel des Objekts enthält. Es kann verwendet werden, um alle Eigenschaften eines Objekts zu durchlaufen:

 function logProperty(name){ console.log(name); //  console.log(obj[name]); //  } Object.keys(obj).forEach(logProperty); 

Es gibt ein Muster, das private Eigenschaften nachahmt und sich auf die Tatsache stützt, dass Entwickler nicht auf Eigenschaften zugreifen, deren Namen mit einem Unterstrich ( _ ) beginnen:

 class Timer{ constructor(callback){   this._fn = callback;   this._timerId = 0; } } 

Werksfunktionen


Eingekapselte Objekte in JavaScript können mithilfe von Factory-Funktionen erstellt werden. Es sieht so aus:

 function TodoStore(callback){   let fn = callback;     function start() {},   function stop() {}     return Object.freeze({      start,      stop   }); } 

Hier ist die Variable fn privat. Nur die Methoden start() und stop() sind öffentlich verfügbar. Diese Methoden können nicht extern geändert werden. Das Schlüsselwort this wird hier nicht verwendet. Wenn Sie diese Methode zum Erstellen von Objekten verwenden, ist das Problem des Verlusts this Kontexts irrelevant.

Der Befehl return verwendet ein Objektliteral, das nur Funktionen enthält. Darüber hinaus werden diese Funktionen zum Abschluss erklärt, sie haben einen gemeinsamen Zustand. Um eine öffentliche API eines Objekts einzufrieren, wird der bereits bekannte Befehl Object.freeze() .

Hier haben wir in den Beispielen das Timer Objekt verwendet. In diesem Material finden Sie die vollständige Implementierung.

Zusammenfassung


In JavaScript werden die Werte primitiver Typen, gewöhnlicher Objekte und Funktionen als Objekte behandelt. Objekte sind dynamischer Natur und können als assoziative Arrays verwendet werden. Objekte sind Erben anderer Objekte. Konstruktorfunktionen und -klassen sind „syntaktischer Zucker“, mit dem Sie Objekte basierend auf Prototypen erstellen können. Sie können die Object.create() -Methode verwenden, um die Einzelvererbung zu organisieren, und Object.create() um die Mehrfachvererbung zu organisieren. Sie können Factory-Funktionen verwenden, um gekapselte Objekte zu erstellen.

Liebe Leser! Wenn Sie aus anderen Sprachen zu JavaScript gekommen sind, teilen Sie uns bitte mit, was Sie an JS-Objekten mögen oder nicht mögen, im Vergleich zur Implementierung von Objekten in Sprachen, die Sie bereits kennen.

Source: https://habr.com/ru/post/de420615/


All Articles