desarrollo-web-br-bd.com

¿Puede (a == 1 && a == 2 && a == 3) alguna vez evaluar como verdadero?

Nota del moderador: Resista el impulso de editar el código o eliminar este aviso. El patrón de espacios en blanco puede ser parte de la pregunta y, por lo tanto, no debe ser manipulado innecesariamente. Si está en el campo "el espacio en blanco es insignificante", debería poder aceptar el código tal como está.

¿Es posible que (a== 1 && a ==2 && a==3) pueda evaluar a true en JavaScript?

Esta es una pregunta de entrevista realizada por una importante empresa de tecnología. Sucedió hace dos semanas, pero todavía estoy tratando de encontrar la respuesta. Sé que nunca escribimos ese código en nuestro trabajo diario, pero tengo curiosidad.

2421

Si aprovecha cómo == funciona , simplemente podría crear un objeto con una función personalizada toString (o valueOf) que cambie lo que devuelve cada vez que se usa de tal manera que satisfaga las tres condiciones.

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

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


La razón por la que esto funciona es debido al uso del operador de igualdad suelta. Cuando se usa la igualdad suelta, si uno de los operandos es de un tipo diferente al otro, el motor intentará convertir uno a otro. En el caso de un objeto a la izquierda y un número a la derecha, intentará convertir el objeto en un número llamando primero a valueOf si es llamable, y si falla, llamará a toString. Utilicé toString en este caso simplemente porque es lo que me vino a la mente, valueOf tendría más sentido. Si, por el contrario, devolviera una cadena de toString, el motor intentaría convertir la cadena en un número que nos proporcionara el mismo resultado final, aunque con una ruta ligeramente más larga.

3185
Kevin B

No pude resistirme, las otras respuestas son indudablemente ciertas, pero realmente no puedes pasar por alto el siguiente código:

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

Tenga en cuenta el espaciado extraño en la declaración if (que copié de su pregunta). Es un Hangul de ancho medio (que es coreano para los que no están familiarizados) el cual es un carácter de espacio Unicode que no es interpretado por el script ECMA como un carácter de espacio, esto significa que es un carácter válido para un identificador. Por lo tanto, hay tres variables completamente diferentes, una con el Hangul después de la a, una con la anterior y la última con solo una. Al reemplazar el espacio con _ para facilitar la lectura, el mismo código se vería así:

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

Echa un vistazo a la validación en el validador de nombre de variable de Mathias . Si ese espacio extraño se incluyó realmente en su pregunta, estoy seguro de que es una sugerencia para este tipo de respuesta.

No hagas esto Seriamente.

Edición: Ha llegado a mi atención que (aunque no se le permite iniciar una variable) el Combinación de ancho cero y Sin combinación de ancho de cero Los caracteres también están permitidos en los nombres de variables - ver Ofuscación JavaScript con caracteres de ancho cero - pros y contras? .

Esto se vería como el siguiente:

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

ES IS ¡POSIBLE!

var i = 0;

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

Esto utiliza un getter dentro de una instrucción with para permitir que a evalúe tres valores diferentes.

... esto todavía no significa que deba usarse en código real ...

Peor aún, este truco también funcionará con el uso de ===.

  var i = 0;

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

591
Jonas Wilms

Ejemplo sin getters o valueOf:

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

Esto funciona porque == invoca toString que llama a .join para Arrays.

Otra solución, utilizando Symbol.toPrimitive que es un equivalente ES6 de toString/valueOf

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

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

458
georg

Si se le pregunta si es posible (no DEBE), puede pedirle a "a" que devuelva un número aleatorio. Sería cierto si genera 1, 2 y 3 secuencialmente.

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

Cuando no puedes hacer nada sin expresiones regulares:

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

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

Funciona debido a que custom valueOf method se llama cuando Object se compara con primitive (como Number). El truco principal es que a.valueOf devuelve un nuevo valor cada vez porque llama a exec en la expresión regular con el indicador g, lo que provoca la actualización lastIndex de esa expresión regular cada vez que se encuentra una coincidencia. Así que la primera vez que this.r.lastIndex == 0 coincide con 1 y actualiza lastIndex: this.r.lastIndex == 1, así que la próxima vez que regex coincidirá con 2 y así sucesivamente.

