Heim > Web-Frontend > js-Tutorial > Vertiefendes Verständnis der JavaScript-Reihe (19): Detaillierte Erläuterung der Bewertungsstrategie_Grundkenntnisse

Vertiefendes Verständnis der JavaScript-Reihe (19): Detaillierte Erläuterung der Bewertungsstrategie_Grundkenntnisse

WBOY
Freigeben: 2016-05-16 16:11:09
Original
962 Leute haben es durchsucht

Einführung

In diesem Kapitel erklären wir die Strategie der Parameterübergabe an Funktionen in ECMAScript.

In der Informatik wird diese Strategie im Allgemeinen als „Bewertungsstrategie“ bezeichnet (Anmerkung des Onkels: Einige Leute sagen, dass sie als Bewertungsstrategie übersetzt wird, und andere übersetzen sie als Zuweisungsstrategie. Wenn ich mir den folgenden Inhalt ansehe, denke ich, dass sie so heißt Zuweisungsstrategie ist ohnehin angemessener, der Titel sollte als Bewertungsstrategie geschrieben werden, die für jedermann leicht verständlich ist, beispielsweise das Festlegen von Regeln für die Bewertung oder Berechnung von Ausdrücken in einer Programmiersprache. Die Strategie der Übergabe von Argumenten an Funktionen ist ein Sonderfall.

http://dmitrysoshnikov.com/ecmascript/chapter-8-evaluation-strategy/
Der Grund für das Schreiben dieses Artikels ist, dass jemand im Forum nach einer genauen Erklärung einiger Parameterübergabestrategien gefragt hat. Wir haben hier die entsprechenden Definitionen angegeben und hoffen, dass sie für alle hilfreich sind.

Viele Programmierer sind davon überzeugt, dass in JavaScript (und sogar einigen anderen Sprachen) Objekte als Referenz übergeben werden, während primitive Werttypen als Wert übergeben werden. Darüber hinaus erwähnen viele Artikel diese „Tatsache“, aber viele Leute tun dies wirklich Verstehen Sie diese Terminologie und wie viel ist richtig? Wir werden sie in diesem Artikel einzeln erklären.

Allgemeine Theorie

Es ist zu beachten, dass es in der Zuweisungstheorie im Allgemeinen zwei Zuweisungsstrategien gibt: strikt – das bedeutet, dass die Parameter vor der Eingabe des Programms berechnet werden; ist, äquivalent zu verzögerter Berechnung).

Dann betrachten wir hier die grundlegende Strategie zur Übergabe von Funktionsparametern, die vom ECMAScript-Ausgangspunkt aus sehr wichtig ist. Als Erstes ist zu beachten, dass ECMAScript (und sogar andere Sprachen wie C, JAVA, Python und Ruby) eine strikte Parameterübergabestrategie verwendet.

Darüber hinaus ist auch die Berechnungsreihenfolge der übergebenen Parameter sehr wichtig – in ECMAScript erfolgt sie von links nach rechts, und die in anderen Sprachen implementierte Reflexionsreihenfolge (von rechts) kann ebenfalls verwendet werden.

Die strikte Parameterübergabestrategie ist außerdem in mehrere Unterstrategien unterteilt, von denen wir die wichtigsten in diesem Kapitel ausführlich besprechen.

Nicht alle unten besprochenen Strategien werden in ECMAScript verwendet. Wenn wir also das spezifische Verhalten dieser Strategien diskutieren, verwenden wir Pseudocode, um sie zu demonstrieren.

Wert übergeben

Wertübergabe, wie viele Entwickler sehr gut wissen. Der Wert eines Parameters ist eine Kopie des vom Aufrufer übergebenen Werts des Parameters Parameter ist in Externer Wert), im Allgemeinen wird neuer Speicher neu zugewiesen (wir achten nicht darauf, wie der zugewiesene Speicher implementiert wird - es handelt sich auch um einen Stapel oder eine dynamische Speicherzuweisung), der Wert des neuen Speicherblocks ist eine Kopie davon das externe Objekt und sein Wert wird innerhalb der Funktion verwendet.

Code kopieren Der Code lautet wie folgt:

Balken = 10

Prozedur foo(barArg):
barArg = 20;
Ende

foo(bar)

// Das Ändern des Werts in foo hat keinen Einfluss auf den Wert des internen Balkens
print(bar) // 10

Wenn es sich bei dem Parameter der Funktion jedoch nicht um einen primitiven Wert, sondern um ein komplexes Strukturobjekt handelt, tritt in C dieses Problem auf: Wenn die Struktur als Wert übergeben wird, handelt es sich um eine vollständige Kopie.

