23 questions difficiles pour les interviews JavaScript

Vous voulez vous préparer pour un entretien JavaScript et vous cherchez des questions pour vous entraîner? Si oui, considérez que votre recherche est terminée. L'auteur du matériel, dont nous publions la traduction aujourd'hui, a déclaré avoir collecté plus de deux douzaines de questions sur JavaScript destinées à ceux qui souhaitent passer d'un junior à un senior, à ceux qui cherchent à réussir une interview dans le domaine du développement front-end et à obtenir une offre intéressante de l'employeur.


image

1. Expliquer les fonctionnalités de validation de l'égalité JavaScript


Difficulté: *


JavaScript a deux opérateurs pour vérifier l'égalité des valeurs. Le premier est l'opérateur dit d'égalité stricte. Le second est l'opérateur d'égalité sans restriction, qui peut être utilisé pour convertir les types de quantités vérifiées.

  • L'opérateur d'égalité stricte ( ===) vérifie l'égalité des valeurs sans effectuer de conversions de type.
  • L'opérateur d'égalité non strict ( ==) vérifie l'égalité des valeurs, les convertissant en un type commun.

var a = "42";
var b = 42;

a == b;         // true
a === b;        // false

Voici quelques directives pour l'utilisation de divers vérificateurs d'égalité en JavaScript:

  • Si l'une des valeurs comparées peut être une valeur trueou false- essayez d'éviter l'opérateur ==. Utilisez un opérateur ===.
  • Utilisez l'opérateur ===dans le cas où si vous travaillez avec les valeurs suivantes: 0, «»ou [](tableau vide).
  • Dans tous les autres cas, vous pouvez utiliser l'opérateur en toute sécurité ==. De plus, cela est non seulement sûr, mais contribue également à simplifier le code et à améliorer sa lisibilité.

Source

2. Donnez des exemples de transtypage en un type logique de valeurs qui ne sont pas liées à ce type


Difficulté: ***


L'essence de cette question est de savoir quelles valeurs, dans le cas de leur conversion en un type logique, deviennent false, et lesquelles - en true.

Voici une liste de valeurs que l'on peut appeler «falsy». Ils, lors de la conversion en un type logique, se transforment en valeur false:

  • «» (ligne vide).
  • 0, -0, NaN(Pas un nombre).
  • null, undefined.

"Faux" est une signification logique false.

Toute valeur qui n'est pas incluse dans cette liste, lorsqu'elle est convertie en un type logique, se transforme en true(ces valeurs sont appelées "true" - truey). Par exemple:

  • «hello».
  • 42.
  • [ ], [ 1, «2», 3 ](tableaux).
  • { }, { a: 42 }(objets).
  • function foo() { .. } (les fonctions).

«Vrai» est également une signification logique true.

Source

3. Qu'est-ce que l'IIFE?


Difficulté: ***


IIFE (Immediately Invoked Function Expression) est une expression fonctionnelle immédiatement invoquée. Une telle expression est exécutée immédiatement après sa création.

(function IIFE(){
    console.log( "Hello!" );
})();
// "Hello!"

Ce modèle est souvent utilisé pour empêcher la pollution de l'espace de noms global. Le fait est que les variables déclarées dans IIFE (comme dans toute autre fonction ordinaire) sont invisibles en dehors de cette fonction.

Source

4. Quand dois-je utiliser les fonctions fléchées apparues dans ES6?


Difficulté: ***


Voici des règles simples pour utiliser les différentes façons de déclarer des fonctions que je respecte lors du développement de code pour des environnements qui prennent en charge ES6 et des normes plus récentes:

  • Utilisez le mot clé functiondans la portée globale et pour les propriétés Object.prototype.
  • Utilisez le mot-clé functionpour les constructeurs d'objets.
  • Dans d'autres cas, utilisez les fonctions fléchées.

