Les questions d'entretien sont un sujet qui préoccupe beaucoup les entreprises de recrutement et les développeurs. Les entreprises espèrent les utiliser pour comprendre le véritable niveau des développeurs et leur capacité à gérer les détails, tandis que les développeurs espèrent pouvoir démontrer leur niveau au maximum. mesure (même au-delà de la norme). Cet article propose de nombreuses questions d'entretien de développement front-end, qui valent la peine d'être lues à la fois par les recruteurs et les candidats !
Avant-propos
Je viens de démissionner il y a quelques années. J'aimerais partager avec vous une question d'entretien que j'ai posée une fois. Cette question est la dernière d'une série de questions d'entretien préliminaires que j'ai posées. Elle est utilisée pour évaluer l'intervieweur. capacité JavaScript complète. C'est dommage que depuis près de deux ans, presque personne n'ait été en mesure de répondre complètement. Ce n'est pas que c'est difficile, mais parce que la plupart des intervieweurs le sous-estiment.
Le sujet est le suivant :
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();
La réponse est :
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
Cette question est basée sur mon expérience précédente en développement et les différents pièges que j'ai rencontrés en JS. Cette question implique de nombreux points de connaissance, notamment la promotion de la définition des variables, le pointage de ce pointeur, la priorité de l'opérateur, le prototype, l'héritage, la pollution des variables globales, la priorité des attributs d'objet et des attributs de prototype, etc.
Cette question contient 7 questions, veuillez les expliquer ci-dessous.
Première question
Regardons d'abord ce que nous avons fait dans la première moitié de cette question. Nous avons d'abord défini une fonction appelée Foo, puis créé une propriété statique appelée getName pour Foo pour stocker une fonction anonyme, puis créé un nouvel objet prototype pour Foo. . Une fonction anonyme appelée getName. Ensuite, une fonction getName est créée via l'expression de variable de fonction, et enfin une fonction getName est déclarée.
La première question, Foo.getName, accède naturellement aux propriétés statiques stockées sur la fonction Foo, qui vaut naturellement 2. Il n'y a rien à dire.
Deuxième question
La deuxième question est d'appeler directement la fonction getName. Puisqu'il est appelé directement, il accède à la fonction appelée getName dans la portée actuelle ci-dessus, cela n'a donc rien à voir avec 1 2 3. De nombreux enquêteurs ont répondu à cette question par 5. Il y a deux pièges ici, l'un est la promotion de la déclaration de variable et l'autre est l'expression de la fonction.
Promotion déclaration variable
C'est-à-dire que toutes les variables déclarées ou fonctions déclarées seront promues en haut de la fonction actuelle.
Par exemple, le code suivant :
console.log('x' in window);//true var x; x = 0;
Lorsque le code est exécuté, le moteur js élèvera l'instruction de déclaration en haut du code et deviendra :
var x; console.log('x' in window);//true x = 0;
Expression de fonction
var getName et function getName sont toutes deux des déclarations de déclaration. La différence est que var getName est une expression de fonction, tandis que la fonction getName est une déclaration de fonction. Pour plus d'informations sur la façon de créer diverses fonctions dans JS, vous pouvez lire les questions classiques de l'entretien de clôture JS que la plupart des gens posent mal. Cet article contient des explications détaillées.
Le plus gros problème avec les expressions de fonction est que js divisera ce code en deux lignes de code et les exécutera séparément.
Par exemple, le code suivant :
console.log(x);//输出:function x(){} var x=1; function x(){}
Le code réellement exécuté consiste d'abord à diviser var x=1 en deux lignes : var x; et x = 1;, puis à élever les deux lignes var x et function x(){} vers le haut pour devenir :
var x; function x(){} console.log(x); x=1;
Ainsi, le x déclaré par la fonction finale couvre le x déclaré par la variable, et la sortie du journal est la fonction x.
De même, l'exécution finale du code dans la question d'origine est :
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
Troisième question
Dans la troisième question, Foo().getName(); exécute d'abord la fonction Foo, puis appelle la fonction d'attribut getName de l'objet de valeur de retour de la fonction Foo.
La première phrase de la fonction Foo getName = function () { alert (1 }); Notez qu'elle n'a pas de déclaration var, recherchez donc d'abord la variable getName dans le Foo actuel. portée de la fonction, et il n’y en a pas. Regardez ensuite la couche supérieure de la portée de la fonction actuelle, c'est-à-dire la portée externe, pour voir si elle contient la variable getName. Elle est trouvée, qui est la fonction alert(4) dans la deuxième question. variable à function(){alert(1) }.
Ici, la fonction getName dans la portée externe est en fait modifiée.
Remarque : s'il n'est toujours pas trouvé ici, il recherchera jusqu'à l'objet window. S'il n'y a pas d'attribut getName dans l'objet window, créez une variable getName dans l'objet window.
Après cela, la valeur de retour de la fonction Foo est la suivante, et il y a déjà de nombreux articles sur ce problème de JS dans le jardin des blogs, donc je n'entrerai pas plus dans les détails ici.
Pour faire simple, l'intérêt de ceci est déterminé par la méthode d'appel de la fonction. Dans la méthode d'appel direct ici, cela pointe vers l'objet window.
La fonction Foo renvoie l'objet window, ce qui équivaut à exécuter window.getName(), et le getName dans la fenêtre a été modifié en alert(1), donc 1 sera affiché à la fin
Deux points de connaissance sont examinés ici, l'un est la question de la portée variable et l'autre est la question de ce pointage.
Quatrième question
Appelez directement la fonction getName, ce qui équivaut à window.getName(), car cette variable a été modifiée lors de l'exécution de la fonction Foo, donc le résultat est le même que la troisième question, qui est
Cinquième question
La cinquième question est new Foo.getName(); ce qui est examiné ici est le problème de priorité de l'opérateur de js.
priorité des opérateurs js :
En consultant le tableau ci-dessus, on peut savoir que la priorité du point (.) est supérieure à la nouvelle opération, ce qui équivaut à :
nouveau (Foo.getName)();
Ainsi, la fonction getName est en fait exécutée en tant que constructeur, et 2 apparaît.
Question 6
La sixième question est new Foo().getName(). Tout d'abord, les crochets de priorité des opérateurs sont supérieurs à new. L'exécution réelle est
.
(new Foo()).getName()
Ensuite, la fonction Foo est exécutée en premier, et Foo, en tant que constructeur, a une valeur de retour, nous devons donc ici expliquer le problème de la valeur de retour du constructeur en js.
La valeur de retour du constructeur
Dans les langages traditionnels, les constructeurs ne doivent pas avoir de valeur de retour. La valeur de retour de l'exécution réelle est l'objet instancié de ce constructeur.
En js, les constructeurs peuvent avoir des valeurs de retour ou non.
1. S'il n'y a pas de valeur de retour, l'objet instancié sera renvoyé comme dans les autres langages.
2. S'il existe une valeur de retour, vérifiez si la valeur de retour est un type de référence. S'il s'agit d'un type non référence, tel qu'un type de base (chaîne, nombre, booléen, nul, non défini), cela équivaut à aucune valeur de retour et son objet instancié est effectivement renvoyé.
3. Si la valeur de retour est un type de référence, la valeur de retour réelle est ce type de référence.
Dans la question d'origine, this est renvoyé, et cela représente à l'origine l'objet instancié actuel dans le constructeur, donc la fonction Foo renvoie finalement l'objet instancié.
Appelez ensuite la fonction getName de l'objet instancié. Comme aucun attribut n'est ajouté à l'objet instancié dans le constructeur Foo, nous recherchons getName dans l'objet prototype de l'objet actuel et le trouvons.
Le résultat final est 3.
Question 7
La septième question, new new Foo().getName(); est également un problème prioritaire pour l'opérateur.
L'exécution finale réelle est :
nouveau ((nouveau Foo()).getName)();
Initialisez d'abord l'objet instancié de Foo, puis utilisez à nouveau la fonction getName sur son prototype comme constructeur new.
Le résultat final est 3
Enfin
En ce qui concerne la réponse aux questions, on peut répondre correctement à la première question 100 % du temps, à la deuxième question on ne peut répondre correctement que 50 % du temps, à la troisième question on ne peut pas répondre correctement, et le la quatrième question peut recevoir une réponse très, très rarement. En fait, il n’existe pas beaucoup d’utilisations délicates et bizarres pour cette question. Ce sont tous des scénarios que vous pouvez rencontrer. La plupart des personnes ayant 1 à 2 ans d’expérience professionnelle devraient avoir tout à fait raison.
Je peux seulement dire que certaines personnes sont trop impatientes et dédaigneuses. J'espère que tout le monde pourra comprendre certaines fonctionnalités de js à travers cet article.