Question Est-ce que (a == 1 && a == 2 && a == 3) peut jamais être vrai?


Note du modérateur: S'il vous plaît résister à l'envie de modifier le code ou supprimer cet avis. Le motif des espaces blancs peut faire partie de la question et ne devrait donc pas être altéré inutilement. Si vous êtes dans le camp «les espaces sont insignifiants», vous devriez être en mesure d'accepter le code tel quel.

Est-il possible que (a== 1 && a ==2 && a==3) pourrait évaluer à true en JavaScript?

Ceci est une question d'entrevue posée par une grande entreprise de technologie. C'est arrivé il y a deux semaines, mais j'essaie toujours de trouver la réponse. Je sais que nous n'écrivons jamais un tel code dans notre travail quotidien, mais je suis curieux.


2250
2018-01-15 20:20


origine


Réponses:


Si vous profitez de Comment == travaux, vous pouvez simplement créer un objet avec une coutume toString (ou valueOf) fonction qui change ce qu'elle retourne chaque fois qu'elle est utilisée de telle sorte qu'elle satisfait toutes les trois conditions.

const a = {
  i: 1,
  toString: function () {
    return a.i++;
  }
}

if(a == 1 && a == 2 && a == 3) {
  console.log('Hello World!');
}


La raison pour laquelle cela fonctionne est due à l'utilisation de l'opérateur d'égalité lâche. Lors de l'utilisation d'une égalité lâche, si l'un des opérandes est d'un type différent de l'autre, le moteur tentera de les convertir l'un vers l'autre. Dans le cas d'un objet à gauche et d'un nombre à droite, il tentera de convertir l'objet en un nombre en appelant d'abord valueOf si elle est appelable, et à défaut, elle appellera toString. j'ai utilisé toString dans ce cas simplement parce que c'est ce qui m'est venu à l'esprit, valueOf aurait plus de sens. Si j'ai plutôt renvoyé une chaîne de toString, le moteur aurait alors tenté de convertir la chaîne en un nombre nous donnant le même résultat final, mais avec un chemin légèrement plus long.


3091
2018-01-15 20:35



Je ne pouvais pas résister - les autres réponses sont indubitablement vraies, mais vous ne pouvez vraiment pas passer le code suivant:

var aᅠ = 1;
var a = 2;
var ᅠa = 3;
if(aᅠ==1 && a== 2 &&ᅠa==3) {
    console.log("Why hello there!")
}

Notez l'espacement étrange dans le if déclaration (que j'ai copié de votre question). C'est le Hangul demi-largeur (coréen pour ceux qui ne sont pas familiers) qui est un caractère espace Unicode qui n'est pas interprété par le script ECMA comme un caractère espace - cela signifie que c'est un caractère valide pour un identifiant. Par conséquent, il existe trois variables complètement différentes, l'une avec le Hangul après le a, l'autre avec le précédent et le dernier avec juste un. Remplacer l'espace avec _ pour la lisibilité, le même code ressemblerait à ceci:

var a_ = 1;
var a = 2;
var _a = 3;
if(a_==1 && a== 2 &&_a==3) {
    console.log("Why hello there!")
}

Check-out la validation sur le validateur de noms de variables de Mathias. Si cet espacement étrange était effectivement inclus dans leur question, je suis sûr que c'est un indice pour ce genre de réponse.

Ne fais pas ça. Sérieusement.

Edit: Il est venu à mon attention que (bien que pas autorisé à démarrer une variable) le Zero-width menuisier et Non-jointeur à largeur nulle les caractères sont également autorisés dans les noms de variables - voir Obfuscation de JavaScript avec des caractères de largeur nulle - avantages et inconvénients?.

Cela ressemblerait à ceci:

var a= 1;
var a‍= 2; //one zero-width character
var a‍‍= 3; //two zero-width characters (or you can use the other one)
if(a==1&&a‍==2&&a‍‍==3) {
    console.log("Why hello there!")
}


1914
2018-01-16 05:14



C'EST POSSIBLE!

var i = 0;

with({
  get a() {
    return ++i;
  }
}) {
  if (a == 1 && a == 2 && a == 3)
    console.log("wohoo");
}

Cela utilise un getter à l'intérieur d'un with déclaration à laisser a évaluer à trois valeurs différentes.

... cela ne veut toujours pas dire que cela devrait être utilisé en code réel ...


565
2018-01-15 20:35



Exemple sans getters ou valueOf:

a = [1,2,3];
a.join = a.shift;
console.log(a == 1 && a == 2 && a == 3);

Cela fonctionne parce que == invoque toString qui appelle .join pour les tableaux.

Une autre solution, en utilisant Symbol.toPrimitive qui est un équivalent ES6 de toString/valueOf:

let a = {[Symbol.toPrimitive]: ((i) => () => ++i) (0)};

console.log(a == 1 && a == 2 && a == 3);


428
2018-01-17 11:37



Si on lui demande si c'est possible (pas MUST), il peut demander à "a" de retourner un nombre aléatoire. Ce serait vrai s'il génère 1, 2 et 3 séquentiellement.

with({
  get a() {
    return Math.floor(Math.random()*4);
  }
}){
  for(var i=0;i<1000;i++){
    if (a == 1 && a == 2 && a == 3){
      console.log("after " + (i+1) + " trials, it becomes true finally!!!");
      break;
    }
  }
}


249
2018-01-16 06:21



Quand vous ne pouvez rien faire sans expressions régulières:

var a = {
  r: /\d/g, 
  valueOf: function(){
    return this.r.exec(123)[0]
  }
}

if (a == 1 && a == 2 && a == 3) {
    console.log("!")
}

Cela fonctionne en raison de la coutume valueOf méthode qui est appelée lorsque l'objet est comparé à la primitive (comme Number). Le truc principal est que a.valueOf renvoie une nouvelle valeur à chaque fois parce qu'il appelle exec sur l'expression régulière avec g drapeau, ce qui provoque la mise à jour lastIndex de cette expression régulière chaque fois que le match est trouvé. Donc la première fois this.r.lastIndex == 0, ça correspond 1 et mises à jour lastIndex: this.r.lastIndex == 1, donc la prochaine fois que regex correspondra 2 etc.


195
2018-01-16 19:35



Cela peut être accompli en utilisant ce qui suit dans la portée globale. Pour nodejs utilisation global au lieu de window dans le code ci-dessous.

var val = 0;
Object.defineProperty(window, 'a', {
  get: function() {
    return ++val;
  }
});
if (a == 1 && a == 2 && a == 3) {
  console.log('yay');
}

Cette réponse abuse des variables implicites fournies par la portée globale dans le contexte d'exécution en définissant un getter pour récupérer la variable.


183
2018-01-15 20:37