Dapat (a== 1 && a ==2 && a==3) pernah mengevaluasi dengan benar?

Moderator note: Jangan menahan keinginan untuk mengedit kode atau menghapus pemberitahuan ini. Pola spasi dapat menjadi bagian dari pertanyaan dan oleh karena itu tidak boleh dirusak dengan yang tidak perlu. Jika anda berada di "spasi adalah signifikan," kamp, anda harus dapat menerima kode tersebut.

Itu pernah mungkin bahwa (a== 1 && a ==2 && a==3) bisa mengevaluasi untuk benar di JavaScript?

Ini adalah pertanyaan wawancara yang diajukan oleh perusahaan teknologi besar. Itu terjadi dua minggu lalu, tapi saya'm masih berusaha untuk menemukan jawabannya. Aku tahu kita tidak pernah menulis kode tersebut di hari-hari kerja, tapi saya'm penasaran.

Mengomentari pertanyaan (17)
Larutan

Jika anda mengambil keuntungan dari cara == worksanda hanya bisa membuat objek dengan custom toString (atau valueOf) fungsi yang mengubah apa itu kembali setiap kali digunakan sedemikian rupa sehingga memenuhi semua tiga kondisi.

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

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

Alasan ini bekerja adalah karena penggunaan longgar kesetaraan operator. Ketika menggunakan longgar kesetaraan, jika salah satu operand adalah dari tipe yang berbeda dari yang lain, mesin akan mencoba untuk mengkonversi satu ke yang lain. Dalam kasus sebuah objek di kiri dan di kanan, itu akan mencoba untuk mengkonversi objek ke nomor dengan terlebih dahulu memanggil valueOf jika itu adalah callable, dan gagal itu, ia akan memanggil toString. Saya menggunakan toString dalam hal ini hanya karena itu's apa yang datang ke pikiran, valueOf akan membuat lebih masuk akal. Kalau aku malah kembali string dari toString, mesin akan kemudian mencoba untuk mengkonversi string ke number memberikan hasil akhir yang sama, meskipun dengan jalan yang sedikit lebih lama.

Komentar (19)

Saya tidak't resist - lain jawaban yang pasti benar, tapi anda benar-benar dapat't berjalan melewati kode berikut:

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

