web-dev-qa-db-fra.com

(A == 1 && a == 2 && a == 3) peut-il être évalué comme étant vrai?

Note au modérateur: Veuillez résister à l'envie de modifier le code ou de supprimer cette notification. La structure des espaces peut faire partie de la question et ne doit donc pas être altérée inutilement. Si vous êtes dans le camp "Les espaces blancs sont insignifiants", vous devriez pouvoir accepter le code tel quel.

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

Ceci est une question 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.

2331

Si vous tirez parti de comment fonctionne == , vous pouvez simplement créer un objet avec une fonction personnalisée toString (ou valueOf) qui modifie ce qu'il retourne chaque fois qu'il est utilisé de sorte qu'il réponde aux 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é en vrac. 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 tente de convertir l'un en 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 s'il est appelable. Sinon, il appellera toString. J'ai utilisé toString dans ce cas simplement parce que c'est ce qui me venait à l'esprit, valueOf aurait plus de sens. Si je renvoyais plutôt 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, avec un chemin légèrement plus long.

3185
Kevin B

Je ne pouvais pas résister - les autres réponses sont sans doute 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 l'instruction if (que j'ai copiée à partir de votre question). C'est le Hangul demi-largeur (c'est-à-dire coréen pour ceux qui ne sont pas familiers) qui est un caractère d'espace Unicode qui n'est pas interprété par le script ECMA comme un caractère d'espace. Cela signifie qu'il s'agit d'un caractère valide pour un identifiant. Par conséquent, il existe trois variables complètement différentes, une avec le Hangul après le a, une avec le précédent et le dernier avec un seul. En remplaçant l'espace par _ pour plus de 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!")
}

Vérifier la validation sur le validateur de nom de variable de Mathias . Si cet espacement étrange était réellement 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.

Éditer: Il m’est venu à mon attention que (bien qu’il ne soit pas permis de démarrer une variable) les menuisiers largeur nulle et non-jointeurs à largeur nulle sont également autorisés dans les noms de variable - voir obscurcissement 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!")
}

1969
Jeff

CELA IS POSSIBLE!

var i = 0;

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

Ceci utilise un getter à l'intérieur d'une instruction with pour laisser a évaluer trois valeurs différentes.

... cela ne signifie toujours pas que cela devrait être utilisé dans du code réel ...

Pire encore, cette astuce fonctionnera également avec l'utilisation de ===.

  var i = 0;

  with({
    get a() {
      return ++i;
    }
  }) {
    if (a !== a)
      console.log("yep, this is printed.");
  }

591
Jonas Wilms

Exemple sans getters ni valueOf:

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

Cela fonctionne parce que == appelle 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);

458
georg

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

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;
    }
  }
}

259
mmmaaa

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 à cause de la méthode valueOf personnalisée appelée lorsque Object est comparée à une primitive (telle que Number). L'astuce principale est que a.valueOf renvoie chaque fois une nouvelle valeur car il appelle exec sur une expression régulière avec l'indicateur g, ce qui provoque la mise à jour lastIndex de cette expression régulière chaque fois qu'une correspondance est trouvée. Donc, la première fois this.r.lastIndex == 0, elle correspond à 1 et met à jour lastIndex: this.r.lastIndex == 1; la prochaine fois que regex correspondra à 2 et ainsi de suite.

203
Kos

Cela peut être accompli en utilisant ce qui suit dans la portée globale. Pour nodejs, utilisez 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 extraire la variable.

186
jontro

Cela est possible dans le cas où la variable a est utilisée par deux travailleurs Web via un SharedArrayBuffer, ainsi que par un script principal. La possibilité est faible, mais il est également possible que, lorsque le code est compilé en code machine, les travailleurs Web mettent à jour la variable a juste à temps afin que les conditions a==1, a==2 et a==3 soient satisfaites.

Cela peut être un exemple de condition de concurrence critique dans un environnement multithread fourni par les travailleurs Web et SharedArrayBuffer en JavaScript.

Voici l'implémentation de base de ci-dessus:

main.js

// Main Thread

const worker = new Worker('worker.js')
const modifiers = [new Worker('modifier.js'), new Worker('modifier.js')] // Let's use 2 workers
const sab = new SharedArrayBuffer(1)

modifiers.forEach(m => m.postMessage(sab))
worker.postMessage(sab)

worker.js

let array

Object.defineProperty(self, 'a', {
  get() {
    return array[0]
  }
});

addEventListener('message', ({data}) => {
    array = new Uint8Array(data)
    let count = 0
    do {
        var res = a == 1 && a == 2 && a == 3
        ++count
    } while(res == false) // just for clarity. !res is fine
    console.log(`It happened after ${count} iterations`)
    console.log('You should\'ve never seen this')
})

