Maison interface Web js tutoriel Questions d'entretien de clôture JS que vous pouvez mal faire grâce aux compétences accident_javascript

Questions d'entretien de clôture JS que vous pouvez mal faire grâce aux compétences accident_javascript

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

Les questions d'entretien ont évolué à partir du travail

C'est une question que j'ai rencontrée au travail. Elle semblait très intéressante, alors j'en ai fait une question pour les entretiens. J'ai découvert que presque personne ne pouvait répondre correctement à toutes les questions et en expliquer les raisons, alors je l'ai reprise et j'en ai parlé. à ce sujet.

Regardez d'abord le code de la question :

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


Copier après la connexion

Il s'agit d'un problème de fermeture JS très typique. Il contient trois niveaux de fonctions amusantes. Il est particulièrement important de déterminer à quelle fonction amusante correspond la fonction amusante de chaque niveau.

Vous pouvez d'abord écrire les résultats que vous pensez sur papier ou à d'autres endroits, puis développer pour voir quelle est la bonne réponse ?

Réponse

//a: undefined,0,0,0
//b: undefined,0,1,2
//c: undefined,0,1,1
Copier après la connexion

Avez-vous tout bien fait ? Si vous avez répondu correctement, félicitations. Il n'y a presque rien qui puisse vous bloquer dans le problème de fermeture de js s'il n'y a pas de réponse, continuez à analyser.

Il existe plusieurs fonctions en JS

Tout d'abord, ce que vous devez comprendre avant cela, c'est que les fonctions en JS peuvent être divisées en deux types, les fonctions nommées (fonctions nommées) et les fonctions anonymes.

La méthode pour distinguer ces deux fonctions est très simple. Vous pouvez en juger en affichant fn.name Celle avec un nom est une fonction nommée, et celle sans nom est une fonction anonyme

.

Remarque : Le nom de la fonction nommée ne peut pas être obtenu sur les versions inférieures d'IE, et undéfini sera renvoyé. Il est recommandé de tester sur Firefox ou Google Chrome

.

Ou utilisez la méthode get function name compatible IE pour obtenir le nom de la fonction :