Comme vous pouvez le voir, il est recommandé d'utiliser les fonctions fléchées presque partout. Il y a plusieurs raisons à cet état de fait:

  • Travail pratique avec le contexte. Les fonctions fléchées utilisent la valeur du thiscontexte environnant sans avoir la leur this. Si ces fonctions sont utilisées séquentiellement, sans utiliser de fonctions ordinaires dans des constructions complexes, cela garantit un travail sûr avec le contexte.
  • Compacité. Le code de la fonction flèche est plus facile à saisir et à lire. Cet avantage des fonctions fléchées par rapport aux fonctions ordinaires vous semblera peut-être controversé et dépend du point de vue de chaque développeur spécifique.
  • Clarté du code. Si presque tout le code est représenté par des fonctions fléchées, toute fonction ordinaire se distingue dans un tel code en créant son propre contexte. À l'aide des fonctions fléchées, le programmeur crée un code plus compréhensible dans lequel il est plus facile de travailler qu'avec du code sans fonctions fléchées this.

Source

5. Quelle est la différence entre les classes ES6 et les constructeurs de fonctions?


Difficulté: ***


Voyons d'abord quelques exemples.

Fonction constructeur:

function Person(name) {
  this.name = name;
}

Classe ES6:

class Person {
  constructor(name) {
    this.name = name;
  }
}

Lorsqu'il s'agit de créer des objets simples, les constructeurs et les classes utilisés à cet effet sont très similaires.

La principale différence entre les constructeurs et les classes apparaît lors de l'utilisation de l'héritage. Si nous devons créer une classe Studentqui est une sous-classe de la classe Personet ajouter un champ à cette nouvelle classe studentId, voici à quoi ressemblera le code dans lequel les constructeurs sont utilisés et le code dans lequel les classes sont utilisées.

Fonction constructeur:

function Student(name, studentId) {
  //      ,   .
  Person.call(this, name);

  //    .
  this.studentId = studentId;
}

Student.prototype = Object.create(Person.prototype);
Student.prototype.constructor = Student;

Classe ES6:

class Student extends Person {
  constructor(name, studentId) {
    super(name);
    this.studentId = studentId;
  }
}

Source

6. Parlez-nous de la méthode Function.prototype.bind ().


Difficulté: ***


Pour citer MDN: «La méthode bind()crée une nouvelle fonction qui, lorsqu'elle est appelée, définit la thisvaleur fournie comme contexte d'exécution . L'ensemble d'arguments est également transmis à la méthode, qui sera définie avant les arguments passés à la fonction liée lors de son appel. »

Je crois que cette méthode. bind()particulièrement utile pour lier des valeurs thisdans des méthodes de classe qui doivent être passées à d'autres fonctions. Cette technique est souvent utilisée dans les composants React.

Source 

7. À quoi servent les fonctions anonymes?


Difficulté: ***


Les fonctions anonymes sont utilisées pour créer des constructions IIFE, les variables déclarées dans lesquelles ne polluent pas la portée globale.

(function() {
  // - .
})();

Les fonctions anonymes sont utilisées comme fonctions de rappel, qui ne sont utilisées qu'à un seul endroit du programme. Le code sera plus autonome et plus lisible si le rappel est annoncé directement à l'endroit où il est utilisé. Cela élimine le besoin de regarder le code à la recherche du corps de la fonction.

setTimeout(function() {
  console.log('Hello world!');
}, 1000);