Lassen Sie uns ein allgemeines Beispiel geben und die folgende Zuweisungsstrategie verwenden, um es zu testen. Der erste Parameter ist der Wert des Objekts und der zweite Parameter ist eine boolesche Markierung Das Objekt wurde vollständig geändert (dem Objekt wurde ein Wert neu zugewiesen) oder es wurden nur einige Eigenschaften des Objekts geändert.

Code kopieren Der Code lautet wie folgt:

// Hinweis: Das Folgende ist Pseudocode, keine JS-Implementierung
bar = {
x: 10,
Jahr: 20
}

Prozedur foo(barArg, isFullChange):

if isFullChange:
barArg = {z: 1, q: 2}
beenden
Ende

barArg.x = 100
barArg.y = 200

Ende

foo(bar)

// Wert übergeben, externe Objekte werden nicht verändert
print(bar) // {x: 10, y: 20}

// Das Objekt vollständig ändern (neuen Wert zuweisen)
foo(bar, true)

//Auch keine Änderung
print(bar) // {x: 10, y: 20}, statt {z: 1, q: 2}

Übergabe als Referenz

Eine andere bekannte Methode zur Referenzübergabe erhält keine Kopie des Werts, sondern eine implizite Referenz auf das Objekt, beispielsweise die direkte externe Referenzadresse des Objekts. Jede Änderung der Parameter innerhalb der Funktion wirkt sich auf den Wert des Objekts außerhalb der Funktion aus, da sich beide auf dasselbe Objekt beziehen, das heißt: Zu diesem Zeitpunkt entspricht der Parameter einem Alias ​​​​des externen Objekts.

Pseudocode:

Code kopieren Der Code lautet wie folgt:

Prozedur foo(barArg, isFullChange):

if isFullChange:
barArg = {z: 1, q: 2}
beenden
Ende

barArg.x = 100
barArg.y = 200

Ende

//Verwenden Sie dasselbe Objekt wie oben
bar = {
x: 10,
Jahr: 20
}

// Das Ergebnis des Aufrufs per Referenz ist wie folgt:
foo(bar)

// Der Attributwert des Objekts wurde geändert
print(bar) // {x: 100, y: 200}

// Die Neuzuweisung neuer Werte wirkt sich auch auf das Objekt aus
foo(bar, true)

// Das Objekt ist jetzt ein neues Objekt
print(bar) // {z: 1, q: 2}

Diese Strategie ermöglicht eine effizientere Bereitstellung komplexer Objekte, beispielsweise großer Strukturobjekte mit großen Eigenschaftenmengen.

Anrufen durch Teilen
Jeder kennt die beiden oben genannten Strategien, aber die Strategie, über die ich hier sprechen möchte, wird möglicherweise nicht von jedem gut verstanden (eigentlich handelt es sich um eine akademische Strategie). Wir werden jedoch bald sehen, dass genau dies die Strategie ist, bei der es bei Parameterübergabestrategien in ECMAScript eine Schlüsselrolle spielt.

Es gibt auch einige Synonyme für diese Strategie: „Pass by Object“ oder „Pass by Object Sharing“.

Diese Strategie wurde 1974 von Barbara Liskov für die Programmiersprache CLU vorgeschlagen.

Der Kernpunkt dieser Strategie ist: Die Funktion erhält eine Kopie (Kopie) des Objekts, und die Referenzkopie wird mit den formalen Parametern und ihren Werten verknüpft.

Wir können die hier erscheinende Referenz nicht als „Referenz übergeben“ bezeichnen, da der von der Funktion empfangene Parameter kein direkter Objektalias, sondern eine Kopie der Referenzadresse ist.

Der wichtigste Unterschied besteht darin, dass die Neuzuweisung eines neuen Werts zum Parameter innerhalb der Funktion keine Auswirkungen auf das externe Objekt hat (ähnlich wie bei der Referenzübergabe im obigen Beispiel), sondern dass der Parameter eine Adresskopie ist Es kann nicht von außen und von innen auf dasselbe Objekt zugegriffen werden (z. B. ist das externe Objekt keine vollständige Kopie wie bei der Übergabe von Werten), und eine Änderung des Attributwerts des Parameterobjekts wirkt sich auf das externe Objekt aus.

Code kopieren Der Code lautet wie folgt:

Prozedur foo(barArg, isFullChange):

if isFullChange:
barArg = {z: 1, q: 2}
beenden
Ende

barArg.x = 100
barArg.y = 200

Ende

//Verwenden Sie weiterhin diese Objektstruktur
bar = {
x: 10,
Jahr: 20
}

// Die Übergabe des Beitrags wirkt sich auf das Objekt
aus foo(bar)

// Die Eigenschaften des Objekts wurden geändert
print(bar) // {x: 100, y: 200}

