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.
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.
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!")
}
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.");
}
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);
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;
}
}
}
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.
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.
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:
Deuxième essai:
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.
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.");
}
})();
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 Aa
- 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.
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!');
}
En JavaScript, il n'y a pas entiers mais seulement Number
s, 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.
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.
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
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!")
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.");
}
}
}
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 fauxif
, mais cela fonctionne pour vous dans ce cas!
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!????");
}
}
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
).
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?' );
}
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.
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');
}
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!');
}
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;
}
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:
{ i: 0 }
, où la propriété i
est notre compteura
a ==
, le type de a
est forcé à une valeur primitivea[Symbol.toPrimitive]()
en internea[Symbol.toPrimitive]
à l’aide du "get handler"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.
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.
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
.
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!
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)
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.