it-swarm-eu.dev

Může (a == 1 && a == 2 && a == 3) někdy vyhodnotit, že je pravda?

Poznámka moderátora: Odporujte nutkání upravit kód nebo odstranit toto oznámení. Vzorec mezer může být součástí otázky, a proto by neměl být zbytečně manipulován. Pokud jste v "mezerách je zanedbatelný" tábor, měli byste být schopni přijmout kód jako je.

Je někdy možné, aby (a== 1 && a ==2 && a==3) mohlo v JavaScriptu vyhodnotit true?

Jedná se o rozhovor otázku, kterou položila hlavní technická společnost. Stalo se to dva týdny zpět, ale stále se snažím najít odpověď. Vím, že takový kód v naší každodenní práci nikdy nepíšeme, ale jsem zvědavý.

2331

Pokud využijete jak == funguje , můžete jednoduše vytvořit objekt s vlastní funkcí toString (nebo valueOf), která změní to, co vrátí při každém použití tak, aby splňovala všechny tři podmínky.

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

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


Důvodem, proč to funguje, je použití operátora volné rovnosti. Při použití volné rovnosti, pokud jeden z operandů má jiný typ než druhý, bude se motor pokoušet převést jeden na druhý. V případě objektu na levé straně a čísla vpravo se pokusí převést objekt na číslo tak, že nejprve zavolá valueOf, pokud je volaný, a v opačném případě zavolá toString. Použil jsem toString v tomto případě jednoduše proto, že to, co přišlo na mysl, valueOf by více smysl. Pokud jsem místo toho vrátil řetězec z toString, motor by se pak pokusil převést řetězec na číslo, které nám dává stejný konečný výsledek, i když s mírně delší cestou.

3185
Kevin B

Nemohl jsem odolat - další odpovědi jsou nepochybně pravdivé, ale opravdu nemůžete projít následující kód:

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

Všimněte si podivných mezer v příkazu if (který jsem zkopíroval z vaší otázky). Je to poloviční šířka Hangul (to je korejština pro ty, kteří nejsou obeznámeni), což je znak Unicode, který není interpretován skriptem ECMA jako znak mezery - to znamená, že je platným znakem pro identifikátor. Proto existují tři zcela odlišné proměnné, jedna s Hangulem za a, jedna s ním před a poslední s jen. Nahradit prostor _ pro čitelnost, stejný kód by vypadal takto:

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

Podívejte se na validaci na Mathias 'Validator . Pokud byla tato podivná mezera skutečně zahrnuta do jejich otázky, jsem si jistý, že je to náznak pro tento druh odpovědi.

Nedělej to. Vážně.

Edit: Připadalo mi, že (ačkoliv není dovoleno spouštět proměnnou) Zero-width joiner a Zero-width non-joiner znaky jsou také povoleny v názvech proměnných - viz Obfuscating JavaScript s nulovou šířkou znaků - klady a zápory? .

To by vypadalo následovně:

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

IT IS MOŽNÉ!

var i = 0;

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

To používá getter uvnitř with prohlášení nechat a vyhodnotit tři různé hodnoty.

... to ještě neznamená, že by to mělo být použito v reálném kódu ...

Ještě horší je, že tento trik bude fungovat i s použitím ===.

  var i = 0;

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

591
Jonas Wilms

Příklad bez getrů nebo valueOf:

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

To funguje, protože == vyvolá toString který volá .join pro pole Arrays.

Další řešení pomocí Symbol.toPrimitive, což je ekvivalent ES6 toString/valueOf

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

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

458
georg

Pokud je dotázán, zda je to možné (ne MUSÍ), může požádat "a", aby vrátil náhodné číslo. Bylo by pravdivé, kdyby generoval postupně 1, 2 a 3.

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

Když nemůžete dělat nic bez regulárních výrazů:

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

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

Funguje to díky metodě custom valueOf , která je volána, když je objekt porovnán s primitivním (například Number). Hlavní trik je, že a.valueOf vrací novou hodnotu pokaždé, protože volá exec na regulárním výrazu s příznakem g, což způsobuje aktualizaci lastIndex tohoto regulárního výrazu při každém nalezení shody. Takže poprvé this.r.lastIndex == 0, to odpovídá 1 a aktualizacím lastIndex: this.r.lastIndex == 1, takže příště se bude regex shodovat s 2 a tak dále.