// Neuzuweisung hat keine Auswirkung
foo(bar, true)

// Immer noch der obige Wert
print(bar) // {x: 100, y: 200}


Bei dieser Verarbeitung wird davon ausgegangen, dass in den meisten Sprachen Objekte und keine primitiven Werte verwendet werden.

Pass-by-Share ist ein Sonderfall von Pass-by-Value

Die Pass-by-Share-Strategie wird in vielen Sprachen verwendet: Java, ECMAScript, Python, Ruby, Visual Basic usw. Darüber hinaus hat die Python-Community diese Terminologie übernommen, und andere Sprachen können diese Terminologie ebenfalls verwenden, da andere Namen dazu neigen, Verwirrung zu stiften. In den meisten Fällen, etwa in Java, ECMAScript oder Visual Basic, wird diese Strategie auch Pass-by-Value genannt – was bedeutet: spezielle Wertreferenzkopie.

Einerseits ist es so: Der an die Funktion übergebene Parameter ist nur ein Name des gebundenen Werts (Referenzadresse) und hat keinen Einfluss auf das externe Objekt.

Andererseits werden diese Begriffe wirklich als falsch angesehen, ohne näher darauf einzugehen, da in vielen Foren darüber gesprochen wird, wie man Objekte an JavaScript-Funktionen übergibt.

Die allgemeine Theorie besagt zwar, dass er als Wert übergeben wird: Derzeit ist der Wert jedoch das, was wir als Adresskopie (Kopie) bezeichnen, sodass er nicht gegen die Regeln verstößt.

In Ruby wird diese Strategie als Referenzübergabe bezeichnet. Nochmals: Es wird nicht als Kopie der großen Struktur übergeben (d. h. nicht als Wert übergeben), und andererseits haben wir es nicht mit einem Verweis auf das Originalobjekt zu tun und können es daher nicht ändern, dieses Kreuz Das Begriffskonzept kann zu weiteren Problemen führen.

Theoretisch gibt es keinen Sonderfall der Referenzübergabe wie den Sonderfall der Wertübergabe.

Dennoch muss man verstehen, dass bei den oben genannten Technologien (Java, ECMAScript, Python, Ruby usw.) tatsächlich die Strategie „Pass-by-Share“ verwendet wird.

Press Share & Pointer

Für С/С ist diese Strategie ideologisch dieselbe wie die Übergabe eines Zeigers nach einem Wert, jedoch mit einem wichtigen Unterschied: Diese Strategie kann den Zeiger dereferenzieren und das Objekt vollständig mutieren. Im Allgemeinen wird jedoch ein Wertzeiger (Adresszeiger) einem neuen Speicherblock zugewiesen (d. h. der zuvor referenzierte Speicherblock bleibt unverändert); eine Änderung der Objektattribute über den Zeiger wirkt sich auf das externe Adon-Objekt aus.

Also und bei der Zeigerkategorie können wir offensichtlich erkennen, dass diese als Adresswert übergeben wird. In diesem Fall handelt es sich bei Pass-by-Share nur um „syntaktischen Zucker“, der sich wie eine Zeigerzuweisung verhält (aber nicht dereferenzieren kann) oder eine Eigenschaft wie eine Referenz ändert (keine Dereferenzierungsoperation erforderlich). Manchmal kann er auch als „Sichere Zeiger“ bezeichnet werden ".

Allerdings verfügt С/С auch über einen speziellen Syntaxzucker, wenn auf Objekteigenschaften ohne explizite Zeiger-Dereferenzierung verwiesen wird:

Code kopieren Der Code lautet wie folgt:

obj->x statt (*obj).x

Diese Ideologie ist am engsten mit C verbunden und zeigt sich beispielsweise in der Implementierung von „Smart Pointern“ in boost::shared_ptr, die den Zuweisungsoperator und den Kopierkonstruktor überladen und auch den Referenzzähler des Objekts verwenden, um Objekte zu löschen GC. Dieser Datentyp hat sogar einen ähnlichen Namen – shared_ptr.

ECMAScript-Implementierung

Jetzt kennen wir die Strategie der Übergabe von Objekten als Parameter in ECMAScript – Übergabe durch gemeinsame Nutzung: Das Ändern der Eigenschaften des Parameters wirkt sich auf die Außenseite aus, eine Neuzuweisung hat jedoch keine Auswirkungen auf das externe Objekt. Wie wir oben erwähnt haben, nennen ECMAScript-Entwickler es jedoch im Allgemeinen: Wertübergabe, mit der Ausnahme, dass der Wert eine Kopie der Referenzadresse ist.