203
Kos

Se puede lograr utilizando lo siguiente en el ámbito global. Para nodejs use global en lugar de window en el siguiente código.

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

Esta respuesta abusa de las variables implícitas proporcionadas por el alcance global en el contexto de ejecución al definir un captador para recuperar la variable.

186
jontro

Esto es posible en el caso de que se acceda a la variable a, por ejemplo, 2 trabajadores web a través de un SharedArrayBuffer, así como algunos scripts principales. La posibilidad es baja, pero es posible que cuando el código se compila a código de máquina, los trabajadores web actualicen la variable a justo a tiempo para que se cumplan las condiciones a==1, a==2 y a==3.

Este puede ser un ejemplo de condición de carrera en un entorno multihilo proporcionado por los trabajadores web y SharedArrayBuffer en JavaScript.

Aquí está la implementación básica de lo anterior:

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')
})

modifier.js

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

En mi MacBook Air, ocurre después de alrededor de 10 mil millones de iteraciones en el primer intento:

 enter image description here

Segundo intento:

 enter image description here

Como dije, las posibilidades serán bajas, pero si se da el tiempo suficiente, llegará a la condición.

Consejo: si toma demasiado tiempo en su sistema. Intente sólo a == 1 && a == 2 y cambie Math.random()*3 a Math.random()*2. Agregar más y más a la lista deja caer la posibilidad de golpear.

182
mehulmpt

Esto también es posible usando una serie de captadores de auto-sobrescritura:

(Esto es similar a la solución de jontro, pero no requiere una variable de contador).

(() => {
    "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

No veo esta respuesta ya publicada, así que la incluiré también en la mezcla. Esto es similar a la respuesta de Jeff con el espacio de Hangul de medio ancho.

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

Es posible que note una ligera discrepancia con la segunda, pero la primera y la tercera son idénticas a simple vista. Los 3 son caracteres distintos:

a - minúscula latina A
- Ancho completo en latín, minúscula A
а - minúscula cirílica A

El término genérico para esto es "homoglyphs": diferentes caracteres Unicode que se ven iguales. Por lo general, es difícil obtener tres que son absolutamente indistinguibles, pero en algunos casos puedes tener suerte. A, Α, А, y Ꭺ funcionarían mejor (Latin-A, Greek Alpha , Cyrillic-A , y Cherokee-A respectivamente; desafortunadamente las letras minúsculas griegas y cherokee son demasiado diferentes del latín a: α, , y por lo tanto no ayuda con el fragmento de código anterior).

Hay toda una clase de Ataques de Homoglyph por ahí, más comúnmente en nombres de dominio falsos (por ejemplo, wikipediа.org (cirílico) vs wikipedia.org (latino)), pero también puede aparecer en el código; por lo general, se lo denomina ser poco inteligente (como se menciona en un comentario, [malicioso] las preguntas están ahora fuera de tema en PPCG , pero solía ser un tipo de desafío donde este tipo de cosas se mostrarían arriba). Utilicé este sitio web para encontrar los homoglifos utilizados para esta respuesta.

127
Draco18s

Alternativamente, podría usar una clase para él y una instancia para la verificación.

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

var a = new A;

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

EDITAR

Usando clases de ES6 se vería así

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, no hay enteros pero solo Numbers, que se implementan como números de coma flotante de doble precisión.

Significa que si un Número a es lo suficientemente grande, puede considerarse igual a tres enteros consecutivos:

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

Es cierto que no es exactamente lo que preguntó el entrevistador (no funciona con a=0), pero no implica ningún truco con funciones ocultas o sobrecarga de operadores.

Otros idiomas

Para referencia, hay soluciones a==1 && a==2 && a==3 en Ruby y Python. Con una ligera modificación, también es posible en Java.

Rubí

Con un == personalizado:

class A
  def ==(o)
    true
  end
end

a = A.new

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

O una creciente a:

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

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

Pitón

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

Es posible modificar 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

¡Sí, es posible! ????

»JavaScript

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

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

El código anterior es una versión corta (gracias a @Forivin por su nota en los comentarios) y el siguiente código es 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 solo ves la parte superior de mi código y lo ejecutas, dices WOW, ¿cómo?

Entonces, creo que es suficiente decir Sí, es posible a alguien que le dijo a Usted: (Nada es imposible

Truco: usé un carácter oculto después de if para hacer una función cuyo nombre sea similar a if. En JavaScript no podemos anular las palabras clave, así que me obligo a usarlas de esta manera. Es una if falsa, ¡pero te funciona en este caso!


» C #

También escribí una versión de C # (con técnica de aumento de valor de propiedad):

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!????");
    }
}

Demo en vivo

90
RAM

Esta es una versión invertida de @ la respuesta de Jeff * donde se usa un carácter oculto (U + 115F, U + 1160 o U + 3164) para crear variables que parecen 1, 2 y 3.

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

* Esa respuesta se puede simplificar utilizando una combinación de ancho cero sin juntura (U + 200C) y una combinación de ancho cero (U + 200D). Ambos de estos caracteres están permitidos dentro de los identificadores pero no al principio:

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);
****/