modificateur.js

addEventListener('message' , ({data}) => {
    setInterval( () => {
        new Uint8Array(data)[0] = Math.floor(Math.random()*3) + 1
    })
})

Sur mon MacBook Air, cela se produit après environ 10 milliards d'itérations au premier essai:

 enter image description here

Deuxième essai:

 enter image description here

Comme je l'ai dit, les chances seront faibles, mais si on dispose de suffisamment de temps, la situation sera touchée.

Conseil: Si cela prend trop de temps sur votre système. Essayez uniquement a == 1 && a == 2 et remplacez Math.random()*3 par Math.random()*2. Ajouter de plus en plus à la liste diminue les chances de frapper.

182
mehulmpt

Ceci est également possible en utilisant une série de getters auto-écrasants:

(Ceci est similaire à la solution de jontro, mais ne nécessite pas de variable de compteur.)

(() => {
    "use strict";
    Object.defineProperty(this, "a", {
        "get": () => {
            Object.defineProperty(this, "a", {
                "get": () => {
                    Object.defineProperty(this, "a", {
                        "get": () => {
                            return 3;
                        }
                    });
                    return 2;
                },
                configurable: true
            });
            return 1;
        },
        configurable: true
    });
    if (a == 1 && a == 2 && a == 3) {
        document.body.append("Yes, it’s possible.");
    }
})();

145
Patrick Dark

Je ne vois pas cette réponse déjà postée, alors je vais aussi inclure celle-ci. Ceci est similaire à la réponse de Jeff avec l'espace Hangul demi-largeur.

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

Vous remarquerez peut-être une légère différence avec le second, mais le premier et le troisième sont identiques à l'œil nu. Tous les 3 sont des personnages distincts:

a - Minuscule latine A
- Pleine largeur Latin minuscule A
а - Cacillique minuscule A

Le terme générique utilisé est "homoglyphes": différents caractères Unicode ayant le même aspect. Il est généralement difficile d’obtenir trois impossibles à distinguer, mais dans certains cas, vous pouvez avoir de la chance. A, Α, А et Ꭺ fonctionneraient mieux (latin-A, grec alpha , cyrillique-A et cherokee-A respectivement; malheureusement, les lettres minuscules grecque et cherokee sont trop différents du latin a: α, , et n'aide donc pas l'extrait de code ci-dessus).

Il existe toute une classe d'attaques homoglyphes, le plus souvent dans les faux noms de domaine (par exemple, wikipediа.org (cyrillique) vs wikipedia.org (latin)), mais cela peut aussi apparaître dans le code; généralement appelé «sournois» (comme mentionné dans un commentaire, [sournois] les questions sont maintenant hors sujet sur PPCG , mais étaient autrefois un type de défi où ce genre de choses montrerait up). J'ai utilisé ce site Web pour trouver les homoglyphes utilisés pour cette réponse.

127
Draco18s

Alternativement, vous pouvez utiliser une classe pour cela et une instance pour le contrôle.

function A() {
    var value = 0;
    this.valueOf = function () { return ++value; };
}

var a = new A;

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

MODIFIER

En utilisant les classes ES6, cela ressemblerait à ceci

class A {
  constructor() {
    this.value = 0;
    this.valueOf();
  }
  valueOf() {
    return this.value++;
  };
}

let a = new A;

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

123
Nina Scholz

JavaScript

a == a +1

En JavaScript, il n'y a pas entiers mais seulement Numbers, qui sont implémentés sous forme de nombres à virgule flottante en double précision.

Cela signifie que si Number a est assez grand, il peut être considéré comme égal à trois entiers consécutifs:

a = 100000000000000000
if (a == a+1 && a == a+2 && a == a+3){
  console.log("Precision loss!");
}

Certes, ce n'est pas exactement ce que l'intervieweur a demandé (cela ne fonctionne pas avec a=0), mais cela n'implique aucune astuce avec des fonctions cachées ou une surcharge d'opérateur.

Autres langues

Pour référence, il existe des solutions a==1 && a==2 && a==3 en Ruby et Python. Avec une légère modification, c'est également possible en Java.

Rubis

Avec un == personnalisé:

class A
  def ==(o)
    true
  end
end

a = A.new

if a == 1 && a == 2 && a == 3
  puts "Don't do this!"
end

Ou une a croissante:

def a
  @a ||= 0
  @a += 1
end

if a == 1 && a == 2 && a == 3
  puts "Don't do this!"
end

Python

class A:
    def __eq__(self, who_cares):
        return True
a = A()

if a == 1 and a == 2 and a == 3:
    print("Don't do that!")

Java

Il est possible de modifier Java Integer cache :

package stackoverflow;

import Java.lang.reflect.Field;