Les fonctions anonymes sont utilisées de manière pratique dans des constructions spécifiques au style de programmation fonctionnelle, ou lorsque vous travaillez avec des bibliothèques comme Lodash (ce cas d'utilisation est similaire à leur utilisation en tant que rappels).

const arr = [1, 2, 3];
const double = arr.map(function(el) {
  return el * 2;
});
console.log(double); // [2, 4, 6]

Source

8. Quelle est la différence entre la méthode Object.freeze () et le mot clé const?


Difficulté: ***


Le mot const- clé et la méthode Object.freeze()sont des choses complètement différentes.

Le mot-clé consts'applique aux liaisons (aux "variables"). Il crée une liaison immuable, c'est-à-dire qu'il est constimpossible de lier quelque chose de nouveau à une variable (constante) déclarée à l'aide d'un mot clé . Une constante ne peut pas être affectée à une nouvelle valeur.

const person = {
    name: "Leonardo"
};
let animal = {
    species: "snake"
};
person = animal; // Uncaught TypeError: Assignment to constant variable.

La méthode Object.freeze()fonctionne avec des valeurs. Ou plutôt, avec des valeurs d'objet. Il rend l'objet immuable, ce qui protège contre les modifications de la valeur des propriétés de cet objet.

let person = {
    name: "Leonardo"
};
Object.freeze(person);
person.name = "Lima"; // Uncaught TypeError: Cannot assign to read only property 'name' of object
console.log(person);

Veuillez noter que le message d'erreur s'affiche en mode strict. En mode normal, l'opération de modification de la propriété d'un objet "figé" ne fonctionne tout simplement pas.

Source

9. Qu'est-ce qu'un «générateur»?


Difficulté: ***


Les générateurs sont des fonctions dont vous pouvez «sortir» et dans lesquelles vous pouvez «entrer» si nécessaire. Leur contexte (liaisons variables) est maintenu entre les sessions "d'entrée" en eux. Les générateurs sont déclarés à l'aide d'un mot-clé function*. Une telle fonction, lors de son premier appel, n'exécute pas le code, renvoyant un objet spécial, un générateur, qui vous permet de contrôler son exécution. Pour obtenir la prochaine valeur émise par le générateur, vous devez appeler sa méthode next(). Pour cette raison, le code de fonction est exécuté jusqu'à ce qu'il rencontre un mot clé yieldqui renvoie une valeur.

La fonction générateur peut être appelée autant de fois que vous le souhaitez. Chaque fois qu'un nouveau générateur reviendra. Mais chaque générateur ne peut être contourné qu'une seule fois.

function* makeRangeIterator(start = 0, end = Infinity, step = 1) {
    let iterationCount = 0;
    for (let i = start; i < end; i += step) {
        iterationCount++;
        yield i;
    }
    return iterationCount;
}

Source

10. Quand faut-il utiliser des générateurs?


Difficulté: ***


Si en un mot pour décrire les principales fonctionnalités utiles des générateurs, il s'avère qu'ils sont les suivants:

  • Le code dans lequel le générateur est utilisé détermine le moment où la prochaine valeur est reçue. Le générateur est uniquement responsable du retour des valeurs, il est contrôlé de l'extérieur.
  • Il existe des générateurs asynchrones. Ils vous permettent de travailler avec des flux de données asynchrones.

L'essentiel dans les générateurs est que vous pouvez obtenir la prochaine valeur retournée par le générateur uniquement lorsqu'elle est nécessaire dans le code qui utilise le générateur. Les générateurs ne renvoient pas tout d'un coup. Dans certaines situations, cette fonctionnalité peut être très pratique.

Source

11. Qu'est-ce que «l'augmentation des variables»?


Difficulté: ****


L'essence du concept d '"augmentation des variables" est que les annonces "montent" au sommet de la portée actuelle. Par conséquent, la variable peut être utilisée avant sa déclaration. Seules les déclarations de variables sont déclenchées, mais pas leur code d'initialisation. Notez que le comportement des variables déclarées à l'aide du mot var- clé est différent du comportement des variables et des constantes déclarées à l'aide de letet const.

Source

12. Que produira le code suivant?


Difficulté: ****


var output = (function(x) {
  delete x;
  return x;
})(0);

console.log(output);

Ce code sortira 0. L'opérateur est deleteutilisé pour supprimer les propriétés des objets. Et x- ce n'est pas une propriété d'objet - c'est une variable locale. L'opérateur deleten'affecte pas les variables locales.

Source

13. Que produira le code suivant?


Difficulté: ****


var Employee = {
  company: 'xyz'
}
var emp1 = Object.create(Employee);
delete emp1.company
console.log(emp1.company);

Ce code sortira xyz. Une propriété companyn'est pas une propriété d'un objet emp1, mais une propriété de son prototype. L'opérateur deletene supprime pas les propriétés prototypes des objets. Un objet emp1n'a pas sa propre propriété company. Vous pouvez le vérifier en:

console.log(emp1.hasOwnProperty('company')); // false

Si nous devons encore supprimer cette propriété, vous pouvez le faire soit en contactant directement l'objet Employee( delete Employee.company), soit en contactant le prototype de l'objet à l' emp1aide de sa propriété __proto__( delete emp1.__proto__.company).

Source

14. Parlez-nous du modèle de conception du prototype.


Difficulté: ****


Le prototype est un modèle de conception générique. Il est utilisé pour créer des objets. Les objets créés à l'aide de celui-ci contiennent des valeurs copiées à partir de leur prototype (à partir de l'objet exemple). Ce modèle est également appelé modèle Propriétés.

Un exemple d'utilisation du modèle «prototype» est l'initialisation de certains objets avec des valeurs standard stockées dans la base de données. Ces valeurs enregistrées dans le prototype sont copiées dans de nouveaux objets sans accéder à la base de données.