203
Kos

Toho lze dosáhnout v následujícím globálním rozsahu. Pro nodejs použijte global místo window v kódu níže.

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

Tato odpověď zneužívá implicitní proměnné poskytované globálním rozsahem v kontextu provádění definováním getteru pro načtení proměnné.

186
jontro

To je možné v případě proměnné a, ke které mají přístup 2 weboví pracovníci prostřednictvím SharedArrayBuffer a také některý hlavní skript. Možnost je nízká, ale je možné, že když je kód zkompilován na strojový kód, weboví pracovníci aktualizují proměnnou a právě včas, takže podmínky a==1, a==2 a a==3 jsou splněny.

To může být příkladem stavu závodu ve vícevláknovém prostředí poskytovaném webovými pracovníky a SharedArrayBuffer v JavaScriptu.

Zde je základní implementace výše:

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

Na mém MacBooku Air se to děje po přibližně 10 miliard iterací při prvním pokusu:

 enter image description here

Druhý pokus:

 enter image description here

Jak jsem řekl, šance budou nízké, ale dost času, to zasáhne podmínku.

Tip: Pokud to trvá příliš dlouho na vašem systému. Zkuste pouze a == 1 && a == 2 a změňte Math.random()*3 na Math.random()*2. Přidání více a více do seznamu klesá šanci zasáhnout.

182
mehulmpt

To je také možné pomocí řady samo přepisujících getrů:

(Toto je podobné řešení jontro, ale nevyžaduje proměnnou čítače.)

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

Nevidím tuto odpověď již zaúčtovanou, takže ji do hodu hodím taky. To je podobné Jeffova odpověď s poloviční šířkou Hangulova prostoru.

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

Můžete si všimnout mírného rozporu s druhým, ale první a třetí jsou identické s pouhým okem. Všechny 3 znaky jsou odlišné:

a - Latinské malé písmeno A
- Plná šířka Latinská malá písmena A
а - malá písmena cyrilice A

Obecný termín pro toto je “homoglyfy”: různé unicode charaktery, které vypadají stejně. Typicky těžké získat tři, které jsou naprosto nerozeznatelné, ale v některých případech můžete mít štěstí. A, Α, А a Ꭺ by fungovaly lépe (latina-A, řecké alfa , cyrilice-A , a Cherokee-A respektive, bohužel řecká a Cherokee malá písmena jsou příliš odlišné od latinského a: α, , a tak nepomůže s výše uvedeným úryvkem).

Tam je celá třída Homoglyph útoků tam, nejvíce obyčejně v falešných doménových jménech (např. wikipediа.org (cyrilice) vs wikipedia.org (latina)), ale to může ukázat se v kódu také; obvykle označované jako podvědomí (jak je uvedeno v komentáři, [underhanded] otázky jsou nyní mimo téma na PPCG , ale bývají typem výzvy, kde by tyto druhy věcí ukazovaly nahoru). Použil jsem tyto webové stránky k nalezení homoglyfů použitých pro tuto odpověď.

127
Draco18s

Alternativně můžete použít třídu pro ni a instanci pro kontrolu.

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

var a = new A;

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

UPRAVIT

Použití tříd ES6 by vypadalo takto

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 == +1

V JavaScriptu nejsou žádná celá čísla ale pouze Numbers, které jsou implementovány jako dvojitá čísla s plovoucí desetinnou čárkou.

To znamená, že pokud je číslo a dostatečně velké, může být považováno za rovné třem po sobě jdoucím celkovým číslům:

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

Pravda, není to přesně to, co tazatel zeptal (nefunguje s a=0), ale nezahrnuje žádný trik se skrytými funkcemi nebo přetížení operátora.

Jiné jazyky

Pro informaci jsou zde a==1 && a==2 && a==3 řešení v Ruby a Pythonu. S mírnou úpravou je to možné i v jazyce Java.

Rubín

S vlastním ==:

class A
  def ==(o)
    true
  end
end

a = A.new

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

Nebo rostoucí a:

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

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

Krajta

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

Jáva

Je možné modifikovat mezipaměť Java Integer :

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

Ano, je to možné! ????

»JavaScript

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

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

Výše uvedený kód je krátká verze (díky @ Forivin pro poznámku v komentářích) a následující kód je originální:

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

