tutorial support español ecmascript javascript ecmascript-6

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



ecmascript 7 (26)

Nota para el moderador: resista la necesidad 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 como 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.


JavaScript

a == a +1

En JavaScript, no hay integers sino solo Number s, 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 del operador.

Otros idiomas

Como 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 una costumbre == :

class A def ==(o) true end end a = A.new if a == 1 && a == 2 && a == 3 puts "Don''t do this!" end

O un 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 el caché de Integer Java:

package ; 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."); } } }


¡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 para alguien que te dijo: Nada es imposible

Truco: Utilicé 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 vi obligado a usarlo de esta manera. Es falso if , ¡pero te funciona en este caso!

» C #

También escribí una versión de C # ( con una técnica de aumento del valor de la 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


¡Éste usa defineProperty con un agradable efecto secundario que causa una variable global!

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


Al anular valueOf 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 el primero, a será igual 1 , en el segundo, a será igual 2 , y así sucesivamente, porque cada vez que valueOf se llama, el valor de a se incrementa.

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


Aquí hay otra variación, utilizando una matriz para extraer 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''); }


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 costumbre valueOf que incremente una variable global i en cada llamada. 23 personajes!


De acuerdo, otro truco 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!''); }


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


Honestamente, sin embargo, si hay una forma de evaluarlo como verdadero o no (y como otros han demostrado, 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í, en algunas circunstancias extrañas que no son obvias para mí de inmediato ... pero si me encontrara con esto en código real, usaría técnicas de depuración comunes para descubrir cómo y por qué estaba haciendo lo que estaba haciendo. e inmediatamente refactorizar el código para evitar esa situación ... pero lo más importante: NUNCA escribiría ese código en primer lugar porque esa es la definición misma de código contorneado, y me esfuerzo por nunca escribir código convolucionado ".

Supongo que algunos entrevistadores se ofenden por tener lo que obviamente debe ser 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 encajar mi pregunta en Una declaración significativa sobre ellos mismos.


Igual, pero diferente, pero igual (puede ser "probado" 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ó de cómo funciona la ecuación de tipo de objeto Número.


Si alguna vez recibe una pregunta de entrevista (o nota un comportamiento igualmente inesperado en su código) piense en 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ás viendo no es la que crees que es. Esto puede suceder si intencionalmente juega con Unicode usando homoglyphs o caracteres de 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 código Unicode inesperado puntos (p. ej., porque un sistema de gestión de contenido realizó un "formateo automático", como reemplazarlo fl con ''LATIN SMALL LIGATURE FL'' (U + FB02) de Unicode).

  2. Condiciones de carrera : puede ocurrir una race-condition , es decir, una situación en la que el código no se ejecuta en la secuencia esperada por el desarrollador. Las condiciones de carrera a menudo suceden en código de subprocesos múltiples, pero varios subprocesos no son un requisito para que las condiciones de carrera sean posibles: la asincronía es suficiente (y no se confunda, async no significa que se usen múltiples subprocesos debajo del capó ).

    Tenga en cuenta que, por lo tanto, JavaScript tampoco está libre de condiciones de carrera solo porque es de un solo subproceso. Vea here un ejemplo simple de un solo subproceso, pero asíncrono. Sin embargo, en el contexto de una sola declaración, la condición de carrera sería bastante difícil de alcanzar en JavaScript.

    JavaScript con los trabajadores web es un poco diferente, ya que puede tener múltiples hilos. @mehulmpt nos ha mostrado una gran race-condition .

  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 aquí, a menudo los efectos secundarios son muy sutiles).

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

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

  • Los efectos secundarios pueden volverse realmente desagradables y un programa bien diseñado debe estar libre de efectos secundarios no deseados.
  • El estado de subprocesamiento múltiple y mutable puede ser problemático.
  • No realizar correctamente la codificación de caracteres y el procesamiento de cadenas puede provocar errores desagradables.

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


Una respuesta ECMAScript 6 que hace uso de símbolos:

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

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


Usando 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 de destino (el primer parámetro), pero interceptan operaciones en el objeto de destino (en este caso, la operación "obtener propiedad") para que haya una oportunidad de hacer algo que no sea el comportamiento predeterminado del objeto. En este caso, se activa la acción "obtener propiedad" a cuando == coacciona su tipo para compararlo con cada número. Esto pasa:

  1. Creamos un objeto objetivo { i: 0 } , donde la i propiedad es nuestro contador
  2. Creamos un Proxy para el objeto de destino y lo asignamos a a
  3. Para cada a == comparación, a el tipo se coacciona a un valor primitivo
  4. Este tipo de coerción resulta en llamadas a[Symbol.toPrimitive]() internas
  5. El Proxy intercepta obtener la a[Symbol.toPrimitive] función usando el "get handler"
  6. "Obtener handler" del proxy comprueba que la propiedad que está siendo conseguido es Symbol.toPrimitive , en cuyo caso se incrementa y luego devuelve el contador desde el 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 una coerción de tipo que el Proxy puede interceptar.


¡ES POSIBLE!

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

Esto usa un captador dentro de una instrucción with para permitir a evaluación de 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."); }


Alternativamente, puede usar una clase para ello 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

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


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 al método valueOf personalizado que se llama cuando Object se compara con primitivo (como Number). El truco principal es que a.valueOf devuelve un nuevo valor cada vez porque llama a exec en una expresión regular con el indicador g , lo que provoca que se actualice lastIndex de esa expresión regular cada vez que se encuentra una coincidencia. Entonces, la primera vez this.r.lastIndex == 0 , coincide con 1 y actualiza lastIndex : this.r.lastIndex == 1 , por lo que la próxima vez la expresión regular coincidirá con 2 y así sucesivamente.


Ejemplo sin captadores 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 las matrices.

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

let i = 0; let a = { [Symbol.toPrimitive]: () => ++i }; console.log(a == 1 && a == 2 && a == 3);


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 se parecen a 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 usando un ancho cero sin unión (U + 200C) y un ancho cero (U + 200D). Ambos 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); ****/

