Maison > Article > interface Web > Questions d'entretien de clôture JS que vous pouvez mal faire grâce aux compétences accident_javascript
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的输出分别是什么?
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
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; }
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
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 >
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);})();
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){ //... } }; }
先看第一个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. 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 0Idem 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.
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 0Lorsque .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,23. 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.