Heim Web-Frontend js-Tutorial JS-Abschlussinterviewfragen, die Sie durch unfallbedingte Javascript-Kenntnisse falsch machen können

JS-Abschlussinterviewfragen, die Sie durch unfallbedingte Javascript-Kenntnisse falsch machen können

May 16, 2016 pm 03:29 PM
js 闭包 面试题

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的输出分别是什么?


Nach dem Login kopieren

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
Nach dem Login kopieren

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;
}
Nach dem Login kopieren

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 

Nach dem Login kopieren

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 werden

Test:

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 >
Nach dem Login kopieren

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);})();
Nach dem Login kopieren

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){
   //...
  }
 };
} 
Nach dem Login kopieren

先看第一个fun函数,属于标准具名函数声明,是新创建的函数,他的返回值是一个对象字面量表达式,属于一个新的object。

这个新的对象内部包含一个也叫fun的属性,通过上述介绍可得知,属于匿名函数表达式,即fun这个属性中存放的是一个新创建匿名函数表达式。

注意:所有声明的匿名函数都是一个新函数。

所以第一个fun函数与第二个fun函数不相同,均为新创建的函数。

函数作用域链的问题

再说第三个fun函数之前需要先说下,在函数表达式内部能不能访问存放当前函数的变量。

测试1:对象内部的函数表达式:

var o={
 fn:function (){
  console.log(fn);
 }
};
o.fn();//ERROR报错
Nach dem Login kopieren

测试2:非对象内部的函数表达式:

var fn=function (){
 console.log(fn);
};
fn();//function (){console.log(fn);};正确
Nach dem Login kopieren


结论:使用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,&#63;,&#63;,&#63;
var b = fun(0).fun(1).fun(2).fun(3);//undefined,&#63;,&#63;,&#63;
var c = fun(0).fun(1); c.fun(2); c.fun(3);//undefined,&#63;,&#63;,&#63;
//问:三行a,b,c的输出分别是什么? 
Nach dem Login kopieren

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 o

Wenn 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.

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 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,2

3. 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 o

Wenn .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,1

Spä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.

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

Heiße KI -Werkzeuge

Undresser.AI Undress

Undresser.AI Undress

KI-gestützte App zum Erstellen realistischer Aktfotos

AI Clothes Remover

AI Clothes Remover

Online-KI-Tool zum Entfernen von Kleidung aus Fotos.

Undress AI Tool

Undress AI Tool

Ausziehbilder kostenlos

Clothoff.io

Clothoff.io

KI-Kleiderentferner

Video Face Swap

Video Face Swap

Tauschen Sie Gesichter in jedem Video mühelos mit unserem völlig kostenlosen KI-Gesichtstausch-Tool aus!

Heiße Werkzeuge

Notepad++7.3.1

Notepad++7.3.1

Einfach zu bedienender und kostenloser Code-Editor

SublimeText3 chinesische Version

SublimeText3 chinesische Version

Chinesische Version, sehr einfach zu bedienen

Senden Sie Studio 13.0.1

Senden Sie Studio 13.0.1

Leistungsstarke integrierte PHP-Entwicklungsumgebung

Dreamweaver CS6

Dreamweaver CS6

Visuelle Webentwicklungstools

SublimeText3 Mac-Version

SublimeText3 Mac-Version

Codebearbeitungssoftware auf Gottesniveau (SublimeText3)

Empfohlen: Ausgezeichnetes JS-Open-Source-Projekt zur Gesichtserkennung und -erkennung Empfohlen: Ausgezeichnetes JS-Open-Source-Projekt zur Gesichtserkennung und -erkennung Apr 03, 2024 am 11:55 AM

Die Technologie zur Gesichtserkennung und -erkennung ist bereits eine relativ ausgereifte und weit verbreitete Technologie. Derzeit ist JS die am weitesten verbreitete Internetanwendungssprache. Die Implementierung der Gesichtserkennung und -erkennung im Web-Frontend hat im Vergleich zur Back-End-Gesichtserkennung Vor- und Nachteile. Zu den Vorteilen gehören die Reduzierung der Netzwerkinteraktion und die Echtzeiterkennung, was die Wartezeit des Benutzers erheblich verkürzt und das Benutzererlebnis verbessert. Die Nachteile sind: Es ist durch die Größe des Modells begrenzt und auch die Genauigkeit ist begrenzt. Wie implementiert man mit js die Gesichtserkennung im Web? Um die Gesichtserkennung im Web zu implementieren, müssen Sie mit verwandten Programmiersprachen und -technologien wie JavaScript, HTML, CSS, WebRTC usw. vertraut sein. Gleichzeitig müssen Sie auch relevante Technologien für Computer Vision und künstliche Intelligenz beherrschen. Dies ist aufgrund des Designs der Webseite erwähnenswert

Was bedeutet der Abschluss im C++-Lambda-Ausdruck? Was bedeutet der Abschluss im C++-Lambda-Ausdruck? Apr 17, 2024 pm 06:15 PM