Il convient de noter que ce modèle est rarement utilisé dans les langues classiques. JavaScript utilise un prototype de modèle d'héritage. Ce motif est utilisé dans la conception de nouveaux objets et de leurs prototypes.

Source

15. Qu'est-ce qu'une «zone morte temporaire» dans ES6?


Difficulté: ****


L'ES6 a effectué la levée des variables et des constantes déclarées à l'aide des mots clés letet const(ceci est fait et la montée de l'entité déclarée à l'aide des mots clés var, classet function). Cependant, le code a une zone qui s'étend de l'entrée de la portée à la déclaration d'une variable ou d'une constante. Lors de l'accès à une variable ou une constante dans cette zone, une erreur sera générée. Il s'agit de la «Zone morte temporelle» (TDZ).

//console.log(aLet)  //  ReferenceError

let aLet;
console.log(aLet); // undefined
aLet = 10;
console.log(aLet); // 10

Dans cet exemple, le TDZ se termine après la déclaration aLet, mais pas une fois la aLetvaleur affectée .

Source

16. Pouvez-vous décrire la principale différence entre les méthodes de tableau forEach () et map ()? Dans quelles situations préféreriez-vous une de ces méthodes à une autre?


Difficulté: ****


Afin de comprendre la différence entre ces méthodes, parlons des caractéristiques de chacune d'entre elles.

Voici comment cela fonctionne .forEach():

  • Il itère sur les éléments du tableau.
  • Il exécute la fonction de rappel qui lui est transmise pour chaque élément du tableau.
  • Il ne retourne rien.

const a = [1, 2, 3];
const doubled = a.forEach((num, index) => {
  //  -  num /  index.
});

// doubled = undefined

Voici une brève description de la méthode .map():

  • Il itère sur les éléments du tableau.
  • Il convertit chaque élément du tableau d'origine en un élément du nouveau tableau, en appelant la fonction qui lui est transmise pour chaque élément du tableau d'origine.

const a = [1, 2, 3];
const doubled = a.map(num => {
  return num * 2;
});

// doubled = [2, 4, 6]

En conséquence, il s'avère que la principale différence entre .forEach()et .map()est qu'elle .map()renvoie un nouveau tableau. Si vous devez obtenir le résultat de la conversion des éléments du tableau d'origine sans modifier ce tableau, vous devez choisir .map(). Si vous avez juste besoin d'itérer sur les éléments du tableau - alors vous pouvez l'utiliser .forEach().

Source

17. Quelle est la différence entre une variable non déclarée, une variable contenant une valeur nulle et une variable non définie? Comment vérifier qu'une variable est non déclarée, ainsi que nulle et non définie?


Difficulté: ****


Une variable non déclarée est créée lorsqu'une valeur est affectée à un identifiant qui n'a pas été précédemment déclaré à l'aide de var, letou const. Les variables non déclarées sont déclarées dans la portée globale, en dehors de la portée actuelle. En mode strict, une exception est levée lors de la tentative d'affectation d'une valeur à une variable non déclarée ReferenceError. L'utilisation de variables non déclarées n'est pas recommandée - tout comme l'utilisation de variables globales n'est pas recommandée. Ils doivent être évités par tous les moyens. Afin de vous protéger des conséquences de l'utilisation de variables non déclarées, utilisez le bloc try/catch.

function foo() {
  x = 1; //     ReferenceError
}

foo();
console.log(x); // 1

Une variable contenant undefinedest une variable déclarée à laquelle aucune valeur n'est affectée. La valeur undefinedforme son propre type de données. Si la fonction ne retourne rien et que le résultat de son appel est écrit dans une variable, alors elle tombera dans cette variable undefined. Pour organiser une vérification undefined, vous pouvez utiliser l'opérateur d'égalité stricte ( ===) ou l'opérateur typeofqui renvoie une chaîne undefined. Veuillez noter que lors de la vérification, undefinedvous ne devez pas utiliser l'opérateur d'égalité non strict ( ==), car il considère que les valeurs undefinedet sont égales null.

var foo;
console.log(foo); // undefined
console.log(foo === undefined); // true
console.log(typeof foo === 'undefined'); // true

console.log(foo == null); // true.        undefined!

function bar() {}
var baz = bar();
console.log(baz); // undefined

Une variable contenant une valeur nulldoit être explicitement définie sur cette valeur. Il symbolise l'absence de sens et diffère de undefined-variable en ce que la valeur qu'il contient lui a été explicitement attribuée. Pour vérifier la valeur on null, il suffit d'utiliser l'opérateur d'égalité stricte. Pour vérifier null, comme dans le cas de la vérification undefined, il ne faut pas utiliser l'opérateur d'égalité non strict, qui considère les valeurs de nullet égales undefined.

var foo = null;
console.log(foo === null); // true
console.log(typeof foo === 'object'); // true

console.log(foo == undefined); // true        null!

J'essaie de ne jamais laisser de variables dans un état non déclaré ou dans un état où elles sont déclarées, mais aucune valeur ne leur est explicitement attribuée. Si je ne vais pas écrire une valeur dans une variable immédiatement après sa déclaration, je lui écris null. Si vous utilisez un linter, il signale généralement des cas d'utilisation de variables non déclarées.

Source

18. Parlez-nous du module de conception "Module ouvert"


Difficulté: *****


Le modèle «Revealing Module» est une variante du modèle «Module». Le but de l'utilisation de ce modèle est de prendre en charge l'encapsulation et de découvrir certaines propriétés et méthodes renvoyées dans le littéral objet. Voici à quoi ressemblera l'implémentation directe de ce modèle:

var Exposer = (function() {
  var privateVariable = 10;

  var privateMethod = function() {
    console.log('Inside a private method!');
    privateVariable++;
  }

  var methodToExpose = function() {
    console.log('This is a method I want to expose!');
  }

  var otherMethodIWantToExpose = function() {
    privateMethod();
  }

  return {
      first: methodToExpose,
      second: otherMethodIWantToExpose
  };
})();

Exposer.first();        // : This is a method I want to expose!
Exposer.second();       // : Inside a private method!
Exposer.methodToExpose; // undefined

L'inconvénient évident de ce modèle est que vous ne pouvez pas utiliser de méthodes privées lors de son utilisation.

Source

19. Quelle est la différence entre les objets Map et WeakMap?


Difficulté: *****


Ces objets se comportent différemment si une variable contenant une référence à un objet qui est la clé de l'une des paires clé / valeur n'est pas disponible. Voici un exemple:

var map = new Map();
var weakmap = new WeakMap();

(function() {
    var a = {
        x: 12
    };
    var b = {
        y: 12
    };

    map.set(a, 1);
    weakmap.set(b, 2);
})()

Une fois l'exécution de l'IIFF terminée, nous n'aurons plus accès aux objets aet b. Par conséquent, le garbage collector supprime la clé bde weakmapet efface la mémoire. Mais le contenu mapreste le même.

En conséquence, il s'avère que les objets WeakMappermettent au garbage collector de se débarrasser des enregistrements qui ne sont pas référencés dans les variables externes. Les objets mapstockent des paires clé / valeur indépendamment de la présence ou de l'absence de références de clé externe. On peut en dire autant de la mise en œuvre de la structure de données à l' Mapaide de tableaux ordinaires. Les WeakMapréférences clés «faibles» sont utilisées. Ils n'interfèrent pas avec le fonctionnement du ramasse-miettes s'il n'y a pas d'autres références à l'objet utilisé comme clé.

Source

20. Comment les paramètres sont-ils transmis à la fonction JavaScript: par référence ou par valeur?


Difficulté: *****


Les paramètres sont toujours passés par valeur, mais les références aux objets sont écrites dans des variables représentant des objets. Par conséquent, lorsqu'un objet est transféré vers une fonction et que la propriété de cet objet est modifiée, cette modification est enregistrée dans l'objet même lorsque la fonction se ferme. En conséquence, on a le sentiment que les paramètres de la fonction sont passés par référence. Mais si vous modifiez la valeur de la variable représentant l'objet, cette modification n'affectera pas les objets qui sont en dehors de la fonction.

Voici un exemple:

function changeStuff(a, b, c)
{
  a = a * 10;
  b.item = "changed";
  c = {item: "changed"};
}

var num = 10;
var obj1 = {item: "unchanged"};
var obj2 = {item: "unchanged"};

changeStuff(num, obj1, obj2);