public class IntegerMess
{
    public static void main(String[] args) throws Exception {
        Field valueField = Integer.class.getDeclaredField("value");
        valueField.setAccessible(true);
        valueField.setInt(1, valueField.getInt(42));
        valueField.setInt(2, valueField.getInt(42));
        valueField.setInt(3, valueField.getInt(42));
        valueField.setAccessible(false);

        Integer a = 42;

        if (a.equals(1) && a.equals(2) && a.equals(3)) {
            System.out.println("Bad idea.");
        }
    }
}
93
Eric Duminil

Oui c'est possible! ????

»JavaScript

if‌=()=>!0;
var a = 9;

if‌(a==1 && a== 2 && a==3)
{
    document.write("<h1>Yes, it is possible!????</h1>")
}

Le code ci-dessus est une version courte (merci à @Forivin pour sa note dans les commentaires) et le code suivant est original:

var a = 9;

if‌(a==1 && a== 2 && a==3)
{
    //console.log("Yes, it is possible!????")
    document.write("<h1>Yes, it is possible!????</h1>")
}

//--------------------------------------------

function if‌(){return true;}

Si vous voyez juste le dessus de mon code et que vous l'exécutez, vous dites WOW, comment?

Donc, je pense qu'il suffit de dire Oui, c'est possible à quelqu'un qui a dit à vous: Rien n'est impossible

Astuce: j'ai utilisé un caractère caché après if pour créer une fonction dont le nom est similaire à if. En JavaScript, nous ne pouvons pas remplacer les mots-clés, donc je suis obligé d'utiliser cette méthode. C'est un faux if, mais cela fonctionne pour vous dans ce cas!


»C #

J'ai aussi écrit une version C # (avec la technique d'augmentation de la valeur de la propriété):

static int _a;
public static int a => ++_a;

public static void Main()
{
    if(a==1 && a==2 && a==3)
    {
        Console.WriteLine("Yes, it is possible!????");
    }
}

Démo en direct

90
RAM

Ceci est une version inversée de @ Réponse de Jeff * où un caractère caché (U + 115F, U + 1160 ou U + 3164) est utilisé pour créer des variables ressemblant à 1, 2 et 3.

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

* Cette réponse peut être simplifiée en utilisant une largeur nulle (non-jointeuse) (U + 200C) et une largeur nulle (U + 200D). Ces deux caractères sont autorisés dans les identificateurs, mais pas au début:

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

/****
var a = 1;
var a\u200c = 2;
var a\u200d = 3;
console.log(a == 1 && a\u200c == 2 && a\u200d == 3);
****/

D'autres astuces sont possibles en utilisant la même idée, par exemple. en utilisant des sélecteurs de variation Unicode pour créer des variables qui se ressemblent parfaitement (a︀ = 1; a︁ = 2; a︀ == 1 && a︁ == 2; // true).

78
Salman A

Règle numéro un des entretiens; ne dites jamais impossible.

Pas besoin de ruse de personnage caché.

window.__defineGetter__( 'a', function(){
    if( typeof i !== 'number' ){
        // define i in the global namespace so that it's not lost after this function runs
        i = 0;
    }
    return ++i;
});

if( a == 1 && a == 2 && a == 3 ){
    alert( 'Oh dear, what have we done?' );
}

72
MonkeyZeus