Son posibles otros trucos utilizando la misma idea, por ejemplo, utilizando selectores de variación Unicode para crear variables que se vean exactamente iguales ( a︀ = 1; a︁ = 2; a︀ == 1 && a︁ == 2; // true ).


Esto es posible en caso de que se acceda a a variable, digamos 2 trabajadores web a través de SharedArrayBuffer, así como algún script principal. La posibilidad es baja, pero es posible que cuando el código se compila en código máquina, los trabajadores web actualizan la variable 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 de subprocesos múltiples 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)

trabajador.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, sucede después de alrededor de 10 mil millones de iteraciones en el primer intento:

Segundo intento:

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

Sugerencia: si tarda demasiado en su sistema. Pruebe solo a == 1 && a == 2 y cambie Math.random()*3 a Math.random()*2 . Agregar más y más a la lista reduce la posibilidad de golpear.


Esto también es posible utilizando una serie de captadores de autoescritura:

(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."); } })();


No pude resistirme: las otras respuestas son indudablemente ciertas, pero realmente no puedes pasar 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 extraño espacio en la declaración if (que copié de su pregunta). Es el Hangul de ancho medio (que es coreano para aquellos que no están familiarizados) que es un carácter de espacio Unicode que el script ECMA no interpreta 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, otra con ella antes y la última con solo a. Reemplazando 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 nombres de variables de Mathias . Si ese espacio extraño realmente se incluyó en su pregunta, estoy seguro de que es una pista para este tipo de respuesta.

No hagas esto. Seriamente.

Editar: Me ha llamado la atención que (aunque no se permite iniciar una variable), los caracteres de unión de ancho cero y de no unión de ancho cero también están permitidos en los nombres de las variables; consulte Ofuscación de JavaScript con caracteres de ancho cero: ventajas y desventajas ? .

Esto se vería así:

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


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

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

Puede notar una ligera discrepancia con el segundo, pero el primero y el tercero son idénticos a simple vista. Los 3 son caracteres distintos:

a - minúscula latina A
- Ancho completo en minúscula latina A
а - Cirílica minúscula A

El término genérico para esto es "homoglifos": diferentes caracteres unicode que tienen el mismo aspecto. Por lo general, es difícil obtener tres que sean completamente indistinguibles, pero en algunos casos puedes tener suerte. A, Α, А y Ꭺ funcionarían mejor (latín-A, griego alfa , Cyrillic-A y Cherokee-A respectivamente; desafortunadamente, las letras minúsculas griegas y cherokee son demasiado diferentes del latín a : α , , y no ayuda con el fragmento anterior).

Hay toda una clase de ataques de homoglifos, más comúnmente en nombres de dominio falsos (por ejemplo, wikipediа.org (cirílico) vs wikipedia.org (latín)), pero también puede aparecer en el código; normalmente se las conoce como poco claras (como se menciona en un comentario, las preguntas [underhanded] ahora están fuera de tema en PPCG , pero solían ser un tipo de desafío donde aparecían este tipo de cosas). Usé este sitio web para encontrar los homoglifos utilizados para esta respuesta.


Regla número uno de entrevistas; Nunca digas imposible.

No hay necesidad de trucos de personajes ocultos.

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


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

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 definiendo un captador para recuperar la variable.


Si aprovecha cómo funciona == , simplemente puede crear un objeto con una función personalizada toString (o valueOf ) que cambie lo que devuelve cada vez que se usa de modo 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 se debe al uso del operador de igualdad suelta. Cuando se utiliza 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 invocable y, en su defecto, llamará a toString . Solía toString en este caso simplemente porque es lo que me vino a la mente, valueOf tendría más sentido. Si, en cambio, devolviera una cadena de toString , el motor habría intentado convertir la cadena en un número que nos diera el mismo resultado final, aunque con una ruta un poco más larga.


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