JavaScript-Erfinder Brendan Ash schrieb auch: Was übergeben wird, ist eine Kopie der Referenz (Kopie der Adresse). Was also alle im Forum einmal über die Wertübergabe gesagt haben, trifft auch unter dieser Erklärung zu.

Genauer gesagt kann dieses Verhalten als einfache Zuweisung verstanden werden. Wir können sehen, dass sich darin ein völlig anderes Objekt befindet, das sich jedoch auf denselben Wert bezieht – also auf eine Kopie der Adresse.

ECMAScript-Code:

Code kopieren Der Code lautet wie folgt:

var foo = {x: 10, y: 20};
var bar = foo;

alarm(bar === foo); // true

bar.x = 100;
bar.y = 200;

alarm([foo.x, foo.y]); // [100, 200]

Das heißt, zwei Bezeichner (Namensbindungen) sind an dasselbe Objekt im Speicher gebunden und teilen sich dieses Objekt:

foo-Wert: addr(0xFF) => {x: 100, y: 200} (Adresse 0xFF) <= bar-Wert: addr(0xFF)
Bei der Neuzuweisung erfolgt die Bindung an einen neuen Objektbezeichner (neue Adresse), ohne dass sich dies auf zuvor gebundene Objekte auswirkt:

Code kopieren Der Code lautet wie folgt:

bar = {z: 1, q: 2};

alarm([foo.x, foo.y]); // [100, 200] – unverändert
Alert([bar.z, bar.q]); // [1, 2] – aber jetzt referenziert ein neues Objekt

Das heißt, jetzt haben foo und bar unterschiedliche Werte und unterschiedliche Adressen:
Code kopieren Der Code lautet wie folgt:

foo-Wert: addr(0xFF) => {x: 100, y: 200} (Adresse 0xFF)
Balkenwert: addr(0xFA) => {z: 1, q: 2} (Adresse 0xFA)

Lassen Sie mich noch einmal betonen, dass der Wert des hier erwähnten Objekts die Adresse (Adresse) ist, nicht die Objektstruktur selbst. Das Zuweisen einer Variablen zu einer anderen Variablen ist ein Verweis auf den zugewiesenen Wert. Daher beziehen sich beide Variablen auf dieselbe Speicheradresse. Die nächste Zuweisung ist eine neue Adresse, die die Adressbindung an das alte Objekt auflöst und es dann an die Adresse des neuen Objekts bindet. Dies ist der wichtigste Unterschied zur Übergabe als Referenz.

Wenn wir außerdem nur die vom ECMA-262-Standard bereitgestellte Abstraktionsebene berücksichtigen, sehen wir im Algorithmus nur das Konzept des „Werts“ und den von der Implementierung übergebenen „Wert“ (kann ein primitiver Wert sein). oder ein Objekt), aber gemäß unserer obigen Definition kann es auch als „Wertübergabe“ bezeichnet werden, da die Referenzadresse auch ein Wert ist.

Um jedoch Missverständnisse zu vermeiden (warum die Eigenschaften externer Objekte innerhalb der Funktion geändert werden können), müssen hier noch Details auf Implementierungsebene berücksichtigt werden – was wir als Passing-by-Sharing oder anders betrachten Wörter - Übergeben Sie einen sicheren Zeiger, und es ist für einen sicheren Zeiger unmöglich, das Objekt zu dereferenzieren und zu ändern, aber er kann den Attributwert des Objekts ändern.

Termversion

Lassen Sie uns die Begriffsversion dieser Strategie in ECMAScript definieren.

Man kann es als „Wertübergabe“ bezeichnen – der hier erwähnte Wert ist ein Sonderfall, das heißt, der Wert ist eine Adresskopie. Von dieser Ebene aus können wir sagen: Alle Objekte in ECMAScript mit Ausnahme von Ausnahmen werden als Wert übergeben. Dies ist tatsächlich die abstrakte Ebene von ECMAScript.

Oder in diesem Fall wird es speziell als „Pass-by-Share“ bezeichnet. Daran können Sie den Unterschied zwischen der traditionellen Pass-by-Value und der Pass-by-Reference erkennen. Diese Situation kann in zwei Situationen unterteilt werden: 1: Originalwerte ​werden als Wert übergeben; 2: Objekte werden durch Teilen übergeben.

Der Satz „Konvertieren eines Objekts in eine Funktion anhand des Referenztyps“ hat nichts mit ECMAScript zu tun und ist falsch.

Fazit

Ich hoffe, dieser Artikel hilft dabei, mehr Details zum Gesamtbild und seiner Implementierung in ECMAScript zu verstehen. Wenn Sie Fragen haben, können Sie diese wie immer gerne besprechen.

Verwandte Etiketten:
Quelle:php.cn
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage