Interviewfragen haben sich aus der Arbeit entwickelt
Das ist eine Frage, die mir bei der Arbeit begegnet ist. Sie erschien mir sehr interessant, also habe ich sie zu einer Frage für Vorstellungsgespräche gemacht. Ich stellte fest, dass fast niemand alle Fragen richtig beantworten und die Gründe nennen konnte, also habe ich sie aufgegriffen und mit ihr gesprochen darüber.
Schauen Sie sich zuerst den Fragecode an:
function fun(n,o) { console.log(o) return { fun:function(m){ return fun(m,n); } }; } var a = fun(0); a.fun(1); a.fun(2); a.fun(3);//undefined,?,?,? var b = fun(0).fun(1).fun(2).fun(3);//undefined,?,?,? var c = fun(0).fun(1); c.fun(2); c.fun(3);//undefined,?,?,? //问:三行a,b,c的输出分别是什么?
Dies ist ein sehr typisches JS-Schließungsproblem. Darin sind drei Ebenen mit Spaßfunktionen verschachtelt. Es ist besonders wichtig herauszufinden, um welche Spaßfunktion es sich bei der jeweiligen Ebene handelt.
Sie können die Ergebnisse, die Sie denken, zunächst auf Papier oder an anderen Orten aufschreiben und dann erweitern, um zu sehen, was die richtige Antwort ist?
Antwort
//a: undefined,0,0,0 //b: undefined,0,1,2 //c: undefined,0,1,1
Hast du alles richtig gemacht? Wenn Sie alles richtig beantwortet haben, herzlichen Glückwunsch. Es gibt fast nichts, was Sie beim js-Abschlussproblem überraschen kann. Wenn es keine Antwort gibt, fahren Sie mit der Analyse fort.
Es gibt mehrere Funktionen in JS
Zuvor müssen Sie zunächst verstehen, dass Funktionen in JS in zwei Typen unterteilt werden können: benannte Funktionen (benannte Funktionen) und anonyme Funktionen.
Die Methode zur Unterscheidung dieser beiden Funktionen ist sehr einfach. Sie können dies anhand der Ausgabe von fn.name beurteilen. Die Funktion mit einem Namen ist eine benannte Funktion und die Funktion ohne Namen ist eine anonyme Funktion.
Hinweis: Der Name der benannten Funktion kann in niedrigeren Versionen des IE nicht abgerufen werden und wird als undefiniert zurückgegeben. Es wird empfohlen, ihn in Firefox oder Google Chrome zu testen.
Oder verwenden Sie die IE-kompatible Methode zum Abrufen des Funktionsnamens, um den Funktionsnamen abzurufen:
/** * 获取指定函数的函数名称(用于兼容IE) * @param {Function} fun 任意函数 */ function getFunctionName(fun) { if (fun.name !== undefined) return fun.name; var ret = fun.toString(); ret = ret.substr('function '.length); ret = ret.substr(0, ret.indexOf('(')); return ret; }
Verwenden Sie die obige Funktion, um zu testen, ob es sich um eine anonyme Funktion handelt:
Sie können wissen, dass die Variable fn1 eine benannte Funktion und fn2 eine anonyme Funktion ist
Mehrere Möglichkeiten, Funktionen zu erstellen
Nachdem Sie über die Funktionstypen gesprochen haben, müssen Sie auch verstehen, dass es mehrere Möglichkeiten gibt, Funktionen in JS zu erstellen.
1. Funktion deklarieren
Die gebräuchlichste und standardmäßigste Methode zum Deklarieren einer Funktion, einschließlich Funktionsname und Funktionskörper.
Funktion fn1(){}
2. Anonymen Funktionsausdruck erstellen
Erstellen Sie eine Variable, deren Inhalt eine Funktion ist
var fn1=function (){}
Beachten Sie, dass die mit dieser Methode erstellte Funktion eine anonyme Funktion ist, das heißt, es gibt keinen Funktionsnamen
var fn1=function (){}; getFunctionName(fn1).length;//0
3. Erstellen Sie einen benannten Funktionsausdruck
Erstellen Sie eine Variable, die eine Funktion mit einem Namen enthält
var fn1=function xxcanghai(){};
Hinweis: Der Funktionsname eines benannten Funktionsausdrucks kann nur innerhalb der erstellten Funktion verwendet werden
Das heißt, die mit dieser Methode erstellte Funktion kann nur fn1 und nicht den Funktionsnamen von xxcanghai in der äußeren Ebene der Funktion verwenden. Die Benennung von xxcanghai kann nur innerhalb der erstellten Funktion
verwendet werdenTest:
var fn1=function xxcanghai(){ console.log("in:fn1<",typeof fn1,">xxcanghai:<",typeof xxcanghai,">"); }; console.log("out:fn1<",typeof fn1,">xxcanghai:<",typeof xxcanghai,">"); fn1(); //out:fn1< function >xxcanghai:< undefined > //in:fn1< function >xxcanghai:< function >
Sie können sehen, dass der Funktionsname von xxcanghai nicht außerhalb der Funktion (out) verwendet werden kann und undefiniert ist.
Hinweis: Das Definieren einer Funktion innerhalb eines Objekts wie var o={ fn : function (){…} } ist auch ein Funktionsausdruck
4. Funktionskonstruktor
Sie können eine Funktionszeichenfolge an den Funktionskonstruktor übergeben und eine Funktion zurückgeben, die diesen Zeichenfolgenbefehl enthält. Diese Methode erstellt eine anonyme Funktion.
5. Selbstausführende Funktion
(function(){alert(1);})(); (function fn1(){alert(1);})();
Selbstausführende Funktionen gehören zu den oben genannten „Funktionsausdrücken“ und die Regeln sind die gleichen
6. Andere Möglichkeiten, Funktionen zu erstellen
Natürlich gibt es auch andere Möglichkeiten, Funktionen zu erstellen oder auszuführen. Ich werde zum Beispiel nicht auf die Verwendung von eval, setInterval eingehen. Da es sich hierbei nicht um Standardmethoden handelt, werde ich hier nicht zu sehr darauf eingehen
Welcher Zusammenhang besteht zwischen den drei Spaßfunktionen?
Nachdem Sie über Funktionstypen und Methoden zum Erstellen von Funktionen gesprochen haben, können Sie zum Thema zurückkehren und sich diese Interviewfrage ansehen.
Dieser Code enthält drei unterhaltsame Funktionen. Der erste Schritt besteht darin, die Beziehung zwischen diesen drei unterhaltsamen Funktionen herauszufinden und herauszufinden, welche Funktion mit welcher Funktion identisch ist.
function fun(n,o) { console.log(o) return { fun:function(m){ //... } }; }
先看第一个fun函数,属于标准具名函数声明,是新创建的函数,他的返回值是一个对象字面量表达式,属于一个新的object。
这个新的对象内部包含一个也叫fun的属性,通过上述介绍可得知,属于匿名函数表达式,即fun这个属性中存放的是一个新创建匿名函数表达式。
注意:所有声明的匿名函数都是一个新函数。
所以第一个fun函数与第二个fun函数不相同,均为新创建的函数。
函数作用域链的问题
再说第三个fun函数之前需要先说下,在函数表达式内部能不能访问存放当前函数的变量。
测试1:对象内部的函数表达式:
var o={ fn:function (){ console.log(fn); } }; o.fn();//ERROR报错
测试2:非对象内部的函数表达式:
var fn=function (){ console.log(fn); }; fn();//function (){console.log(fn);};正确
结论:使用var或是非对象内部的函数表达式内,可以访问到存放当前函数的变量;在对象内部的不能访问到。
原因也非常简单,因为函数作用域链的问题,采用var的是在外部创建了一个fn变量,函数内部当然可以在内部寻找不到fn后向上册作用域查找fn,而在创建对象内部时,因为没有在函数作用域内创建fn,所以无法访问。
所以综上所述,可以得知,最内层的return出去的fun函数不是第二层fun函数,是最外层的fun函数。
所以,三个fun函数的关系也理清楚了,第一个等于第三个,他们都不等于第二个。
到底在调用哪个函数?
再看下原题,现在知道了程序中有两个fun函数(第一个和第三个相同),遂接下来的问题是搞清楚,运行时他执行的是哪个fun函数?
function fun(n,o) { console.log(o) return { fun:function(m){ return fun(m,n); } }; } var a = fun(0); a.fun(1); a.fun(2); a.fun(3);//undefined,?,?,? var b = fun(0).fun(1).fun(2).fun(3);//undefined,?,?,? var c = fun(0).fun(1); c.fun(2); c.fun(3);//undefined,?,?,? //问:三行a,b,c的输出分别是什么?
1. Die erste Zeile a
var a = fun(0); a.fun(1); a.fun(3);
Es ist bekannt, dass der erste Spaß (0) die Spaßfunktion der ersten Ebene aufruft. Der zweite Fun(1) ist die Fun-Funktion, die den Rückgabewert des vorherigen Fun aufruft, also:
Die letzten Funktionen fun(1), fun(2), fun(3) rufen alle die Fun-Funktion der zweiten Ebene auf.
Dann:
Wenn fun(0) zum ersten Mal aufgerufen wird, ist o undefiniert;
Wenn fun(1) zum zweiten Mal aufgerufen wird, ist m 1. Zu diesem Zeitpunkt schließt fun n der äußeren Funktion, d. h. n = 0 für den ersten Aufruf, d. h. m = 1, n =0, und in der ersten Ebene wird die Fun-Funktion fun(1,0) intern aufgerufen; also ist oWenn fun(2) zum dritten Mal aufgerufen wird, ist m 2, aber a.fun wird immer noch aufgerufen, sodass n aus dem ersten Aufruf immer noch geschlossen ist, sodass die erste Ebene von fun(2,0) aufgerufen wird intern. ;Also ist o 0
Das Gleiche wie beim vierten Mal;
Das heißt: Die endgültige Antwort ist undefiniert,0,0,0
2. Die zweite Zeile b
var b = fun(0).fun(1).fun(2).fun(3);//undefiniert,?,?,?
Beginnen wir mit fun(0) Es muss die Fun-Funktion der ersten Ebene sein und ihr Rückgabewert ist ein Objekt, also ruft die Fun-Funktion der zweiten Ebene auf Im Folgenden werden auch die Fun-Funktionen der zweiten Ebene genannt.
Wenn die erste Ebene fun(0) zum ersten Mal aufgerufen wird, ist o undefiniert;
Wenn .fun(1) zum zweiten Mal aufgerufen wird, ist m 1. Zu diesem Zeitpunkt schließt fun n der äußeren Funktion, d. h. n = 0 für den ersten Aufruf, d. h. m = 1, n=0, und rufen Sie intern die Fun-Funktion der ersten Ebene auf, also ist o
Wenn .fun(2) zum dritten Mal aufgerufen wird, ist m 2. Zu diesem Zeitpunkt ist die aktuelle Fun-Funktion nicht das Rückgabeobjekt der ersten Ausführung, sondern das Rückgabeobjekt der zweiten Ausführung. Wenn die Spaßfunktion der ersten Ebene zum zweiten Mal ausgeführt wird, (1,0), also n = 1, o = 0, wird das zweite n bei der Rückkehr geschlossen, also wenn die Spaßfunktion der dritten Ebene zum dritten Mal aufgerufen wird Zeit, m =2,n=1, das heißt, die Fun-Funktion fun(2,1) der ersten Ebene wird aufgerufen, also ist o
Wenn .fun(3) zum vierten Mal aufgerufen wird, ist m 3, was das n des dritten Aufrufs schließt. Ebenso ist der letzte Aufruf der Fun-Funktion der ersten Ebene fun(3,2); o ist 2;
Das ist die endgültige Antwort: undefiniert,0,1,23. Die dritte Zeile c
var c = fun(0).fun(1); c.fun(2);//undefiniert,?,?,?
Anhand der beiden vorherigen Beispiele können wir Folgendes wissen:
fun(0) führt die Fun-Funktion der ersten Ebene aus, .fun(1) führt die von fun(0) zurückgegebene Fun-Funktion der zweiten Ebene aus, die Anweisung endet hier und c speichert den Rückgabewert von fun(1). , und nicht der Rückgabewert von fun(0), daher ist der Abschluss in c auch der Wert von n, wenn fun(1) zum zweiten Mal ausgeführt wird. c.fun(2) führt die von fun(1) zurückgegebene Fun-Funktion der zweiten Ebene aus, und c.fun(3) führt auch die von fun(1) zurückgegebene Fun-Funktion der zweiten Ebene aus.
Dann:
Wenn die erste Ebene fun(0) zum ersten Mal aufgerufen wird, ist o undefiniert;
Wenn .fun(1) zum zweiten Mal aufgerufen wird, ist m 1. Zu diesem Zeitpunkt schließt fun n der äußeren Funktion, d. h. n = 0 für den ersten Aufruf, d. h. m = 1, n=0, und rufen Sie intern die Fun-Funktion der ersten Ebene auf, also ist oWenn .fun(2) zum dritten Mal aufgerufen wird, ist m 2. Zu diesem Zeitpunkt ist der Spaßabschluss für den zweiten Aufruf n=1, also m=2, n=1 und den ersten Die Funktion fun(2,1); ist also 1;
Das Gleiche gilt für .fun(3) zum vierten Mal, aber es ist immer noch der Rückgabewert des zweiten Aufrufs, sodass die Fun-Funktion fun(3,1) der ersten Ebene schließlich aufgerufen wird, sodass o immer noch 1 ist
Das ist die endgültige Antwort: undefiniert,0,1,1Spätere Wörter Dieser Code wurde ursprünglich beim Umschreiben eines asynchronen Rückrufs in eine synchrone Aufrufkomponente erstellt. Ich habe diese Gefahr entdeckt und ein tieferes Verständnis für JS-Abschlüsse gewonnen.
Es gibt unzählige Artikel im Internet darüber, was Abschlüsse sind, aber um zu verstehen, was Abschlüsse sind, muss man sie erst noch selbst im Code entdecken und verstehen.
Wenn Sie mich fragen, was ein Abschluss ist, denke ich, dass Abschluss im weitesten Sinne bedeutet, dass eine Variable in ihrem eigenen Bereich verwendet wird, der als Abschluss bezeichnet wird.
Haben alle richtig geantwortet? Ich hoffe, dass die Leser durch diesen Artikel ein besseres Verständnis des Schließungsphänomens erlangen können. Wenn Sie andere Erkenntnisse oder Meinungen haben, können Sie diese gerne korrigieren und diskutieren.