Was steht darin geschrieben? Hinter den Kulissen von JavaScript-Objekten

JavaScript ist eine Multi-Paradigmen-Sprache, die objektorientierte Programmierung und dynamische Methodenbindung unterstützt - ein leistungsstarkes Konzept, mit dem sich die Struktur von JavaScript-Code während der Programmausführung ändern kann. Dies bietet Entwicklern ernsthafte Möglichkeiten, macht die Sprache flexibel, aber Sie müssen für alles bezahlen. In diesem Fall müssen Sie mit der Klarheit des Codes bezahlen. Ein wesentlicher Beitrag zu diesem Preis leistet das this , bei dessen Verhalten viele Dinge gesammelt wurden, die den Programmierer verwirren können.



Dynamische Methodenbindung


Mit der dynamischen Bindung können Sie während der Programmausführung und nicht während der Kompilierung die Methode angeben, die beim Ausführen eines bestimmten Befehls aufgerufen werden muss. In JavaScript wird dieser Mechanismus mithilfe des this und der Prototypenkette implementiert. Insbesondere wird der spezifische Wert innerhalb der Methode zur Laufzeit bestimmt, und die Regeln zum Bestimmen dieses Werts variieren in Abhängigkeit davon, wie die Methode deklariert wurde.

Lass uns ein Spiel spielen. Ich nenne es "Was steht darin geschrieben?". Hier ist ihre erste Option - ES6-Modulcode:

 const a = { a: 'a' }; const obj = { getThis: () => this, getThis2 () {   return this; } }; obj.getThis3 = obj.getThis.bind(obj); obj.getThis4 = obj.getThis2.bind(obj); const answers = [ obj.getThis(), obj.getThis.call(a), obj.getThis2(), obj.getThis2.call(a), obj.getThis3(), obj.getThis3.call(a), obj.getThis4(), obj.getThis4.call(a) ]; 

Überlegen Sie sich vor dem weiteren Lesen, was in das Antwortarray fällt, und schreiben Sie die Antworten auf. Testen Sie sich anschließend selbst, indem Sie answers console.log() mit console.log() . Haben Sie es geschafft, den Wert in jedem Fall korrekt zu "entschlüsseln"?

Wir werden dieses Problem beginnend mit dem ersten Beispiel analysieren. Das Konstrukt obj.getThis() gibt undefined . Warum? Diese Pfeilfunktion kann nicht gebunden werden. Solche Funktionen verwenden this aus ihrem umgebenden lexikalischen Bereich. Die Methode wird im ES6-Modul aufgerufen, in ihrem lexikalischen Bereich ist this undefined . Aus dem gleichen Grund gibt undefined einen Aufruf von obj.getThis.call(a) . Der Wert für die Arbeit mit .call() kann auch mit .call() oder .bind() nicht neu zugewiesen werden. Dieser Wert entspricht immer dem aus dem lexikalischen Bereich, in dem sich solche Funktionen befinden.

Der Befehl obj.getThis2() zeigt, wie Sie damit arbeiten, wenn Sie normale Objektmethoden verwenden. Wenn this nicht an eine ähnliche Methode gebunden ist und diese Methode keine Pfeilfunktion ist, dh this Bindung unterstützt, wird das Schlüsselwort this an das Objekt gebunden, für das die Methode mithilfe der Syntax für den Zugriff auf die Eigenschaften des Objekts aufgerufen wird Punkt oder mit eckigen Klammern.

Das obj.getThis2.call(a) ist bereits etwas schwieriger herauszufinden. Mit der call() -Methode können Sie eine Funktion mit einem bestimmten Wert aufrufen, der als optionales Argument angegeben wird. Mit anderen Worten, in diesem Fall wird this dem Parameter .call() entnommen, .call() der Aufruf von obj.getThis2.call(a) das Objekt a zurückgibt.

Verwenden des Befehls obj.getThis3 = obj.getThis.bind(obj); Wir versuchen, an this Methode zu binden, bei der es sich um eine Pfeilfunktion handelt. Wie wir bereits herausgefunden haben, ist dies nicht möglich. Infolgedessen geben Aufrufe von obj.getThis3() und obj.getThis3.call(a) undefined .

Methoden, die gewöhnliche Funktionen sind, können daran angehängt werden, sodass obj.getThis4() erwartungsgemäß obj . Ein Aufruf von obj.getThis4.call(a) gibt obj und nicht, wie zu erwarten, a . Tatsache ist, dass wir this vor dem Aufrufen dieses Befehls bereits mit dem obj.getThis4 = obj.getThis2.bind(obj); . Infolgedessen wird bei der Ausführung von obj.getThis4.call(a) der Status der Methode berücksichtigt, in der sie sich nach Berücksichtigung der ersten Bindung befand.

Verwenden Sie dies in Klassen


Hier ist die zweite Version unseres Spiels - dieselbe Aufgabe, aber jetzt basierend auf Klassen. Hier verwenden wir die Syntax zum Deklarieren von Feldern für öffentliche Klassen (im Moment befindet sich der Vorschlag für diese Syntax in der dritten Genehmigungsphase. Er ist standardmäßig in Chrome verfügbar. Sie können ihn mit den @babel/plugin-proposal-class-properties )

 class Obj { getThis = () => this getThis2 () {   return this; } } const obj2 = new Obj(); obj2.getThis3 = obj2.getThis.bind(obj2); obj2.getThis4 = obj2.getThis2.bind(obj2); const answers2 = [ obj2.getThis(), obj2.getThis.call(a), obj2.getThis2(), obj2.getThis2.call(a), obj2.getThis3(), obj2.getThis3.call(a), obj2.getThis4(), obj2.getThis4.call(a) ]; 

