Die Erklärung sagt, Array und Einträge seien neu; Änderungen am Ergebnis sollten die Eingabe also unberührt lassen. Doch wenn der Aufrufer ein Label in Großbuchstaben umwandelt, ändert sich auch das Original. Die flache Kopie hat den Verweis auf das verschachtelte Objekt beibehalten.
Laden Sie das Paketvorschau-Übungsprojekt herunter. Code, Daten und die absichtlich falsche Erklärung wurden eigens für dieses Tutorial erstellt. Verwenden Sie Node.js 22 oder neuer. Installation, Zugangsdaten und Netzwerkzugriff sind nicht erforderlich.
Aussage und Quelle nebeneinander halten
Prüfen Sie, ob eine Änderung am Label eines kopierten Pakets auch die Eingabe verändert. Lesen Sie Kopierfunktion und Aufrufer zusammen.
Öffnen Sie den entpackten Ordner parcel-preview-lab in Yenpo. Öffnen Sie mit Command-P (Zur Datei…) claims.md, src/copy-parcels.mjs und src/preview.mjs in Tabs. Vergleichen Sie die Aussage sowohl mit der Kopierfunktion als auch mit ihrem Aufrufer. Über Alle Dateien durchsuchen finden Sie row.label.text; prüfen Sie die umgebende Bedingung uppercase, statt einen Treffer als Beweis für eine Mutation bei jedem Aufruf zu betrachten.
Für diesen Leseschritt brauchen Sie weder Modellverbindung noch Sprachserver. Laden Sie Yenpo für Mac herunter, um dasselbe Beispiel zu öffnen, oder nutzen Sie die Hilfe zur Datei- und Textsuche, wenn Sie die Orientierung verlieren. Führen Sie Beobachter und Prüfungen unten selbst im Terminal aus und halten Sie die Belege fest.
Die Erklärung in Aussagen zerlegen
Diese Lehrbeispiel-Erklärung werden wir prüfen:
copyParcelserzeugt ein neues Array und für jedes Paket ein neues Objekt. Die Etiketten sind unabhängige Kopien, deshalb kannpreviewsie großschreiben, ohne die Eingabe zu ändern.
Der erste Satz enthält zwei Aussagen. Der zweite ergänzt eine Aussage über Besitzverhältnisse und eine Verhaltensfolgerung. Würde man den Absatz insgesamt als „richtig oder falsch“ bewerten, gingen die nützlichen Teile zusammen mit dem Fehler verloren.
| ID | Aussage | Was würde sie belegen oder widerlegen? |
|---|---|---|
C1 |
Das zurückgegebene Array ist neu. | Identitäten von Eingabe- und Ausgabearray vergleichen. |
C2 |
Jedes äußere Paketobjekt ist neu. | Identitäten der jeweiligen Pakete vergleichen. |
C3 |
Jedes verschachtelte Etikett ist unabhängig. | Etikettidentitäten vergleichen, nicht nur den Text. |
C4 |
Großschreibung in der Vorschau lässt die Eingabe unverändert. | Den Zweig ausführen und danach die Eingabe des Aufrufers prüfen. |
C5 |
Jede Eingabemutation ist ein fachlicher Fehler. | Eine Anforderung oder einen Besitzvertrag finden; Code allein liefert beides nicht. |
C5 ist eine mögliche Ergänzung durch den Leser, kein Teil der zitierten Erklärung. Halten Sie sie getrennt, damit die Widerlegung von C4 nicht stillschweigend die Produktanforderung festlegt.
Unser Geltungsbereich ist ein dichtes Array gewöhnlicher veränderbarer Paketobjekte, jeweils mit einem einfachen label-Objekt und einer Zeichenfolge text. Getter, Proxies, Klasseninstanzen und externe Ein-/Ausgabe sind nicht abgedeckt. Halten Sie diesen Rahmen bei der Erklärung fest, statt den Lauf auf alle JavaScript-Werte zu verallgemeinern.
Die Referenz über die Funktionsgrenze verfolgen
Öffnen Sie src/copy-parcels.mjs:
export function copyParcels(parcels) {
return parcels.map((parcel) => ({ ...parcel }));
}
Das Objektliteral erzeugt bei jedem Callback-Aufruf ein neues äußeres Objekt. Es kopiert den Wert der Eigenschaft label, also eine Objektreferenz. Ein neues verschachteltes Etikett entsteht nicht. MDN beschreibt diesen Unterschied unter Spread-Syntax und flache Kopie.
Öffnen Sie nun den tatsächlichen Aufrufer src/preview.mjs:
import { copyParcels } from './copy-parcels.mjs';
export function preview(parcels, { uppercase = false } = {}) {
const rows = copyParcels(parcels);
if (uppercase) {
for (const row of rows) {
row.label.text = row.label.text.toUpperCase();
}
}
return rows;
}
Entscheidend ist die Zuweisung an row.label.text. Eine Neuzuweisung von row.id würde das neue äußere Objekt betreffen; der Zugriff über row.label erreicht das gemeinsame verschachtelte Objekt. Für die Fixture sieht die Referenzstruktur direkt nach dem Kopieren so aus:
input ──> original array ──> original parcel ──┐
├──> one label object
rows ──> new array ──────> new parcel ────────┘
Diese Zuweisung zu finden erklärt noch keinen konkreten Lauf. Der Standardzweig überspringt sie. Zum Prüfen von C4 übergeben Sie uppercase: true und Text, dessen Großschreibung vom aktuellen Wert abweicht.
Den Zweig ausführen, der die Erklärung widerlegen kann
Entpacken Sie den Download, wechseln Sie nach parcel-preview-lab und führen Sie Folgendes aus:
node observe.mjs
Der Beobachter erstellt für jeden Zweig eine neue Fixture. Vor dem Aufruf von preview speichert er den ursprünglichen Text als Zeichenfolge und gibt anschließend Identitäten und Werte aus. Das sind die Ergebnisse:
| Beobachtung | uppercase: false |
uppercase: true |
|---|---|---|
| Dasselbe Array? | false |
false |
| Dasselbe äußere Paket? | false |
false |
| Dasselbe verschachtelte Etikett? | true |
true |
| Vor dem Aufruf gespeicherter Text | fragile |
fragile |
| Eingabetext nach dem Aufruf | fragile |
FRAGILE |
| Vorschautext nach dem Aufruf | fragile |
FRAGILE |
Das ist ein erreichbares Gegenbeispiel zu C4: Der Aufrufer übergibt die Eingabe, wählt die Großschreibung und kann die Änderung seines eigenen verschachtelten Texts beobachten. C1 und C2 bleiben richtig. C3 wird bereits durch die gemeinsame Etikettidentität widerlegt, auch im Zweig ohne Zuweisung.
Ein bereits großgeschriebenes Etikett ist eine schwächere Probe: Die Zuweisung passiert, aber Vorher- und Nachherwert sind gleich. Ein leeres Array erreicht die Schleife nie. Solche Eingaben können die Prüfung „Text unverändert“ bestehen, ohne unabhängigen Besitz zu belegen. Die Belegtests enthalten beide Kontrollfälle.
Gleiche Werte nicht mit unabhängigen Objekten verwechseln
Vor der Zuweisung besteht diese Assertion:
assert.deepEqual(rows, input);
Das zeigt, dass verglichene Struktur und Werte übereinstimmen. Es zeigt nicht, dass entsprechende verschachtelte Objekte verschiedene Identitäten haben. Diese drei Assertions bestehen gleichzeitig ebenfalls:
assert.notStrictEqual(rows, input);
assert.notStrictEqual(rows[0], input[0]);
assert.strictEqual(rows[0].label, input[0].label);
Das Projekt importiert assert aus node:assert/strict. Die Vergleichssemantik beschreibt Nodes Assertion-Referenz. Behalten Sie beide Belegarten: Identitäten beschreiben geteilte Objekte, Vorher-/Nachherwerte zeigen die Folge einer erreichbaren Zuweisung.
Verwenden Sie const before = input nicht als gespeicherten Vorherzustand. Diese Variable würde weiterhin auf dasselbe Array zeigen und ebenfalls den geänderten Wert anzeigen. Der Beobachter speichert vor dem Aufruf die primitive Zeichenfolge beforeText. Ein festgehaltener Wert und eine Referenz auf ein veränderbares Objekt beantworten verschiedene Fragen.
Aus dem Ergebnis eine korrigierte Erklärung machen
Führen Sie diese Befehle einzeln aus; der erste soll absichtlich fehlschlagen:
node --test checks/original-claims.test.mjs
node --test checks/evidence.test.mjs
Der Lauf mit den ursprünglichen Aussagen hat zwei erfolgreiche und zwei fehlgeschlagene Tests und endet mit einem Fehlercode. C1 und C2 bestehen; unabhängige Etiketten und unveränderte Eingabe werden widerlegt. Der Beleglauf hat sechs erfolgreiche Tests und endet mit null. Nodes Test-Runner-Dokumentation erklärt die Dateiausführung. Zwischen den Läufen wird kein Anwendungscode geändert: Die Assertions formulieren verschiedene Aussagen über dasselbe Programm.
Halten Sie Urteil und Geltungsgrenze fest, nicht nur Grün oder Rot:
| ID | Urteil | Beleg und Grenze |
|---|---|---|
C1, C2 |
Im geprüften Rahmen gestützt | Neues Array und neue äußere Pakete: map, Objektliteral und Identitätsprüfungen stimmen überein. |
C3 |
Widerlegt | Verschachtelte Etiketten sind geteilt: Ein- und Ausgabe halten dieselbe Referenz. |
C4 |
Widerlegt | Großschreibung ändert die Eingabe: Im wahren Zweig wird fragile zu FRAGILE. |
C5 |
Ungeklärt | Ein Anforderungsverstoß ist nicht belegt: Es wurde kein Besitzvertrag vorgelegt. |
Eine korrigierte Erklärung lautet:
copyParcelserzeugt ein neues Array und neue äußere Paketobjekte, behält aber jede verschachtelte Etikettreferenz bei. Mituppercase: falseweistpreviewkeinen Text zu. Mituppercase: trueschreibt es über das gemeinsame Etikett. Bei der Fixturefragilewird die Eingabe des Aufrufers zuFRAGILE. Ob diese Mutation zulässig ist, hängt vom Besitzvertrag ab.
Die ZIP enthält dieses Protokoll in claims.md, den Beobachter, beide Testdateien und ein Datei-Hash-Manifest.
Die Belege in die Leseaufgabe zurückführen
Halten Sie Quellcodeversion, geprüfte Erklärung sowie Ein- und Ausgabe des Tests fest. Fragen Sie dann: „Die beiden Labels teilen ein Objekt, und die Umwandlung in Großbuchstaben ändert das Original. Schreibe die Erklärung entsprechend um.“ Prüfen Sie die neue Antwort am selben Code.
Mit Yenpos KI-Codeleser können Sie lokale Quellen durchsuchen, gezielt fragen und Referenzen öffnen, während Sie Aufrufern folgen. Führen Sie Projekt und eigene Prüfungen im Terminal oder mit Ihren Testwerkzeugen aus; Yenpo führt keine Shell-Befehle oder Tests für Sie aus.
Soll als Nächstes über eine Verhaltensänderung entschieden werden, klären Sie zuerst die Anforderung. Für einen konkreten Patch folgt das Prüfen KI-generierten Codes mit einem Gegenbeispiel. Soll unbekanntes Verhalten vor einem Refactoring festgehalten werden, nutzen Sie Charakterisierungstests.