In C++ ist ein Abschluss ein Lambda-Ausdruck, der auf externe Variablen zugreifen kann. Um einen Abschluss zu erstellen, erfassen Sie die äußere Variable im Lambda-Ausdruck. Abschlüsse bieten Vorteile wie Wiederverwendbarkeit, Ausblenden von Informationen und verzögerte Auswertung. Sie sind in realen Situationen nützlich, beispielsweise bei Ereignishandlern, bei denen der Abschluss auch dann noch auf die äußeren Variablen zugreifen kann, wenn diese zerstört werden.

Wie implementiert man einen Abschluss im C++-Lambda-Ausdruck? Wie implementiert man einen Abschluss im C++-Lambda-Ausdruck? Jun 01, 2024 pm 05:50 PM

C++-Lambda-Ausdrücke unterstützen Abschlüsse, die Funktionsbereichsvariablen speichern und sie für Funktionen zugänglich machen. Die Syntax lautet [capture-list](parameters)->return-type{function-body}. Capture-Liste definiert die zu erfassenden Variablen. Sie können [=] verwenden, um alle lokalen Variablen nach Wert zu erfassen, [&], um alle lokalen Variablen nach Referenz zu erfassen, oder [Variable1, Variable2,...], um bestimmte Variablen zu erfassen. Lambda-Ausdrücke können nur auf erfasste Variablen zugreifen, den ursprünglichen Wert jedoch nicht ändern.

Was sind die Vor- und Nachteile von Abschlüssen in C++-Funktionen? Was sind die Vor- und Nachteile von Abschlüssen in C++-Funktionen? Apr 25, 2024 pm 01:33 PM

Ein Abschluss ist eine verschachtelte Funktion, die auf Variablen im Bereich der äußeren Funktion zugreifen kann. Zu ihren Vorteilen gehören Datenkapselung, Zustandserhaltung und Flexibilität. Zu den Nachteilen gehören der Speicherverbrauch, die Auswirkungen auf die Leistung und die Komplexität des Debuggens. Darüber hinaus können Abschlüsse anonyme Funktionen erstellen und diese als Rückrufe oder Argumente an andere Funktionen übergeben.

Lösen Sie das durch Schließungen verursachte Speicherverlustproblem Lösen Sie das durch Schließungen verursachte Speicherverlustproblem Feb 18, 2024 pm 03:20 PM

Titel: Durch Abschlüsse und Lösungen verursachte Speicherlecks Einführung: Abschlüsse sind ein sehr verbreitetes Konzept in JavaScript, das internen Funktionen den Zugriff auf Variablen externer Funktionen ermöglicht. Allerdings können Schließungen bei falscher Verwendung zu Speicherverlusten führen. In diesem Artikel wird das durch Schließungen verursachte Speicherverlustproblem untersucht und Lösungen sowie spezifische Codebeispiele bereitgestellt. 1. Durch Schließungen verursachte Speicherlecks Das Merkmal von Schließungen besteht darin, dass interne Funktionen auf Variablen externer Funktionen zugreifen können, was bedeutet, dass in Schließungen referenzierte Variablen nicht durch Müll gesammelt werden. Bei unsachgemäßer Verwendung

Die Beziehung zwischen js und vue Die Beziehung zwischen js und vue Mar 11, 2024 pm 05:21 PM

Die Beziehung zwischen js und vue: 1. JS als Eckpfeiler der Webentwicklung; 2. Der Aufstieg von Vue.js als Front-End-Framework; 3. Die komplementäre Beziehung zwischen JS und Vue; Vue.

Der Einfluss von Funktionszeigern und -abschlüssen auf die Golang-Leistung Der Einfluss von Funktionszeigern und -abschlüssen auf die Golang-Leistung Apr 15, 2024 am 10:36 AM

Die Auswirkungen von Funktionszeigern und -abschlüssen auf die Go-Leistung sind wie folgt: Funktionszeiger: Etwas langsamer als direkte Aufrufe, aber verbessert die Lesbarkeit und Wiederverwendbarkeit. Schließungen: Normalerweise langsamer, kapseln aber Daten und Verhalten. Praktischer Fall: Funktionszeiger können Sortieralgorithmen optimieren und Abschlüsse können Ereignishandler erstellen, aber sie bringen Leistungseinbußen mit sich.

Die Rolle des Golang-Funktionsschlusses beim Testen Die Rolle des Golang-Funktionsschlusses beim Testen Apr 24, 2024 am 08:54 AM

Funktionsabschlüsse der Go-Sprache spielen beim Unit-Testen eine wichtige Rolle: Werte erfassen: Abschlüsse können auf Variablen im äußeren Bereich zugreifen, sodass Testparameter erfasst und in verschachtelten Funktionen wiederverwendet werden können. Vereinfachen Sie den Testcode: Durch die Erfassung von Werten vereinfachen Abschlüsse den Testcode, indem sie die Notwendigkeit beseitigen, Parameter für jede Schleife wiederholt festzulegen. Verbessern Sie die Lesbarkeit: Verwenden Sie Abschlüsse, um die Testlogik zu organisieren und so den Testcode klarer und leichter lesbar zu machen.

See all articles