Bevor Sie answers2 , denken Sie über den Code nach und schreiben Sie Ihre Vision auf, was in das Array answers2 wird.

Bist du fertig

Hier geben alle Methodenaufrufe mit Ausnahme von obj2.getThis2.call(a) einen Verweis auf die Instanz des Objekts zurück. Der gleiche Aufruf gibt das Objekt a . Pfeilfunktionen übernehmen this immer noch aus dem lexikalischen Bereich. Der Unterschied zwischen diesem und dem vorherigen Beispiel ist der Unterschied im Umfang, aus dem this entnommen wird.

Hier arbeiten wir nämlich mit Klasseneigenschaften, die das Verhalten dieses Codes bestimmen.

Tatsache ist, dass während der Vorbereitung des Codes für die Ausführung die Werte wie folgt in die Eigenschaften der Klassen geschrieben werden:

 class Obj { constructor() {   this.getThis = () => this; } ... 

Mit anderen Worten, es stellt sich heraus, dass die Pfeilfunktion im Kontext der Konstruktorfunktion deklariert ist. Da wir mit einer Klasse arbeiten, können Sie sie nur instanziieren, indem Sie das new Schlüsselwort verwenden (wenn Sie dieses Schlüsselwort vergessen, wird eine Fehlermeldung angezeigt).

Die wichtigsten Aufgaben, die mit dem new Schlüsselwort gelöst werden, bestehen darin, eine neue Instanz des Objekts zu erstellen und this an den Konstruktor zu binden. Diese Funktion soll Ihnen helfen, zu verstehen, was gerade passiert, wenn Sie berücksichtigen, worüber wir bereits im vorherigen Abschnitt gesprochen haben.

Zusammenfassung


Haben Sie die in diesem Artikel beschriebenen Aufgaben erledigt? Wenn Sie genau wissen, wie sich this Schlüsselwort in JavaScript verhält, sparen Sie beim Debuggen eine Menge Zeit, wenn Sie nach nicht offensichtlichen Gründen für unklare Fehler suchen. Wenn Sie einige der Fragen falsch beantwortet haben, bedeutet dies, dass Sie üben können.

Experimentieren Sie mit dem Beispielcode und versuchen Sie es dann erneut usw., bis Sie alle Fragen richtig beantworten können. Nachdem Sie es selbst herausgefunden haben, finden Sie jemanden, der bereit ist, Ihnen zuzuhören, und erklären Sie ihm, warum die Methoden aus den Aufgaben genau das zurückgeben, was sie zurückgeben.

Wenn Ihnen das alles komplizierter erscheint als erwartet, dann wissen Sie, dass Sie damit nicht allein sind. Ich habe einige Entwickler auf ihr Wissen getestet, und ich denke, dass nur einer von ihnen in all ihren Antworten absolut korrekt war.

Dieses Subsystem der Sprache, das am Anfang wie eine dynamische Suche nach Methoden aussah, die mit .call() , .bind() oder .apply() beeinflusst werden .call() , .bind() nach dem Auftreten von .apply() und -klassen viel komplizierter aus.

Anscheinend wird es nützlich sein, die Hauptmerkmale von Klassen und Pfeilfunktionen zu beachten, um this . Denken Sie daran, dass Pfeilfunktionen dies immer aus ihrem lexikalischen Bereich verwenden und das this in Klassen tatsächlich an die Konstruktorfunktion der Klasse gebunden ist. Und wenn Sie jemals das Gefühl haben, nicht genau zu wissen, worauf this hindeutet, verwenden Sie den Debugger, um Ihre diesbezüglichen Annahmen zu überprüfen.

Denken Sie auch daran, dass Sie in JavaScript viel tun können, ohne this in Ihrem Code zu verwenden. Die Erfahrung zeigt, dass fast jeder JS-Code in Form von reinen Funktionen umgeschrieben werden kann, die alle Argumente akzeptieren, mit denen sie arbeiten, und zwar in Form einer explizit angegebenen Liste von Parametern ( this kann als implizit angegebener Parameter mit einem veränderlichen Status interpretiert werden). Die in reinen Funktionen enthaltene Logik ist deterministisch, was ihre Testbarkeit verbessert. Solche Funktionen haben keine Nebenwirkungen, was bedeutet, dass Sie bei der Arbeit mit ihnen im Gegensatz zur Manipulation wahrscheinlich nichts außerhalb davon „brechen“. Wann immer Sie dies ändern, werden Sie mit einem potenziellen Problem konfrontiert, nämlich dass etwas, das davon abhängt, möglicherweise nicht mehr richtig funktioniert.

Trotz alledem sollte beachtet werden, dass this ein nützliches Konzept ist. Beispielsweise kann es angewendet werden, um die gemeinsame Nutzung einer bestimmten Methode durch eine Vielzahl von Objekten zu organisieren. Selbst in der funktionalen Programmierung kann this nützlich sein, um andere Methoden von einer Methode eines Objekts aus aufzurufen, sodass Sie basierend auf vorhandenen Konstrukten etwas Neues erstellen können.



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


All Articles