Otros trucos son posibles usando la misma idea, p. Ej. utilizando los selectores de variación de Unicode para crear variables que se ven exactamente iguales (a︀ = 1; a︁ = 2; a︀ == 1 && a︁ == 2; // true).

78
Salman A

Regla número uno de las entrevistas; nunca digas imposible.

No hay necesidad de trucos ocultos de personajes.

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

Sin embargo, honestamente, ya sea que haya una manera de evaluar si es verdadera o no (y, como han demostrado otros, hay varias formas), la respuesta que estaría buscando, hablando como alguien que ha realizado cientos de entrevistas, sería algo en la línea de:

"Bueno, tal vez sí bajo un extraño conjunto de circunstancias que no son inmediatamente obvias para mí ... pero si encontrase esto en un código real, usaría técnicas comunes de depuración para averiguar cómo y por qué estaba haciendo lo que estaba haciendo. y luego refactorice inmediatamente el código para evitar esa situación ... pero lo más importante es que NUNCA escribiría ese código en primer lugar porque esa es la definición misma de código enrevesado, y me esfuerzo por nunca escribir código enrevesado ".

Supongo que algunos entrevistadores se ofenderían por lo que obviamente significa una pregunta muy complicada, pero no me importa que los desarrolladores tengan una opinión, especialmente cuando pueden respaldarla con un pensamiento razonado y pueden unir mi pregunta. una declaración significativa sobre ellos mismos.

66
Frank W. Zammetti

Aquí hay otra variación, utilizando una matriz para resaltar los valores que desee.

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

Si alguna vez recibe una pregunta de esta entrevista (o nota algún comportamiento igualmente inesperado en su código), piense qué tipo de cosas podrían causar un comportamiento que parece imposible a primera vista:

  1. Codificación : En este caso, la variable que está viendo no es la que cree que es. Esto puede suceder si intencionalmente juegas con Unicode usando homoglifos o caracteres del espacio para hacer que el nombre de una variable se parezca a otra, pero los problemas de codificación también se pueden introducir accidentalmente, por ejemplo. al copiar y pegar código de la Web que contiene puntos de código Unicode inesperados (por ejemplo, porque un sistema de administración de contenido realizó algunos "autoformatos", como reemplazar fl con Unicode 'LATIN SMALL LIGATURE FL' (U + FB02)).

  2. Condiciones de carrera : Puede producirse una condición de carrera , es decir, una situación en la que el código no se está ejecutando en la secuencia esperada por el desarrollador. Las condiciones de carrera a menudo ocurren en código de subprocesos múltiples, pero los subprocesos múltiples no son un requisito para que las condiciones de carrera sean posibles; la asincronicidad es suficiente (y no se confunda, asíncrono no significa que se usen múltiples hilos debajo del capó ). 

    Tenga en cuenta que, por lo tanto, JavaScript tampoco está exento de condiciones de carrera solo porque es de un solo hilo. Consulte aquí para ver un ejemplo simple de un solo hilo, pero asíncrono. En el contexto de una sola declaración, la condición de carrera, sin embargo, sería bastante difícil de alcanzar en JavaScript.

    JavaScript con los trabajadores web es un poco diferente, ya que puedes tener múltiples hilos. @mehulmpt nos ha mostrado una gran prueba de concepto usando trabajadores web .

  3. Efectos secundarios : Un efecto secundario de la operación de comparación de igualdad (que no tiene que ser tan obvio como en los ejemplos de este documento, a menudo los efectos secundarios son muy sutiles). 

Este tipo de problemas pueden aparecer en muchos lenguajes de programación, no solo en JavaScript, por lo que no vemos uno de los clásicos JavaScript WTF aquí1

Por supuesto, la pregunta de la entrevista y las muestras aquí parecen muy elaboradas. Pero son un buen recordatorio de que:

  • Los efectos secundarios pueden ser realmente desagradables y un programa bien diseñado debería estar libre de efectos secundarios no deseados.
  • El multihilo y el estado mutable pueden ser problemáticos.
  • No hacer la codificación de caracteres y el correcto procesamiento de cadenas puede provocar errores desagradables.

1 Por ejemplo, puede encontrar un ejemplo en un lenguaje de programación (C #) totalmente diferente que muestre un efecto secundario (uno obvio) aquí .

37
Dirk Vollmar

Bueno, otro hack con generadores:

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 realidad, la respuesta a la primera parte de la pregunta es "Sí" en todos los lenguajes de programación. Por ejemplo, esto es en el caso 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

Utilizando 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);

Los proxies básicamente pretenden ser un objeto objetivo (el primer parámetro), pero interceptan operaciones en el objeto objetivo (en este caso, la operación "obtener propiedad") para que haya una oportunidad de hacer algo diferente al comportamiento predeterminado del objeto. En este caso, la acción "obtener propiedad" se llama en a cuando == obliga a su tipo para compararlo con cada número. Esto pasa:

  1. Creamos un objeto de destino, { i: 0 }, donde la propiedad i es nuestro contador
  2. Creamos un Proxy para el objeto de destino y lo asignamos a a
  3. Para cada comparación de a ==, el tipo a es obligado a un valor primitivo
  4. Este tipo de coerción resulta en llamar a[Symbol.toPrimitive]() internamente
  5. El Proxy intercepta obtener la función a[Symbol.toPrimitive] usando el "controlador de obtención"
  6. El "controlador de obtención" del Proxy comprueba que la propiedad que se obtiene es Symbol.toPrimitive, en cuyo caso se incrementa y luego devuelve el contador del objeto de destino: ++target.i. Si se recupera una propiedad diferente, simplemente volvemos a devolver el valor de propiedad predeterminado, target[name]

Asi que:

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

Como con la mayoría de las otras respuestas, esto solo funciona con una verificación de igualdad suelta (==), porque las verificaciones de igualdad estrictas (===) no hacen coerción de tipo que el Proxy puede interceptar.

27
IceCreamYou

Igual, pero diferente, pero igual (puede "probarse" varias veces):

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!');
}

