Schauen Sie sich zuerst den Codeausschnitt an:
var f = function foo(){
return typeof foo; // foo is valid in the internal Scope
};
// foo ist Die externe Verwendung ist unsichtbar
typeof foo; // "undefiniert"
f(); // "function"
Was ich hier sagen möchte ist, dass foo im Funktionsausdruck ist, Es kann nur innerhalb der Funktion referenziert werden, nicht außerhalb.
json
Viele JavaScript-Entwickler bezeichnen JavaScript-Objektliterale fälschlicherweise als JSON-Objekte. JSON soll ein Datenaustauschformat beschreiben. Es verfügt außerdem über eine eigene Syntax, die eine Teilmenge von JavaScript ist.
{ „prop“: „val“ } Eine solche Deklaration kann je nach Kontext, in dem sie verwendet wird, ein JavaScript-Objektliteral oder ein JSON-String sein. Wenn es in einem Zeichenfolgenkontext verwendet wird (in einfachen oder doppelten Anführungszeichen gesetzt oder aus einer Textdatei gelesen), handelt es sich um eine JSON-Zeichenfolge. Wenn es in einem Objektliteralkontext verwendet wird, handelt es sich um ein Objektliteral.
// Dies ist ein JSON-String
var foo = '{ "prop": "val" }';
// Dies ist ein Objektliteral
var bar = { "prop ": "val" };
Eine weitere Sache, die Sie wissen sollten, ist, dass JSON.parse zum Deserialisieren von JSON-Strings in Objekte verwendet wird und JSON.stringify zum Serialisieren von Objekten in JSON-Strings verwendet wird. Ältere Browserversionen unterstützen dieses Objekt nicht, aber Sie können die gleiche Funktionalität über json2.js erreichen.
Prototyp
Funktion Animal (){
// ...
}
Funktion Katze (){
// ...
}
cat.prototype = new Animal();//Diese Methode erbt, was im Konstruktor ist.
cat.prototype = Animal.prototype;//Diese Methode erbt den Konstruktor nicht.
//Ein weiteres wichtiges Detail ist, dass Sie Ihre eigene Prototypenkette pflegen müssen. Neulinge werden dies immer vergessen!
cat.prototype.constructor = cat;
Wenn wir das Prototypattribut der Funktion vollständig ändern (indem wir ein neues Objekt zuweisen), geht der Verweis auf den ursprünglichen Konstruktor verloren, da das Objekt von uns erstellt wurde Ohne das Konstruktorattribut:
function A() {}
A.prototype = {
x: 10
};
var a = new A();
alert(a.x); // 10
alert(a.constructor === A); // false!
Werfen wir einen Blick auf die Erklärung des Konstruktors auf MDN: Prototyp: Returns ein Verweis auf die Objektfunktion, die den Prototyp der Instanz erstellt hat. Daher muss der Prototypverweis auf die Funktion manuell wiederhergestellt werden:
function A() {}
A.prototype = {
Konstruktor: A,
x: 10
};
var a = new A();
alert(a.x); // 10
alert(a.constructor === A) ; // wahr
Die Übermittlung des Prototypattributs hat jedoch keine Auswirkungen auf den Prototyp des erstellten Objekts (es wird nur beeinflusst, wenn sich das Prototypattribut des Konstruktors ändert), d. h. auf das neue Das erstellte Objekt verfügt über einen neuen Prototyp. Das erstellte Objekt verweist weiterhin auf den ursprünglichen alten Prototyp (dieser Prototyp kann nicht mehr geändert werden).
function A() {}
A.prototype.x = 10;
var a = new A();
alert(a.x); // 10
A.prototype = {
Konstruktor: A,
x: 20
y: 30
};
// Objekt a wird von Rohöl durch implizite [[Prototyp]] referenziert vom Prototyp erhaltener Wert
alert(a.x); // 10
alert(a.y) // undefiniert
var b = new A();
// Aber das neue Objekt stammt vom neuen Prototyp Erhaltener Wert
alert(b.x); // 20
alert(b.y) // 30
Daher ist „die dynamische Änderung des Prototyps wirkt sich auf alle Objekte aus und wird neue Prototypen haben“ falsch. Der neue Prototyp wird erst auf neu erstellte Objekte wirksam, nachdem der Prototyp geändert wurde. Die Hauptregel hierbei lautet: Der Prototyp eines Objekts wird erstellt, wenn das Objekt erstellt wird, und kann danach nicht mehr in ein neues Objekt geändert werden. Wenn er immer noch auf dasselbe Objekt verweist, kann er über einen expliziten Prototyp im Konstruktor referenziert werden. Nachdem das Objekt erstellt wurde, können nur die Attribute des Prototyps hinzugefügt oder geändert werden.
Variables Objekt Im Funktionsausführungskontext kann nicht direkt auf VO (Variablenobjekt) zugegriffen werden. Zu diesem Zeitpunkt spielt das Aktivierungsobjekt (Aktivierungsobjekt) die Rolle von VO. Das aktive Objekt wird beim Betreten des Funktionskontexts erstellt und über das Arguments-Attribut der Funktion initialisiert. Der Wert des Arguments-Attributs ist das Arguments-Objekt:
function foo(x, y, z) {
// Die Anzahl der deklarierten Funktionsparameter arguments (x, y, z)
Alert(foo.length); // 3
// Die Anzahl der tatsächlich übergebenen Parameter (nur x, y)
Alert(arguments.length); // 2
/ / Der Aufrufer des Parameters Ist die Funktion selbst
warning(arguments.callee === foo); // true
}
Beim Eintritt in den Ausführungskontext (vor der Codeausführung), VO bereits enthält die folgenden Attribute: 1 . Alle formalen Parameter der Funktion (wenn wir uns im Funktionsausführungskontext befinden)
Alle Funktionsdeklarationen (FunctionDeclaration, FD); ;
Ein weiteres klassisches Beispiel:
alert(x); // function
var x = 10;
x = 20 ;
function x() {};
alert(x); // 20
Gemäß der Spezifikation werden Funktionsdeklarationen bei der Eingabe in den Kontext ausgefüllt. Bei der Eingabe in den Kontext gibt es auch eine Variablendeklaration „x“. Wie oben erwähnt, folgt die Variablendeklaration der Funktionsdeklaration und bildet sich nacheinander Parameterdeklaration, und während dieser Phase des Eintritts in den Kontext beeinträchtigt die Variablendeklaration nicht Funktionsdeklarationen oder formale Parameterdeklarationen mit demselben Namen, die bereits in VO vorhanden sind. Im Vergleich zu einfachen Attributen haben Variablen ein Attribut: {DontDelete}. Die Bedeutung dieses Attributs besteht darin, dass das Variablenattribut nicht direkt mit dem Löschoperator gelöscht werden kann.
a = 10;
alert(window.a); // 10
alert(delete a); // true
alert(window.a); undefiniert
var b = 20;
alert(window.b); // 20
alert(delete b); // false
alert(window.b); // immer noch 20. b ist eine Variable, keine Eigenschaft!
var a = 10; // Variable im globalen Kontext
(function () {
var b = 20; // Lokale Variable im Funktionskontext
} )( );
alert(a); // 10
alert(b); // Die globale Variable „b“ ist nicht in einem Funktionskontext deklariert, dies wird bestimmt durch Der Aufrufer wird bereitgestellt und hängt davon ab, wie die Funktion aufgerufen wird. Wenn die linke Seite der aufrufenden Klammer () ein Wert vom Referenztyp ist, wird dieser auf das Basisobjekt des Referenztypwerts gesetzt. In anderen Fällen (alle anderen Eigenschaften, die vom Referenztyp abweichen) ist dieser Wert null . Es gibt jedoch keine tatsächliche Situation, in der der Wert von this null ist, denn wenn der Wert von this null ist, wird sein Wert implizit in ein globales Objekt konvertiert.
alarm(this); // null => global
})();
bar: function () {
alarm(this);
}
};
foo.bar(); Referenz, OK => foo
(foo.bar)(); // Referenz, OK => foo.bar)(); // global
(false ||. foo.bar)(); // global
(foo.bar, foo.bar)(); // Das Problem mit global
ist, dass die folgenden drei Aufrufe bestimmte Operationen anwenden , ist der Wert auf der linken Seite der aufrufenden Klammer kein Referenztyp mehr.
Im zweiten Beispiel gilt der Gruppenoperator nicht. Denken Sie an die oben genannten Methoden, die den tatsächlichen Wert eines Objekts von einem Referenztyp erhalten, wie z. B. GetValue. Dementsprechend erhalten wir bei der Rückgabe der Gruppenoperation immer noch einen Referenztyp. Aus diesem Grund wird dieser Wert wieder auf das Basisobjekt gesetzt, nämlich foo.
Im dritten Beispiel ruft der Zuweisungsoperator im Gegensatz zum Gruppenoperator die GetValue-Methode auf. Das zurückgegebene Ergebnis ist ein Funktionsobjekt (jedoch kein Referenztyp), was bedeutet, dass dieser auf null gesetzt ist und das Ergebnis ein globales Objekt ist.Das Gleiche gilt für die vierte und fünfte – der Kommaoperator und der logische Operator (OR) rufen die GetValue-Methode auf, und dementsprechend verlieren wir die Referenz und erhalten die Funktion. Und stellen Sie es wieder auf global ein.
Wie wir wissen, werden lokale Variablen, interne Funktionen und formale Parameter im Aktivierungsobjekt einer bestimmten Funktion gespeichert.
function foo() {
function bar() {
}
bar(); )
}
Das aktive Objekt wird immer als dieses zurückgegeben und der Wert ist null – (das heißt, AO.bar() im Pseudocode entspricht null.bar()). Hier kehren wir noch einmal zum oben beschriebenen Beispiel zurück, wobei dieses auf das globale Objekt gesetzt ist.
Bereichskette
function foo() {
var y = 20; function barFD() { // Funktionsdeklaration
warning(x);
alarm(y);
}
var barFn = Function('alert(x); alarm(y);');
barFD( // 10, 20
barFn(); // 10, „y“ ist nicht definiert
}
foo();
Also:
var x = 10, y = 10;
mit ({x: 20}) { var x = 30, y = 30;
//x = 30 deckt hier x = 20;
Alert(x); // 30
Alert ab (y); // 30
}
alert(x); // 10
alert(y); // 30
Was passiert beim Betreten des Kontexts? Dem Variablenobjekt wurden die Bezeichner „x“ und „y“ hinzugefügt. Nehmen Sie außerdem während der Codeausführungsphase die folgenden Änderungen vor:
x = 10, y = 10;
Das Objekt {x:20} wird an der Vorderseite des Bereichs hinzugefügt;
Innerhalb von with wird die var-Deklaration angetroffen, und natürlich wird nichts erstellt. denn beim Eintritt in den Kontext wurden alle Variablen analysiert und hinzugefügt;
Im zweiten Schritt wird nur die Variable „x“ geändert, tatsächlich wird nun das „x“ im Objekt analysiert und an der Vorderseite des Bereichs hinzugefügt Kette: „x“ ist 20, geändert in 30; Wenn die Anweisung abgeschlossen ist, wird ihr spezifisches Objekt aus der Bereichskette entfernt (die geänderte Variable „x“ - 30 wird ebenfalls aus diesem Objekt entfernt), d. h. die Struktur der Bereichskette wird in den Zustand vor der Stärkung zurückversetzt.
In den letzten beiden Warnungen bleibt das „x“ des aktuellen Variablenobjekts gleich und der Wert von „y“ ist jetzt gleich 30, was sich während der Ausführung der with-Anweisung geändert hat.
Funktion
Frage zu Klammern
Sehen wir uns diese Frage an: „Warum muss man eine Funktion in Klammern setzen, wenn man sie direkt nach ihrer Erstellung aufruft?“ “, lautet die Antwort: Die Einschränkung von Ausdruckssätzen ist so.
Gemäß dem Standard kann eine Ausdrucksanweisung nicht mit einer geschweiften Klammer { beginnen, da es schwierig ist, sie von einem Codeblock zu unterscheiden. Ebenso kann sie nicht mit einem Funktionsschlüsselwort beginnen, da es schwierig ist, sie von einem zu unterscheiden Funktionsdeklaration. Das heißt, wenn wir eine Funktion definieren, die unmittelbar nach ihrer Erstellung ausgeführt wird, rufen wir sie wie folgt auf:
function () {
...}();
/ / Auch wenn es einen Namen gibt
Funktion foo() {
...
}();
Wir verwenden die Funktionsdeklaration, die beiden oben genannten Definitionen werden vom Interpreter verwendet Melden Sie einen Fehler bei der Interpretation, dies kann jedoch mehrere Gründe haben. Wenn es im globalen Code (d. h. auf Programmebene) definiert ist, behandelt der Interpreter es als Funktionsdeklaration, da es mit dem Schlüsselwort function beginnt. Im ersten Beispiel erhalten wir einen SyntaxError, da die Funktionsdeklaration keinen Namen hat. (Wir haben bereits erwähnt, dass Funktionsdeklarationen Namen haben müssen). Im zweiten Beispiel haben wir eine Funktionsdeklaration namens foo, die normal erstellt wird, aber wir erhalten immer noch einen Syntaxfehler – einen Gruppierungsoperatorfehler ohne Ausdruck. Es handelt sich tatsächlich um einen Gruppierungsoperator nach der Funktionsdeklaration, nicht um die in einem Funktionsaufruf verwendeten Klammern. Wenn wir also den folgenden Code deklarieren:
alert(foo); // function
function foo(x ) {
Alert(x);
}(1); // Dies ist nur ein Gruppierungsoperator, kein Funktionsaufruf!
foo(10); // Dies ist ein echter Funktionsaufruf, das Ergebnis ist 10
Der einfachste Weg, einen Ausdruck zu erstellen, ist die Verwendung von Gruppierungsoperatorklammern, und der darin platzierte Ausdruck ist immer , also Der Dolmetscher wird beim Dolmetschen keine Unklarheiten haben. Während der Codeausführungsphase wird diese Funktion erstellt, sofort ausgeführt und dann automatisch zerstört (wenn keine Referenz vorhanden ist)
warning(x);
})(1); // Dies ist der Aufruf, nicht der Gruppierungsoperator
Der obige Code ist das, was wir nennen, indem wir einen Ausdruck in Klammern setzen und ihn dann über (1) aufrufen. Beachten Sie, dass für die folgende Funktion, die sofort ausgeführt wird, die umgebenden Klammern nicht erforderlich sind, da sich die Funktion bereits an der Position des Ausdrucks befindet und der Parser weiß, dass es sich um die FE handelt, die während der Funktionsausführungsphase erstellt werden soll , sodass sie unmittelbar nach der Erstellung der Funktion aufgerufen wird.
var foo = {
bar: function (x) { return x % 2 != 0 ? 'yes' : 'no';
}( 1)
};
alert(foo.bar); // 'yes'
Wie wir sehen können, ist foo.bar eine Zeichenfolge und keine Funktion, die Funktion hier ist es nur wird verwendet, um diese Eigenschaft basierend auf den bedingten Parametern zu initialisieren – sie wird sofort erstellt und aufgerufen.
Daher lautet die vollständige Antwort auf die Frage „Über Klammern“ wie folgt:
Gruppenoperator-Klammern sind erforderlich, wenn sich eine Funktion nicht an einer Ausdrucksposition befindet – also konvertieren Funktion manuell in FE einbinden.Wenn der Parser weiß, dass es sich um FE handelt, ist die Verwendung von Klammern nicht erforderlich.
Freie Variablen:
function testFn() {
var localVar = 10;//Für die innerFn-Funktion ist localVar eine freie Variable. function innerFn(innerParam) {
alarm(innerParam + localVar);
}
return innerFn;
}
Statischer Abschlussbereich:
var z = 10;
function foo() { warning(z);
}
foo(); // 10 – Verwendung statischer und dynamischer Bereiche Wenn
(function () {
var z = 20;
foo(); // 10 – statischen Bereich verwenden, 20 – dynamischen Bereich verwenden
})();
// Es ist das Gleiche bei Verwendung von foo als Parameter
(function (funArg) {
var z = 30;
funArg(); // 10 – statischer Bereich, 30 – dynamischer Bereich
})(foo);
Theorie: Aufgrund der Bereichskette sind alle Funktionen Abschlüsse (unabhängig vom Funktionstyp: anonyme Funktionen, FE, NFE und FD sind alles Abschlüsse). Aus praktischer Sicht: Die folgenden Funktionen gelten als Abschlüsse: * Sie existiert auch dann noch, wenn der Kontext, in dem sie erstellt wurde, zerstört wurde (z. B. die innere Funktion kehrt von der übergeordneten Funktion zurück)
* Auf freie Variablen wird im Code verwiesen
Abschließend:
ECMAScript ist eine objektorientierte Sprache, die prototypbasierte delegierte Vererbung unterstützt.