Catatan aneh spasi di jika pernyataan (yang saya disalin dari pertanyaan anda). Itu adalah setengah lebar Hangul (yang's korea bagi mereka yang tidak akrab) yang merupakan Unicode karakter spasi yang tidak ditafsirkan oleh ECMA script seperti karakter ruang - ini berarti bahwa ia adalah karakter yang valid untuk identifier. Oleh karena itu ada tiga benar-benar variabel yang berbeda, satu dengan Hangul setelah a, satu dengan sebelumnya dan yang terakhir dengan hanya. Mengganti ruang dengan _ untuk dibaca, kode yang sama akan terlihat seperti ini:

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

Check out validasi pada Mathias' nama variabel validator. Jika yang aneh jarak sebenarnya termasuk dalam pertanyaan mereka, saya merasa yakin bahwa itu's petunjuk untuk jenis jawaban.

Don't melakukan hal ini. Serius.

Edit: Itu telah datang ke perhatian saya yang (meskipun tidak diizinkan untuk memulai variabel) yang Nol-lebar joiner dan Zero width non-joiner karakter juga diizinkan dalam nama-nama variabel - lihat Obfuscating JavaScript dengan nol-lebar karakter - pro dan kontra?.

Ini akan terlihat seperti berikut:

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

HAL INI MUNGKIN!

var i = 0;

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

Ini menggunakan getter dalam dengan pernyataan untuk membiarkan a untuk mengevaluasi tiga nilai yang berbeda.

... ini masih tidak berarti ini harus digunakan dalam kode nyata...

Bahkan lebih buruk lagi, trik ini juga akan bekerja dengan menggunakan ===.

  var i = 0;

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

Contoh tanpa getter atau valueOf:

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

Ini bekerja karena == memanggil toString panggilan .join untuk Array.

Solusi lain, menggunakan Simbol.toPrimitive yang merupakan ES6 setara dengan toString/valueOf:

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

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

Jika ditanya apakah mungkin (tidak HARUS), dapat bertanya "a" untuk kembali nomor acak. Itu akan menjadi benar jika itu menghasilkan 1, 2, dan 3 secara berurutan.


with({
  get a() {
    return Math.floor(Math.random()*4);
  }
}){
  for(var i=0;i
Komentar (5)

Ketika anda dapat't melakukan apa-apa tanpa ekspresi reguler:

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

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

Ia bekerja karena custom valueOf metode yang dipanggil saat Objek dibandingkan dengan primitif (seperti Nomor). Trik utama adalah bahwa.valueOf mengembalikan nilai baru setiap saat karena itu's calling exec pada ekspresi reguler dengan g bendera, yang menyebabkan memperbarui lastIndex bahwa ekspresi reguler setiap kali pertandingan ditemukan. Jadi dulu waktu ini.r.lastIndex == 0, ini pertandingan 1 dan update lastIndex: ini.r.lastIndex == 1, sehingga waktu berikutnya regex akan match 2 dan seterusnya.

Komentar (4)

Ini adalah mungkin dalam kasus variabel a yang diakses oleh, katakanlah 2 pekerja web melalui SharedArrayBuffer serta beberapa naskah utama. Kemungkinan lebih rendah, tetapi adalah mungkin bahwa ketika kode ini disusun untuk kode mesin, pekerja web memperbarui variabel a tepat pada waktunya sehingga kondisi a==1, a==2 dan a==3 puas.

Ini dapat menjadi contoh dari kondisi balapan di multi-threaded lingkungan yang disediakan oleh web pekerja dan SharedArrayBuffer dalam JavaScript.

Berikut ini adalah dasar pelaksanaan atas:

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

Pada MacBook Air, hal itu terjadi setelah sekitar 10 miliar iterasi pada upaya pertama:

Upaya kedua:

Seperti yang saya katakan, kemungkinan akan rendah, tetapi mengingat waktu yang cukup, it'll memukul kondisi.

Tip: Jika waktu terlalu lama pada sistem anda. Cobalah hanya a == 1 && a == 2 dan ubah Matematika.random()*3 untuk Matematika.random()*2. Menambahkan lebih banyak dan lebih banyak untuk daftar tetes kesempatan memukul.

Komentar (17)

Hal ini dapat dicapai dengan menggunakan kode berikut dalam lingkup global. Untuk nodejs menggunakan global dan bukan jendela di dalam kode di bawah ini.

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

Jawaban ini pelanggaran implisit variabel yang disediakan oleh lingkup global dalam konteks eksekusi dengan mendefinisikan getter untuk mengambil nilai variabel.

Komentar (6)

Ini juga mungkin menggunakan serangkaian self-timpa getter:

(Hal ini mirip dengan jontro's solusi, tapi doesn't memerlukan variabel counter.)

(() => {
    "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.");
    }
})();
Komentar (5)

Sebagai alternatif, anda bisa menggunakan class dan instance untuk memeriksa.

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

var a = new A;

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

EDIT

Menggunakan ES6 kelas itu akan terlihat seperti ini

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

Saya don't melihat jawaban ini sudah diposting, jadi saya'll membuang yang satu ini ke dalam campuran juga. Hal ini mirip dengan Jeff's jawaban dengan setengah-lebar Hangul ruang.

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

Anda mungkin melihat sedikit perbedaan dengan yang kedua, tapi yang pertama dan ketiga adalah identik dengan mata telanjang. Semua 3 karakter yang berbeda:

a - Latin huruf A a - Lebar Penuh Latin huruf A а - Huruf huruf A

Istilah umum untuk ini adalah "homoglyphs": berbeda karakter unicode yang terlihat sama. Biasanya sulit untuk mendapatkan tiga bahwa benar-benar tidak bisa dibedakan, tetapi dalam beberapa kasus, anda bisa mendapatkan beruntung. A, Α, А, dan Ꭺ akan bekerja lebih baik (Latin-A, yunani Alpha, Huruf-A, dan Cherokee-A masing-masing; sayangnya yunani dan Cherokee huruf yang terlalu berbeda dari bahasa Latin a: α,, dan jadi doesn't membantu dengan potongan di atas).

Ada's seluruh kelas Homoglyph Serangan di luar sana, paling sering di palsu nama domain (misalnya. wikipedia.org (Cyrillic) vs wikipedia.org (Latin)), tetapi dapat muncul di kode juga; biasanya disebut sebagai licik (seperti yang disebutkan dalam komentar, [curang] pertanyaan sekarang off-topik di PPCG, tapi digunakan untuk menjadi jenis tantangan di mana hal-hal semacam ini akan muncul). Aku digunakan website untuk menemukan homoglyphs digunakan untuk jawaban ini.

Komentar (5)

Ya, itu mungkin! 😎

» JavaScript

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

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

Kode di atas adalah versi pendek (terima kasih untuk @Forivin untuk dicatat dalam komentar) dan berikut kode asli:

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

Jika anda hanya melihat sisi atas dari kode saya dan menjalankannya anda berkata WOW, bagaimana?

Jadi saya pikir itu sudah cukup untuk mengatakan Ya, itu mungkin untuk seseorang yang berkata kepada anda: tidak Ada yang mustahil

Trik: saya menggunakan karakter tersembunyi setelah jika untuk membuat sebuah fungsi yang namanya mirip dengan jika. Di JavaScript kita tidak bisa mengesampingkan kata kunci jadi aku terpaksa menggunakan cara ini. Itu adalah palsu jika, tapi itu bekerja untuk anda dalam hal ini!


» C#

Saya juga menulis sebuah C# version (dengan meningkatkan nilai properti 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

Komentar (14)

JavaScript

a == a +1

Dalam JavaScript, tidak ada [bulat], 1 tetapi hanya `Nomor ini, yang dilaksanakan sebagai double precision floating point nomor.

Itu berarti bahwa jika Nomor a yang cukup besar, hal ini dapat dianggap sama dengan tiga berturut-turut bilangan bulat:

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

Benar, itu's tidak persis apa yang pewawancara bertanya (itu doesn't bekerja dengan a=0), tapi itu doesn't melibatkan trik tersembunyi dengan fungsi atau operator overloading.

Bahasa lain

Untuk referensi, ada a==1 && a==2 && a==3 solusi Ruby dan Python. Dengan sedikit modifikasi, it's juga mungkin di pulau Jawa.

Ruby

Dengan custom ==:

class A
  def ==(o)
    true
  end
end

a = A.new

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

Atau peningkatan a:

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

It's mungkin untuk memodifikasi Jawa bilangan Bulat 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.");
        }
    }
}
Komentar (10)

Ini adalah terbalik versi @Jeff's jawaban* di mana karakter tersembunyi (U+115F, U+1160 atau U+3164) digunakan untuk membuat variabel-variabel yang terlihat seperti 1, 2 dan 3.

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

* Jawaban itu dapat disederhanakan dengan menggunakan zero width non-joiner (U+200C) dan lebar nol joiner (U+200D). Kedua karakter ini diperbolehkan dalam pengidentifikasi tapi tidak di awal:

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

Trik lain yang mungkin menggunakan ide yang sama misalnya dengan menggunakan Unicode variasi penyeleksi untuk membuat variabel-variabel yang terlihat persis sama (a︀ = 1; a︁ = 2; a︀ == 1 && a︁ == 2; // true).

Komentar (0)

Aturan nomor satu dari wawancara; tidak pernah mengatakan tidak mungkin.

Tidak perlu untuk karakter tersembunyi tipu daya.

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

Jujur saja, apakah ada cara untuk itu untuk mengevaluasi dengan benar atau tidak (dan lain-lain telah menunjukkan, ada beberapa cara), jawaban saya'a akan mencari, berbicara sebagai seseorang yang telah melakukan ratusan wawancara, akan menjadi sesuatu di sepanjang baris:

"Yah, mungkin ya di bawah beberapa set aneh dari keadaan yang tidak't segera jelas bagi saya... tapi jika saya mengalami ini di kode nyata maka saya akan menggunakan umum debugging teknik untuk mencari tahu bagaimana dan mengapa itu melakukan apa yang dilakukannya dan kemudian segera refactor kode untuk menghindari situasi itu... tapi yang lebih penting: saya akan benar-benar PERNAH menulis kode itu di tempat pertama karena itu adalah definisi yang sangat berbelit-belit kode, dan saya berusaha untuk tidak pernah menulis berbelit-belit kode".

Saya kira beberapa pewawancara akan tersinggung untuk memiliki apa yang jelas dimaksudkan untuk menjadi sebuah pertanyaan yang sangat rumit yang disebut keluar, tapi aku don't pikiran pengembang yang memiliki pendapat, terutama ketika mereka bisa kembali ke atas dengan beralasan berpikir dan pas saya pertanyaan menjadi pernyataan yang berarti tentang diri mereka sendiri.

Komentar (13)

Jika anda pernah mendapatkan sebuah pertanyaan wawancara (atau sama-sama melihat beberapa perilaku tak terduga dalam kode anda) berpikir tentang apa jenis hal-hal yang bisa menyebabkan perilaku yang tampak mustahil pada pandangan pertama:

  1. Encoding: Dalam hal ini variabel yang anda cari di bukan salah satu yang anda pikir itu adalah. Hal ini dapat terjadi jika anda sengaja main-main dengan Unicode menggunakan homoglyphs atau space karakter untuk membuat nama variabel yang terlihat seperti satu sama lain, tapi masalah pengkodean juga dapat diperkenalkan secara tidak sengaja, misalnya ketika menyalin & paste kode dari Web yang berisi terduga kode Unicode poin (misalnya karena sistem manajemen konten melakukan beberapa "auto-format" seperti mengganti fl dengan Unicode 'KECIL LATIN LIGATUR FL' (U+FB02)).

  2. Kondisi ras: A race-kondisi mungkin terjadi, yaitu situasi di mana kode ini tidak mengeksekusi dalam urutan yang diharapkan oleh pengembang. Perlombaan kondisi yang sering terjadi pada multi-threaded kode, tapi beberapa thread yang tidak merupakan persyaratan untuk kondisi balapan menjadi mungkin – asynchronicity cukup (dan don't bingung, async tidak berarti beberapa benang yang digunakan di bawah hood).

Perhatikan bahwa oleh karena itu JavaScript ini juga tidak lepas dari kondisi balapan hanya karena itu adalah single-threaded. Lihat di sini untuk sederhana single-threaded – tapi async – contoh. Dalam konteks pernyataan tunggal dengan kondisi balapan namun akan menjadi lebih sulit untuk memukul di JavaScript.

JavaScript dengan pekerja web ini sedikit berbeda, karena anda dapat memiliki beberapa benang. @mehulmpt telah menunjukkan kepada kita besar bukti-of-konsep menggunakan pekerja web.

  1. Efek samping: efek samping dari kesetaraan operasi perbandingan (yang doesn't harus jelas seperti dalam contoh di sini, sering efek samping yang sangat halus).

Jenis-jenis masalah yang dapat muncul di banyak bahasa pemrograman, tidak hanya JavaScript, sehingga kita tidak't melihat salah satu klasik JavaScript WTFs di sini1.

Tentu saja, pertanyaan wawancara dan sampel di sini semua terlihat sangat dibuat-buat. Tapi mereka adalah pengingat yang baik bahwa:

  • Efek samping yang bisa benar-benar jahat dan yang baik-program yang dirancang harus bebas dari efek samping yang tidak diinginkan.
  • Multi-threading dan bisa berubah negara dapat menjadi bermasalah.
  • Tidak melakukan pengkodean karakter dan string pengolahan yang tepat dapat menyebabkan bug jahat.

1 misalnya, anda dapat menemukan contoh yang benar-benar berbeda bahasa pemrograman (C#) menunjukkan efek samping (salah satu yang jelas) di sini.

Komentar (10)

Berikut ini's variasi lain, dengan menggunakan array untuk pop dari apapun nilai-nilai yang anda inginkan.

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

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

Oke, hack lain dengan generator:

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

Menggunakan 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 pada dasarnya berpura-pura menjadi target objek (parameter pertama), tapi mencegat operasi pada objek target (dalam hal ini "mendapatkan properti" operasi) sehingga ada kesempatan untuk melakukan sesuatu yang lain dari yang default objek perilaku. Dalam kasus ini, "mendapatkan properti" tindakan ini disebut a ketika == memaksa jenisnya dalam rangka untuk membandingkannya dengan masing-masing nomor. Hal ini terjadi:

  1. Kita membuat objek target, { i: 0 }, mana aku properti counter kami
  2. Kami membuat Proxy untuk target objek dan menetapkan a
  3. Untuk setiap a == perbandingan, a's jenis dipaksa untuk primitif nilai
  4. Jenis paksaan hasil yang di panggil a[Simbol.toPrimitive]() secara internal
  5. Proxy penyadapan mendapatkan a[Simbol.toPrimitive] fungsi menggunakan "mendapatkan handler"
  6. Proxy's "mendapatkan handler" cek bahwa properti yang didapat adalah Simbol.toPrimitive, dalam hal ini penambahan dan kemudian kembali counter dari objek target: ++target.aku. Jika sebuah properti yang berbeda yang diambil, kita hanya jatuh kembali untuk kembali properti nilai default, target[nama]

Jadi:

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

Seperti dengan sebagian besar jawaban yang lain, ini hanya bekerja dengan longgar cek kesetaraan (==), karena yang ketat kesetaraan cek (===) tidak melakukan jenis paksaan bahwa Proxy dapat mencegat.

Komentar (1)