/**
  * 获取指定函数的函数名称(用于兼容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;
}
Copier après la connexion

Utilisez la fonction ci-dessus pour tester s'il s'agit d'une fonction anonyme :

Vous pouvez savoir que la variable fn1 est une fonction nommée et fn2 est une fonction anonyme

Plusieurs façons de créer des fonctions

Après avoir parlé des types de fonctions, vous devez également comprendre qu'il existe plusieurs façons de créer des fonctions en JS.

1. Déclarer la fonction

La manière la plus courante et la plus standard de déclarer une fonction, y compris le nom de la fonction et le corps de la fonction.

fonction fn1(){}

2. Créer une expression de fonction anonyme

Créer une variable dont le contenu est une fonction

var fn1=fonction (){}
Notez que la fonction créée à l'aide de cette méthode est une fonction anonyme, c'est-à-dire qu'il n'y a pas de nom de fonction

var fn1=function (){};
getFunctionName(fn1).length;//0 

Copier après la connexion

3. Créez une expression de fonction nommée

Créer une variable contenant une fonction avec un nom

var fn1=function xxcanghai(){};
Remarque : Le nom de fonction d'une expression de fonction nommée ne peut être utilisé qu'à l'intérieur de la fonction créée

C'est-à-dire que la fonction créée à l'aide de cette méthode ne peut utiliser que fn1 et non le nom de fonction de xxcanghai dans la couche externe de la fonction. Le nom de xxcanghai ne peut être utilisé qu'à l'intérieur de la fonction créée

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 >
Copier après la connexion

Vous pouvez voir que le nom de la fonction xxcanghai ne peut pas être utilisé en dehors de la fonction (out), et il n'est pas défini.

Remarque : Définir une fonction au sein d'un objet tel que var o={ fn : function (){…} } est également une expression de fonction

4. Constructeur de fonction

Vous pouvez transmettre une chaîne de fonction au constructeur Function et renvoyer une fonction contenant cette commande de chaîne. Cette méthode crée une fonction anonyme.

5. Fonction auto-exécutable

(function(){alert(1);})();
(function fn1(){alert(1);})();
Copier après la connexion

Les fonctions auto-exécutables appartiennent aux "expressions de fonctions" mentionnées ci-dessus et les règles sont les mêmes

6. Autres façons de créer des fonctions

Bien sûr, il existe d'autres façons de créer des fonctions ou d'exécuter des fonctions. Je n'entrerai pas dans les détails ici, par exemple en utilisant eval, setTimeout, setInterval et d'autres méthodes très courantes. Je n'entrerai pas trop dans l'introduction ici. Ce sont des méthodes non standard, je ne vais pas trop développer ici

.

Quelle est la relation entre les trois fonctions ludiques ?

Après avoir parlé des types de fonctions et des méthodes de création de fonctions, vous pouvez revenir au sujet et regarder cette question d'entretien.

Il y a trois fonctions amusantes dans ce code, la première étape consiste donc à comprendre la relation entre ces trois fonctions amusantes et quelle fonction est la même que quelle fonction.

function fun(n,o) {
 console.log(o)
 return {
  fun:function(m){
   //...
  }
 };
} 
Copier après la connexion

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

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

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

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

函数作用域链的问题

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

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

var o={
 fn:function (){
  console.log(fn);
 }
};
o.fn();//ERROR报错
Copier après la connexion

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

var fn=function (){
 console.log(fn);
};
fn();//function (){console.log(fn);};正确
Copier après la connexion


结论:使用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的输出分别是什么? 
Copier après la connexion

1. La première ligne a

var a = fun(0); a.fun(1); a.fun(3);
On peut savoir que le premier fun(0) appelle la fonction fun de premier niveau. Le deuxième fun(1) est la fonction fun qui appelle la valeur de retour du fun précédent, donc :

Les dernières fonctions fun(1), fun(2), fun(3) appellent toutes la fonction fun de deuxième niveau.

Puis :

Lorsque fun(0) est appelé pour la première fois, o n'est pas défini

Lorsque fun(1) est appelé pour la deuxième fois, m vaut 1. À ce moment, fun ferme n de la fonction externe, c'est-à-dire n=0 pour le premier appel, c'est-à-dire m=1, n =0, et dans La fonction fun de premier niveau fun(1,0) est appelée en interne donc o vaut 0 ;

Lorsque fun(2) est appelé pour la troisième fois, m vaut 2, mais a.fun est toujours appelé, donc n dès le premier appel est toujours fermé, donc la première couche de fun(2,0) est appelée en interne ;Donc o vaut 0

Idem que la quatrième fois ;

C'est-à-dire : la réponse finale est indéfinie,0,0,0

2. La deuxième ligne b

var b = fun(0).fun(1).fun(2).fun(3);//undéfini,?,?,?
Commençons par fun(0). Ce doit être la fonction fun de premier niveau appelée ; et sa valeur de retour est un objet, donc le deuxième fun(1) appelle la fonction fun de deuxième niveau. Les quelques fonctions suivantes sont également appelées fonctions amusantes de deuxième niveau.

Puis :

Lorsque le fun(0) de premier niveau est appelé pour la première fois, o n'est pas défini

;

Lorsque .fun(1) est appelé pour la deuxième fois, m vaut 1. À ce moment, fun ferme n de la fonction externe, c'est-à-dire n=0 pour le premier appel, c'est-à-dire m=1, n=0, et appelle en interne la fonction fun de premier niveau fun(1,0); donc o est 0

Lorsque .fun(2) est appelé pour la troisième fois, m vaut 2. À ce stade, la fonction fun actuelle n'est pas l'objet de retour de la première exécution, mais l'objet de retour de la deuxième exécution. Lorsque la fonction amusante de premier niveau est exécutée pour la deuxième fois, (1,0), donc n=1, o=0, le deuxième n est fermé lors du retour, donc lorsque la fonction amusante de troisième niveau est appelée pour la troisième time, m =2,n=1, c'est-à-dire que la fonction fun de premier niveau fun(2,1) est appelée, donc o vaut 1 ;

Lorsque .fun(3) est appelé pour la quatrième fois, m vaut 3, ce qui ferme le n du troisième appel. De même, l'appel final à la fonction fun de premier niveau est fun(3,2) ; o vaut 2;

C'est la réponse finale : undefined,0,1,2

3. La troisième ligne c

var c = fun(0).fun(1); c.fun(2);//undéfini,?,?,?
Sur la base des deux exemples précédents, nous pouvons savoir :
fun(0) exécute la fonction fun de premier niveau, .fun(1) exécute la fonction fun de deuxième niveau renvoyée par fun(0), l'instruction se termine ici et c stocke le retour de la valeur fun(1) , plutôt que la valeur de retour de fun(0), donc la fermeture dans c est également la valeur de n lorsque fun(1) est exécuté pour la deuxième fois. c.fun(2) exécute la fonction amusante de deuxième niveau renvoyée par fun(1), et c.fun(3) exécute également la fonction amusante de deuxième niveau renvoyée par fun(1).

Puis :

Lorsque le fun(0) de premier niveau est appelé pour la première fois, o n'est pas défini

;

Lorsque .fun(1) est appelé pour la deuxième fois, m vaut 1. À ce moment, fun ferme n de la fonction externe, c'est-à-dire n=0 pour le premier appel, c'est-à-dire m=1, n=0, et appelle en interne la fonction fun de premier niveau fun(1,0); donc o est 0

Lorsque .fun(2) est appelé pour la troisième fois, m vaut 2. À ce moment, la fermeture amusante est n=1 pour le deuxième appel, c'est-à-dire m=2, n=1 et le premier la couche de plaisir est appelée en interne. Function fun(2,1); donc o vaut 1;

La même chose est vraie pour la quatrième fois .fun(3), mais c'est toujours la valeur de retour du deuxième appel, donc la fonction fun de premier niveau fun(3,1) est finalement appelée, donc o est toujours 1

C'est la réponse finale : undefined,0,1,1

Mots ultérieurs

Ce code a été créé à l'origine lors de la réécriture d'un rappel asynchrone en un composant d'appel synchrone. J'ai découvert ce piège et acquis une compréhension plus approfondie des fermetures JS.

Il existe d'innombrables articles sur Internet sur ce que sont les fermetures, mais pour comprendre ce que sont les fermetures, il faut encore les découvrir et les comprendre soi-même dans le code.

Si vous me demandez ce qu'est une fermeture, je pense que la fermeture au sens large signifie qu'une variable est utilisée dans sa propre portée, ce qu'on appelle la fermeture.

Est-ce que tout le monde a répondu correctement ? J'espère que les lecteurs pourront mieux comprendre le phénomène de fermeture grâce à cet article. Si vous avez d'autres idées ou opinions, n'hésitez pas à les corriger et à en discuter.

Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn

Outils d'IA chauds

Undresser.AI Undress

Undresser.AI Undress

Application basée sur l'IA pour créer des photos de nu réalistes

AI Clothes Remover

AI Clothes Remover

Outil d'IA en ligne pour supprimer les vêtements des photos.

Undress AI Tool

Undress AI Tool

Images de déshabillage gratuites

Clothoff.io

Clothoff.io

Dissolvant de vêtements AI

Video Face Swap

Video Face Swap

Échangez les visages dans n'importe quelle vidéo sans effort grâce à notre outil d'échange de visage AI entièrement gratuit !

Outils chauds

Bloc-notes++7.3.1

Bloc-notes++7.3.1

Éditeur de code facile à utiliser et gratuit

SublimeText3 version chinoise

SublimeText3 version chinoise

Version chinoise, très simple à utiliser

Envoyer Studio 13.0.1

Envoyer Studio 13.0.1

Puissant environnement de développement intégré PHP

Dreamweaver CS6

Dreamweaver CS6

Outils de développement Web visuel

SublimeText3 version Mac

SublimeText3 version Mac

Logiciel d'édition de code au niveau de Dieu (SublimeText3)

Recommandé : Excellent projet de détection et de reconnaissance des visages open source JS Recommandé : Excellent projet de détection et de reconnaissance des visages open source JS Apr 03, 2024 am 11:55 AM

La technologie de détection et de reconnaissance des visages est déjà une technologie relativement mature et largement utilisée. Actuellement, le langage d'application Internet le plus utilisé est JS. La mise en œuvre de la détection et de la reconnaissance faciale sur le front-end Web présente des avantages et des inconvénients par rapport à la reconnaissance faciale back-end. Les avantages incluent la réduction de l'interaction réseau et de la reconnaissance en temps réel, ce qui réduit considérablement le temps d'attente des utilisateurs et améliore l'expérience utilisateur. Les inconvénients sont les suivants : il est limité par la taille du modèle et la précision est également limitée ; Comment utiliser js pour implémenter la détection de visage sur le web ? Afin de mettre en œuvre la reconnaissance faciale sur le Web, vous devez être familier avec les langages et technologies de programmation associés, tels que JavaScript, HTML, CSS, WebRTC, etc. Dans le même temps, vous devez également maîtriser les technologies pertinentes de vision par ordinateur et d’intelligence artificielle. Il convient de noter qu'en raison de la conception du côté Web

Quelle est la signification de la fermeture dans l'expression lambda C++ ? Quelle est la signification de la fermeture dans l'expression lambda C++ ? Apr 17, 2024 pm 06:15 PM

En C++, une fermeture est une expression lambda qui peut accéder à des variables externes. Pour créer une fermeture, capturez la variable externe dans l'expression lambda. Les fermetures offrent des avantages tels que la réutilisabilité, la dissimulation des informations et une évaluation paresseuse. Ils sont utiles dans des situations réelles telles que les gestionnaires d'événements, où la fermeture peut toujours accéder aux variables externes même si elles sont détruites.

Comment implémenter la fermeture dans une expression C++ Lambda ? Comment implémenter la fermeture dans une expression C++ Lambda ? Jun 01, 2024 pm 05:50 PM

Les expressions C++ Lambda prennent en charge les fermetures, qui enregistrent les variables de portée de fonction et les rendent accessibles aux fonctions. La syntaxe est [capture-list](parameters)->return-type{function-body}. capture-list définit les variables à capturer. Vous pouvez utiliser [=] pour capturer toutes les variables locales par valeur, [&] pour capturer toutes les variables locales par référence, ou [variable1, variable2,...] pour capturer des variables spécifiques. Les expressions Lambda ne peuvent accéder qu'aux variables capturées mais ne peuvent pas modifier la valeur d'origine.

Quels sont les avantages et les inconvénients des fermetures dans les fonctions C++ ? Quels sont les avantages et les inconvénients des fermetures dans les fonctions C++ ? Apr 25, 2024 pm 01:33 PM

Une fermeture est une fonction imbriquée qui peut accéder aux variables dans la portée de la fonction externe. Ses avantages incluent l'encapsulation des données, la conservation de l'état et la flexibilité. Les inconvénients incluent la consommation de mémoire, l’impact sur les performances et la complexité du débogage. De plus, les fermetures peuvent créer des fonctions anonymes et les transmettre à d'autres fonctions sous forme de rappels ou d'arguments.

Résoudre le problème de fuite de mémoire causé par les fermetures Résoudre le problème de fuite de mémoire causé par les fermetures Feb 18, 2024 pm 03:20 PM

Titre : Fuites de mémoire causées par les fermetures et solutions Introduction : Les fermetures sont un concept très courant en JavaScript, qui permettent aux fonctions internes d'accéder aux variables des fonctions externes. Cependant, les fermetures peuvent provoquer des fuites de mémoire si elles ne sont pas utilisées correctement. Cet article explorera le problème de fuite de mémoire provoqué par les fermetures et fournira des solutions et des exemples de code spécifiques. 1. Fuites de mémoire causées par les fermetures La caractéristique des fermetures est que les fonctions internes peuvent accéder aux variables des fonctions externes, ce qui signifie que les variables référencées dans les fermetures ne seront pas récupérées. S'il est mal utilisé,

La relation entre js et vue La relation entre js et vue Mar 11, 2024 pm 05:21 PM

La relation entre js et vue : 1. JS comme pierre angulaire du développement Web ; 2. L'essor de Vue.js en tant que framework front-end ; 3. La relation complémentaire entre JS et Vue ; Vue.

Appels enchaînés et fermetures de fonctions PHP Appels enchaînés et fermetures de fonctions PHP Apr 13, 2024 am 11:18 AM

Oui, la simplicité et la lisibilité du code peuvent être optimisées grâce à des appels et des fermetures enchaînés : les appels en chaîne lient les appels de fonction dans une interface fluide. Les fermetures créent des blocs de code réutilisables et accèdent à des variables en dehors des fonctions.

Le rôle de la fermeture de la fonction Golang dans les tests Le rôle de la fermeture de la fonction Golang dans les tests Apr 24, 2024 am 08:54 AM

Les fermetures de fonctions du langage Go jouent un rôle essentiel dans les tests unitaires : Capture de valeurs : les fermetures peuvent accéder aux variables dans la portée externe, permettant ainsi de capturer et de réutiliser les paramètres de test dans des fonctions imbriquées. Simplifiez le code de test : en capturant les valeurs, les fermetures simplifient le code de test en éliminant le besoin de définir des paramètres à plusieurs reprises pour chaque boucle. Améliorez la lisibilité : utilisez des fermetures pour organiser la logique de test, rendant ainsi le code de test plus clair et plus facile à lire.

See all articles