Pokud vidíte jen horní stranu mého kódu a spustíte ho, řeknete WOW, jak?

Takže si myslím, že stačí říct Ano, je to možné někomu, kdo řekl Vám: Nic není nemožné

Trick: Použil jsem skrytý znak po if, abych vytvořil funkci, která je podobná if. V JavaScriptu nemůžeme přepsat klíčová slova, takže jsem byl nucen použít tento způsob. Je to falešný if, ale v tomto případě to funguje pro vás!


»C #

Také jsem napsal verzi C # (se zvýšením hodnoty vlastnosti technic):

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

Live Demo

90
RAM

Toto je invertovaná verze @ Jeffovy odpovědi * kde se používá skrytý znak (U + 115F, U + 1160 nebo U + 3164) k vytvoření proměnných, které vypadají jako 1, 2 a 3.

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

* Tato odpověď může být zjednodušena použitím spojky s nulovou šířkou (U + 200C) a spojky s nulovou šířkou (U + 200D). Oba tyto znaky jsou povoleny uvnitř identifikátorů, ale ne na začátku:

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

Jiné triky jsou možné s použitím stejné myšlenky, např. pomocí selektorů variant Unicode vytvořit proměnné, které vypadají přesně stejně (a︀ = 1; a︁ = 2; a︀ == 1 && a︁ == 2; // true).

78
Salman A

Pravidlo číslo jedna z rozhovorů; nikdy neříkej nemožné.

Není třeba skrýt skryté znaky.

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

Upřímně řečeno, zda existuje způsob, jak to hodnotit pravdivě nebo ne (a jak ukázali jiní, existuje několik způsobů), odpověď, kterou bych hledal, mluvila jako někdo, kdo provedl stovky rozhovorů, by byl něco ve smyslu:

"No, možná ano pod nějakým podivným souborem okolností, které mi nejsou okamžitě zřejmé ... ale když jsem se s tím setkal v reálném kódu, pak bych použil běžné ladicí techniky, abych zjistil, jak a proč to dělá, co dělá. a pak okamžitě refactor kód, aby se zabránilo této situaci ... ale co je důležitější: Já bych absolutně nikdy nikdy, že kód na prvním místě, protože to je velmi definice spletitý kód, a já se snažím nikdy psát spletitý kód ".

Myslím, že někteří tazatelé by se urazili na to, co je očividně zamýšleno jako velmi složitá otázka, která by byla vyhlášena, ale nevadí mi vývojáři, kteří mají svůj názor, zejména když ji mohou podpořit s odůvodněným myšlením a dokáží zapracovat do mé otázky. smysluplné prohlášení o sobě.

66

Zde je další variace, pomocí pole pop vypnout jakékoli hodnoty, které chcete.

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

Pokud jste někdy dostali takový dotaz (nebo si všimnete nějakého neočekávaného chování ve vašem kódu), zamyslete se nad tím, jaké věci by mohly na první pohled způsobit chování, které vypadá na první pohled nemožné:

  1. Kódování: V tomto případě proměnná, na kterou se díváte, není ta, o které si myslíte, že je. To může nastat, pokud se úmyslně nepořádáte s Unicode pomocí homoglyfů nebo mezerníkové znaky , aby název proměnné vypadal jako jiný, ale problémy s kódováním lze také zavést náhodně, např. při kopírování a vkládání kódu z webu, který obsahuje neočekávané kódové body Unicode (např. protože systém pro správu obsahu provedl nějaké „automatické formátování“, například nahrazení fl Unicode 'LATIN SMALL LIGATURE FL' (U + FB02)).

  2. Závodní podmínky: Může se vyskytnout A závod-podmínka , tj. Situace, kdy kód není prováděn v pořadí očekávaném vývojářem. Dostihové podmínky se často dějí ve vícevláknovém kódu, ale vícenásobná vlákna nejsou podmínkou pro to, aby byly možné podmínky závodu - asynchronicita je dostačující (a nenechte se zmást, async neznamená, že pod kapotou se používá více vláken ). 

    Všimněte si proto, že JavaScript také není prostý rasových podmínek jen proto, že je jednovláknový. Viz zde pro jednoduchý jednovláknový - ale asynchronní příklad. V souvislosti s jedním prohlášením by však bylo obtížné zasáhnout do JavaScriptu podmínky závodu.

    JavaScript s web pracovníky je trochu jiný, protože můžete mít více vláken. @mehulmpt nám ukázal velký důkaz o konceptu pomocí webových pracovníků .

  3. Vedlejší efekty: Vedlejší efekt operace porovnávání rovnosti (která nemusí být tak zřejmá jako v příkladech zde, často vedlejší účinky jsou velmi jemné). 

Tyto problémy se mohou objevit v mnoha programovacích jazycích, nejen v JavaScriptu, takže nevidíme jeden z klasických JavaScript WTFs zde1

Samozřejmě, že pohovor a vzorky zde vypadají velmi vynalézavě. Jsou však dobrou připomínkou, že:

  • Vedlejší účinky mohou být opravdu ošklivé a dobře navržený program by měl být bez nežádoucích vedlejších účinků.
  • Problémem může být vícevláknový a mutovatelný stav.
  • Neprovádění kódování znaků a zpracování řetězců může vést k nepříjemným chybám.

1 Například můžete najít příklad v úplně jiném programovacím jazyce (C #), který vykazuje vedlejší efekt (zjevný) zde .

37
Dirk Vollmar

Okay, další hack s generátory:

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

Odpověď na první část otázky je ve všech programovacích jazycích "Ano". Například v případě 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

Použití Proxy :

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

Proxy v podstatě předstírají, že jsou cílovým objektem (první parametr), ale zachycují operace na cílovém objektu (v tomto případě operaci "get property"), takže existuje příležitost udělat něco jiného než výchozí chování objektu. V tomto případě se akce "get property" nazývá a, když == vynucuje svůj typ, aby ji bylo možné porovnat s každým číslem. To se stává:

  1. Vytvoříme cílový objekt, { i: 0 }, kde vlastnost i je náš čítač
  2. Pro cílový objekt vytvoříme proxy a přiřadíme jej k a
  3. Pro každé porovnání a == je typ a přiveden na primitivní hodnotu
  4. Výsledkem tohoto typu donucování je interní volání a[Symbol.toPrimitive]()
  5. Proxy zachytí funkci a[Symbol.toPrimitive] pomocí "get handler"
  6. Proxy "get handler" kontroluje, zda je vlastnost, která se dostává, Symbol.toPrimitive, v tomto případě se zvyšuje a pak vrací čítač z cílového objektu: ++target.i. Pokud se načítá jiná vlastnost, vrátíme se zpět k vrácení výchozí hodnoty vlastnosti target[name]

Tak:

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

Stejně jako u většiny ostatních odpovědí to funguje pouze s volnou kontrolou rovnosti (==), protože striktní kontroly rovnosti (===) nedělají donucování, které může Proxy zachytit.

27
IceCreamYou

Stejný, ale odlišný, ale stále stejný (může být několikrát testován):

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

Můj nápad začal z toho, jak funguje numerická rovnice typu objektu.

26
Preda7or

Odpověď ECMAScript 6, která používá symboly:

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

Kvůli == použití, JavaScript má coerce a do něčeho blízko druhého operandu (1, 2, 3 v tomto případě). Než se však JavaScript pokusí postavit sám sebe, pokusí se zavolat Symbol.toPrimitive . Pokud poskytnete Symbol.toPrimitive JavaScript, použije hodnotu, kterou vaše funkce vrací. Pokud ne, JavaScript by zavolal valueOf .

23
Omar Alshaker

Myslím, že je to minimální kód pro jeho implementaci:

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

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

Vytvoření fiktivního objektu s vlastním valueOf, který zvyšuje globální proměnnou i při každém volání. 23 znaků!

23
Gaafar

Ten používá definiční vlastnost s pěkným vedlejším efektem, který způsobuje globální proměnnou!

var _a = 1

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

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

11
Ben Aubin

Přepsáním valueOf v deklaraci třídy lze provést:

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

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

const a = new Thing();

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

Co se stane, je to, že valueOf je voláno v každém operátorovi porovnání. Na první, a bude rovnat 1, na druhé, a bude rovnat 2, a tak dále a tak dále, protože pokaždé, když je valueOf zavoláno, je hodnota a zvýšena.

Console.log proto vypálí a vypne (v mém terminálu stejně) Thing: { value: 4}, což znamená, že podmínka byla pravdivá.

0
Jonathan Kuhl