Mi idea comenzó a partir de cómo funciona la ecuación de tipo de objeto Número.

26
Preda7or

Una respuesta de ECMAScript 6 que hace uso de los Símbolos:

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

Debido al uso de ==, se supone que JavaScript debe coaccionar a a en algo cercano al segundo operando (1, 2, 3 en este caso). Pero antes de que JavaScript intente calcular la coacción por sí solo, intenta llamar a Symbol.toPrimitive . Si proporciona Symbol.toPrimitive JavaScript usaría el valor que devuelve su función. Si no, JavaScript llamaría valueOf .

23
Omar Alshaker

Creo que este es el código mínimo para implementarlo:

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

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

Crear un objeto ficticio con una valueOf personalizada que incrementa una variable global i en cada llamada 23 caracteres!

23
Gaafar

¡Este utiliza la propiedad defineProperty con una variable global que causa un efecto secundario de Niza!

var _a = 1

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

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

11
Ben Aubin

Al reemplazar valueOf en una declaración de clase, se puede hacer:

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

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

const a = new Thing();

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

Lo que sucede es que valueOf se llama en cada operador de comparación. En la primera, a será igual a 1, en la segunda, a será igual a 2, y así sucesivamente, porque cada vez que se llama a valueOf, el valor de a se incrementa.

Por lo tanto, console.log se activará y generará (en mi terminal de todos modos) Thing: { value: 4}, indicando que el condicional era verdadero.

0
Jonathan Kuhl