Interviewfragen sind ein Thema, das sowohl Personalvermittlungsunternehmen als auch Entwicklern sehr am Herzen liegt. Unternehmen hoffen, damit das wahre Niveau und die Fähigkeit der Entwickler zu verstehen, mit Details umzugehen, während Entwickler hoffen, ihr Niveau bestmöglich unter Beweis stellen zu können Ausmaß (sogar über die Norm hinaus). Dieser Artikel enthält viele Fragen zu Vorstellungsgesprächen im Bereich Front-End-Entwicklung, die sowohl für Personalvermittler als auch für Bewerber eine Lektüre wert sind!
Vorwort
Ich habe gerade vor ein paar Jahren gekündigt. Ich möchte Ihnen eine Frage im Vorstellungsgespräch mitteilen. Diese Frage ist die letzte Frage in einer Reihe von Fragen im Vorgespräch, die ich gestellt habe Es ist schade, dass es in den fast zwei Jahren bisher fast niemandem gelungen ist, es vollständig zu beantworten. Das liegt nicht daran, dass es schwierig ist, sondern daran, dass die meisten Interviewer es unterschätzen.
Das Thema lautet wie folgt:
function Foo() { getName = function () { alert (1); }; return this; } Foo.getName = function () { alert (2);}; Foo.prototype.getName = function () { alert (3);}; var getName = function () { alert (4);}; function getName() { alert (5);} //请写出以下输出结果: Foo.getName(); getName(); Foo().getName(); getName(); new Foo.getName(); new Foo().getName(); new new Foo().getName();
Die Antwort lautet:
function Foo() { getName = function () { alert (1); }; return this; } Foo.getName = function () { alert (2);}; Foo.prototype.getName = function () { alert (3);}; var getName = function () { alert (4);}; function getName() { alert (5);} //答案: Foo.getName();//2 getName();//4 Foo().getName();//1 getName();//1 new Foo.getName();//2 new Foo().getName();//3 new new Foo().getName();//3
Diese Frage basiert auf meiner bisherigen Entwicklungserfahrung und verschiedenen JS-Fallstricken, auf die ich gestoßen bin. Diese Frage umfasst viele Wissenspunkte, einschließlich der Förderung von Variablendefinitionen, dieses Zeigers, der Operatorpriorität, des Prototyps, der Vererbung, der Verschmutzung globaler Variablen, der Priorität von Objektattributen und Prototypattributen usw.
Diese Frage enthält 7 Fragen, bitte erläutern Sie diese unten.
Erste Frage
Schauen wir uns zunächst an, was in der ersten Hälfte dieser Frage gemacht wurde. Zuerst haben wir eine Funktion namens Foo definiert, dann eine statische Eigenschaft namens getName für Foo erstellt, um eine anonyme Funktion zu speichern, und dann ein neues Prototypobjekt für erstellt Foo. Eine anonyme Funktion namens getName. Anschließend wird über einen Funktionsvariablenausdruck eine getName-Funktion erstellt und schließlich eine getName-Funktion deklariert.
Die erste Frage, Foo.getName, greift natürlich auf die statischen Eigenschaften zu, die in der Foo-Funktion gespeichert sind, was natürlich 2 ist. Es gibt nichts zu sagen.
Zweite Frage
Die zweite Frage besteht darin, die getName-Funktion direkt aufzurufen. Da es direkt aufgerufen wird, greift es auf die Funktion namens getName im aktuellen Gültigkeitsbereich oben zu, hat also nichts mit 1 2 3 zu tun. Viele Interviewer beantworteten diese Frage mit 5. Hier gibt es zwei Fallstricke: Die eine ist die Förderung der Variablendeklaration und die andere der Funktionsausdruck.
Variablendeklarationsförderung
Das heißt, alle deklarierten Variablen oder deklarierten Funktionen werden an die Spitze der aktuellen Funktion heraufgestuft.
Zum Beispiel der folgende Code:
console.log('x' in window);//true var x; x = 0;
Wenn der Code ausgeführt wird, hebt die js-Engine die Deklarationsanweisung an den Anfang des Codes und wird zu:
var x; console.log('x' in window);//true x = 0;
Funktionsausdruck
var getName und function getName sind beide Deklarationsanweisungen. Der Unterschied besteht darin, dass var getName ein Funktionsausdruck ist, während function getName eine Funktionsdeklaration ist. Weitere Informationen zum Erstellen verschiedener Funktionen in JS finden Sie in den klassischen JS-Abschlussinterviewfragen, die die meisten Leute falsch stellen. Dieser Artikel enthält detaillierte Erklärungen.
Das größte Problem bei Funktionsausdrücken besteht darin, dass js diesen Code in zwei Codezeilen aufteilt und diese separat ausführt.
Zum Beispiel der folgende Code:
console.log(x);//输出:function x(){} var x=1; function x(){}
Der tatsächlich ausgeführte Code besteht darin, var x=1 zunächst in zwei Zeilen aufzuteilen: var x; und x = 1; und dann die beiden Zeilen var x und function x(){} nach oben zu bringen, um Folgendes zu erhalten:
var x; function x(){} console.log(x); x=1;
Das von der endgültigen Funktion deklarierte x deckt also das von der Variablen deklarierte x ab, und die Protokollausgabe ist die x-Funktion.
In ähnlicher Weise lautet die endgültige Ausführung des Codes in der ursprünglichen Frage:
function Foo() { getName = function () { alert (1); }; return this; } var getName;//只提升变量声明 function getName() { alert (5);}//提升函数声明,覆盖var的声明 Foo.getName = function () { alert (2);}; Foo.prototype.getName = function () { alert (3);}; getName = function () { alert (4);};//最终的赋值再次覆盖function getName声明 getName();//最终输出4
Dritte Frage
In der dritten Frage führt Foo().getName(); zuerst die Foo-Funktion aus und ruft dann die getName-Attributfunktion des Rückgabewertobjekts der Foo-Funktion auf.
Der erste Satz der Foo-Funktion getName = function () { warning (1 }); Beachten Sie, dass es keine var-Deklaration gibt, also suchen Sie zuerst nach der getName-Variablen im aktuellen Foo Funktionsumfang, und es gibt keinen. Schauen Sie sich dann die obere Ebene des aktuellen Funktionsbereichs an, dh den äußeren Bereich, um zu sehen, ob er die Variable getName enthält. Dies ist die Funktion „alert(4)“ in der zweiten Frage Variable zu function(){alert(1) }.
Hier ist tatsächlich die getName-Funktion im äußeren Bereich, die geändert wird.
Hinweis: Wenn es hier immer noch nicht gefunden wird, wird bis zum Fensterobjekt gesucht. Wenn im Fensterobjekt kein getName-Attribut vorhanden ist, erstellen Sie eine getName-Variable im Fensterobjekt.
Danach ist der Rückgabewert der Foo-Funktion dieser, und es gibt bereits viele Artikel zu diesem Problem von JS im Blog-Garten, daher werde ich hier nicht näher darauf eingehen.
Um es einfach auszudrücken: Der Sinn davon wird durch die aufrufende Methode der Funktion bestimmt. In der hier direkt aufrufenden Methode zeigt dies auf das Fensterobjekt.
Die Foo-Funktion gibt das Fensterobjekt zurück, was der Ausführung von window.getName() entspricht, und der getName im Fenster wurde in warning(1) geändert, sodass am Ende 1 ausgegeben wird
Hier werden zwei Wissenspunkte untersucht, einer ist das Problem des variablen Umfangs und der andere ist das Problem dieser Zeiger.
Vierte Frage
Rufen Sie die getName-Funktion direkt auf, was window.getName() entspricht, da diese Variable bei der Ausführung der Foo-Funktion geändert wurde, sodass das Ergebnis das gleiche ist wie bei der dritten Frage, nämlich
Fünfte Frage
Die fünfte Frage ist new Foo.getName(); was hier untersucht wird, ist das Problem der Operatorpriorität von js.
Js-Operator-Priorität:
Anhand der obigen Tabelle können wir erkennen, dass die Priorität von Punkt (.) höher ist als die der neuen Operation, was äquivalent ist zu:
new (Foo.getName)();
Die getName-Funktion wird also tatsächlich als Konstruktor ausgeführt und 2 wird angezeigt.
Frage 6
Die sechste Frage ist new Foo().getName(). Erstens sind die Prioritätsklammern des Operators höher als new. Die tatsächliche Ausführung ist
(new Foo()).getName()
Dann wird zuerst die Foo-Funktion ausgeführt, und Foo hat als Konstruktor einen Rückgabewert. Daher müssen wir hier das Rückgabewertproblem des Konstruktors in js erklären.
Der Rückgabewert des Konstruktors
In herkömmlichen Sprachen sollten Konstruktoren keinen Rückgabewert haben. Der tatsächliche Rückgabewert der Ausführung ist das instanziierte Objekt dieses Konstruktors.
In js können Konstruktoren Rückgabewerte haben oder nicht.
1. Wenn kein Rückgabewert vorhanden ist, wird das instanziierte Objekt wie in anderen Sprachen zurückgegeben.
2. Wenn ein Rückgabewert vorhanden ist, prüfen Sie, ob der Rückgabewert ein Referenztyp ist. Wenn es sich um einen Nicht-Referenztyp handelt, beispielsweise um einen Basistyp (Zeichenfolge, Zahl, Boolescher Wert, Null, undefiniert), ist dies dasselbe wie kein Rückgabewert, und sein instanziiertes Objekt wird tatsächlich zurückgegeben.
3. Wenn der Rückgabewert ein Referenztyp ist, ist der tatsächliche Rückgabewert dieser Referenztyp.
In der ursprünglichen Frage wird dies zurückgegeben und stellt ursprünglich das aktuell instanziierte Objekt im Konstruktor dar, sodass die Foo-Funktion schließlich das instanziierte Objekt zurückgibt.
Dann rufen wir die getName-Funktion des instanziierten Objekts auf. Da dem instanziierten Objekt im Foo-Konstruktor keine Attribute hinzugefügt werden, suchen wir im Prototypobjekt des aktuellen Objekts nach getName.
Die endgültige Ausgabe ist 3.
Frage 7
Die siebte Frage, new new Foo().getName(); ist ebenfalls ein Operator-Prioritätsproblem.
Die endgültige tatsächliche Ausführung ist:
new ((new Foo()).getName)();
Initialisieren Sie zuerst das instanziierte Objekt von Foo und verwenden Sie dann die Funktion getName für seinen Prototyp erneut als Konstruktor.
Das Endergebnis ist 3
Endlich
Was die Antworten betrifft, kann die erste Frage zu 100 % richtig beantwortet werden, die zweite Frage ist nur zu etwa 50 % richtig, die dritte Frage kann nicht oft beantwortet werden und die vierte Frage ist sehr, sehr selten. Tatsächlich gibt es nicht viele knifflige und bizarre Verwendungsmöglichkeiten für diese Frage. Dies sind nur einige Szenarien, denen Sie begegnen könnten. Die meisten Menschen mit 1 bis 2 Jahren Berufserfahrung dürften vollkommen Recht haben.
Ich kann nur sagen, dass einige Leute zu ungeduldig und abweisend sind. Ich hoffe, dass jeder durch diesen Artikel einige Funktionen von js verstehen kann.