console.log(num);
console.log(obj1.item);
console.log(obj2.item);

Voici ce que ce code affichera:

10
changed
unchanged

Source

21. Comment organiser un «gel profond» d'un objet?


Difficulté: *****


Afin de fournir le «surgélation» d'un objet à l'aide Object.freeze(), vous devez créer une fonction récursive qui «fige» les propriétés de l'objet, qui sont également des objets.

Voici un exemple de «gel» ordinaire d'un objet:

let person = {
    name: "Leonardo",
    profession: {
        name: "developer"
    }
};
Object.freeze(person); //   
person.profession.name = "doctor";
console.log(person); // { name: 'Leonardo', profession: { name: 'doctor' } }

Voici le «gel profond»:

function deepFreeze(object) {
    let propNames = Object.getOwnPropertyNames(object);
    for (let name of propNames) {
        let value = object[name];
        object[name] = value && typeof value === "object" ?
            deepFreeze(value) : value;
    }
    return Object.freeze(object);
}
let person = {
    name: "Leonardo",
    profession: {
        name: "developer"
    }
};
deepFreeze(person);
person.profession.name = "doctor"; // TypeError: Cannot assign to read only property 'name' of object

Le message d'erreur s'affiche uniquement en mode strict. En mode normal, la valeur ne change pas sans messages d'erreur.

Source

22. Pourquoi les programmeurs JavaScript ont-ils du mal à utiliser le mot-clé this?


Difficulté: *****


La chose la plus importante à comprendre thisest que les fonctions n'ont pas de valeur fixe this. Cette valeur dépend de la façon dont la fonction est appelée. Si nous disons qu'une fonction est appelée avec une valeur spécifique this, cela signifie que cette valeur est déterminée non pas lors de la déclaration de la fonction, mais lors de son appel. Voici quelques fonctionnalités this:

  • Si la fonction est appelée sous la forme habituelle (c'est-à-dire en utilisant la construction de vue someFunc()), elle thisse référera à l'objet global (dans le navigateur ceci window). Si le code est exécuté en mode strict, une thisvaleur sera écrite undefined.
  • Si la fonction est appelée comme méthode d'un objet, le mot-clé thissera représenté par l'objet auquel appartient la méthode.
  • call apply, this , call apply.
  • , this .
  • , new, this , prototype -.
  • Si la fonction a été créée à l'aide de la méthode bind , le mot this- clé function sera lié de manière rigide à la valeur transmise en bindtant que premier argument. Il s'agit de la seule exception à la règle selon laquelle les fonctions n'ont pas de valeur codée en dur this. Les fonctions créées à l'aide bindsont immuables this.

Source

23. Comparez l'utilisation de la construction et des générateurs async / attendent pour implémenter la même fonctionnalité


Difficulté: *****


  • Lors de l'itération d'un générateur à l'aide d'une méthode, .next()chaque appel à cette méthode renvoie une valeur unique à l'aide du mot clé yield. Lors de l'utilisation de la construction async / attendent, les expressions attendent sont exécutées séquentiellement.
  • La conception asynchrone / attente simplifie la mise en œuvre d'un cas d'utilisation de générateur spécifique.
  • Les valeurs renvoyées par le générateur ont toujours la forme {value: X, done: Boolean}et les fonctions asynchrones renvoient les promesses résolues avec la valeur X, ou échouent.
  • Une fonction asynchrone peut être convertie en générateur à l'aide de promesses. Voici un exemple d'une telle conversion.

Voici la fonction asynchrone:

//  
async function init() {
    const res1 = await doTask1();
    console.log(res1);

    const res2 = await doTask2(res1);
    console.log(res2);

    const res3 = await doTask3(res2);
    console.log(res3);

    return res3;
}

init();

Voici un générateur similaire.

//    
function runner(genFn) {
    const itr = genFn();

    function run(arg) {
        let result = itr.next(arg);

        if (result.done) {
            return result.value;
        } else {
            return Promise.resolve(result.value).then(run);
        }
    }

    return run;
}

//   runner    
runner(function* () {
    const res1 = await doTask1();
    console.log(res1);

    const res2 = await doTask2(res1);
    console.log(res2);

    const res3 = await doTask3(res2);
    console.log(res3);

    return res3;
});

Source

Chers lecteurs! Quelles questions JavaScript avez-vous posées lors de vos entretiens?


All Articles