Honnêtement, cependant, s’il existe un moyen de s’évaluer ou de ne pas être vrai (et comme d’autres l'ont montré, il existe de nombreuses façons), la réponse que je voudrais rechercher, en tant que personne ayant conduit des centaines d'interviews, serait: quelque chose comme:

"Eh bien, peut-être que oui dans certaines circonstances étranges qui ne me sont pas immédiatement évidentes ... mais si je rencontrais cela dans du code réel, j'utiliserais des techniques de débogage courantes pour comprendre comment et pourquoi il faisait ce qu'il faisait et alors immédiatement refactoriser le code pour éviter cette situation ... mais plus important encore: je n’écrirais JAMAIS ce code, car c’est la définition même du code compliqué, et je m'efforce de ne jamais écrire du code compliqué ".

J'imagine que certains intervieweurs s'offusqueraient de poser une question très délicate, mais les développeurs qui ont une opinion, cela ne me dérange pas, surtout lorsqu'ils peuvent le soutenir avec une pensée raisonnée et qu'ils peuvent intégrer ma question à la question. une déclaration significative sur eux-mêmes.

66
Frank W. Zammetti

Voici une autre variante, utilisant un tableau pour extraire les valeurs souhaitées.

const a = {
  n: [3,2,1],
  toString: function () {
    return a.n.pop();
  }
}

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

41
Théophile

Ok, un autre hack avec des générateurs:

const value = function* () {
  let i = 0;
  while(true) yield ++i;
}();

Object.defineProperty(this, 'a', {
  get() {
    return value.next().value;
  }
});

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

31
BaggersIO

En fait, la réponse à la première partie de la question est "Oui" dans tous les langages de programmation. Par exemple, il s’agit de C/C++:

#define a   (b++)
int b = 1;
if (a ==1 && a== 2 && a==3) {
    std::cout << "Yes, it's possible!" << std::endl;
} else {
    std::cout << "it's impossible!" << std::endl;
}
27
Gustavo Rodríguez

Utiliser Proxies :

var a = new Proxy({ i: 0 }, {
    get: (target, name) => name === Symbol.toPrimitive ? () => ++target.i : target[name],
});
console.log(a == 1 && a == 2 && a == 3);

Les mandataires prétendent fondamentalement être un objet cible (le premier paramètre), mais interceptent les opérations sur l'objet cible (dans ce cas, l'opération "get property"), de sorte qu'il est possible d'effectuer autre chose que le comportement par défaut de l'objet. Dans ce cas, l'action "get property" est appelée sur a lorsque == coercce son type afin de le comparer à chaque nombre. Ça arrive:

  1. Nous créons un objet cible, { i: 0 }, où la propriété i est notre compteur
  2. Nous créons un proxy pour l'objet cible et l'attribuons à a
  3. Pour chaque comparaison a ==, le type de a est forcé à une valeur primitive
  4. Cette contrainte de type entraîne l'appel de a[Symbol.toPrimitive]() en interne
  5. Le proxy intercepte l’obtention de la fonction a[Symbol.toPrimitive] à l’aide du "get handler"
  6. Le "get handler" du proxy vérifie que la propriété obtenue est Symbol.toPrimitive, auquel cas il incrémente puis renvoie le compteur à partir de l'objet cible: ++target.i. Si une propriété différente est en cours de récupération, nous retournons simplement à la valeur par défaut de la propriété, target[name]

Alors:

var a = ...; // a.valueOf == target.i == 0
a == 1 && // a == ++target.i == 1
a == 2 && // a == ++target.i == 2
a == 3    // a == ++target.i == 3

Comme avec la plupart des autres réponses, cela ne fonctionne qu'avec un contrôle d'égalité non structuré (==), car les contrôles d'égalité stricts (===) ne permettent pas la coercition de type que le proxy puisse intercepter.

27
IceCreamYou

Identique, mais différent, mais toujours identique (peut être "testé" plusieurs fois):

const a = { valueOf: () => this.n = (this.n || 0) % 3 + 1}
    
if(a == 1 && a == 2 && a == 3) {
  console.log('Hello World!');
}

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

Mon idée a commencé à partir de la façon dont l'équation de type d'objet Number fonctionne.

26
Preda7or

Une réponse ECMAScript 6 utilisant des symboles:

const a = {value: 1};
a[Symbol.toPrimitive] = function() { return this.value++ };
console.log((a == 1 && a == 2 && a == 3));

En raison de l'utilisation de ==, JavaScript est censé contraindre a dans un élément proche du deuxième opérande (1, 2, 3 dans ce cas). Mais avant que JavaScript n'essaie d'imaginer la contrainte, il essaie d'appeler Symbol.toPrimitive . Si vous fournissez Symbol.toPrimitive JavaScript utiliserait la valeur renvoyée par votre fonction. Si ce n’est pas le cas, JavaScript s’appelle valueOf .

23
Omar Alshaker

Je pense que c'est le code minimal pour l'implémenter:

i=0,a={valueOf:()=>++i}

if (a == 1 && a == 2 && a == 3) {
  console.log('Mind === Blown');
}

Création d'un objet factice avec une variable valueOf personnalisée qui incrémente une variable globale i à chaque appel. 23 personnages!

23
Gaafar

Celui-ci utilise le defineProperty avec une variable globale causant un effet secondaire de Nice!

var _a = 1

Object.defineProperty(this, "a", {
  "get": () => {
    return _a++;
  },
  configurable: true
});

console.log(a)
console.log(a)
console.log(a)

11
Ben Aubin

En remplaçant valueOf dans une déclaration de classe, cela peut être fait:

class Thing {
    constructor() {
        this.value = 1;
    }

    valueOf() {
        return this.value++;
    }
}

const a = new Thing();

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

Ce qui se passe, c'est que valueOf est appelé dans chaque opérateur de comparaison. Sur le premier, a sera égal à 1, sur le second, a sera égal à 2, et ainsi de suite, car chaque fois que valueOf est appelé, la valeur de a est incrémentée.

Par conséquent, le fichier console.log se déclenchera et générera (dans mon terminal de toute façon) Thing: { value: 4}, indiquant que la condition était vraie.

0
Jonathan Kuhl