web-dev-qa-db-ja.com

(a == 1 && a == 2 && a == 3)は真と評価できますか?

モデレータノート: /コードを編集するかこの通知を削除するという衝動に抵抗してください。空白のパターンは問題の一部である可能性があるため、不必要に改ざんされるべきではありません。あなたが「空白は重要ではない」キャンプにいるなら、あなたはそのコードをそのまま受け入れることができるはずです。

JavaScriptで(a== 1 && a ==2 && a==3)trueに評価される可能性はありますか?

これは大手ハイテク企業が尋ねたインタビューの質問です。それは2週間前に起こりました、しかし私はまだ答えを見つけようとしています。私たちが日々の仕事でそのようなコードを書くことは決してないことを私は知っていますが、私は興味があります。

2376

how ==のしくみ を利用する場合は、3つの条件をすべて満たすように使用するたびに返される値を変更するカスタムのtoString(またはvalueOf)関数を使用してオブジェクトを作成するだけです。

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

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


これが機能するのは、ルーズ等価演算子を使用しているためです。ゆるい等式を使用するとき、オペランドの1つが他のものと異なるタイプであるなら、エンジンは1つを他へ変換しようとします。左側のオブジェクトと右側の数字の場合、呼び出し可能であれば最初にvalueOfを呼び出すことでオブジェクトを数字に変換しようとします。失敗した場合はtoStringを呼び出します。この場合toStringを使用したのは、それが頭に浮かんだからです。valueOfのほうが理にかなっています。代わりにtoStringから文字列を返した場合、エンジンはその文字列を数値に変換しようとしましたが、パスは多少長くなりますが同じ結果になります。

3185
Kevin B

私は抵抗することができませんでした - 他の答えは間違いなく本当です、しかし、あなたは本当に以下のコードを通り過ぎることができません:

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

ifステートメントの奇妙な間隔に注意してください(私があなたの質問からコピーしたものです)。 ECMAスクリプトによってスペース文字として解釈されないUnicodeスペース文字は、半角ハングル(慣れない人には韓国語です)です。つまり、これは識別子として有効な文字です。そのため、完全に異なる変数が3つあります。1つはaの後にハングル、1つは前に、最後の1つはaだけです。読みやすくするためにスペースを_に置き換えると、同じコードは次のようになります。

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

Mathiasの変数名バリデーターの検証 をチェックしてください。その奇妙な間隔が実際に彼らの質問に含まれていたら、それはこの種の答えのためのヒントであると確信しています。

しないでください。真剣に。

編集:それは(変数を開始することはできませんが) ゼロ幅ジョイナ および ゼロ幅非ジョイナ 文字も変数名に使用できることに気付きました 幅がゼロの文字を含むJavaScriptの難読化 - 長所と短所

これは次のようになります。

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可能です!

var i = 0;

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

これは、withを3つの異なる値に評価させるために、aステートメント内でゲッターを使用します。

...これはまだこれが実際のコードで使用されるべきであるという意味ではありません...

さらに悪いことに、このトリックは===を使ってもうまくいくでしょう。

  var i = 0;

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

591
Jonas Wilms

ゲッターまたはvalueOfなしの例:

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

これは==が配列のために.joinを呼び出すtoStringを呼び出すのでうまくいきます。

Symbol.toPrimitiveと同等のES6であるtoString/valueOfを使用した別の解決策: 

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

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

458
georg

可能かどうかを尋ねられた場合(MUSTではありません)、乱数を返すように "a"に依頼できます。それが1、2、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

正規表現がないと何もできない場合

__コード__

Objectがプリミティブ(Numberなど)と比較されるときに呼び出されるカスタムの valueOf メソッドのために機能します。主なトリックは、var a = { r: /\d/g, valueOf: function(){ return this.r.exec(123)[0] } } if (a == 1 && a == 2 && a == 3) { console.log("!") }execフラグを使用して正規表現に対してgを呼び出すため、a.valueOfが毎回新しい値を返すことです。これにより、一致が見つかるたびにその正規表現の lastIndex が更新されます。そのため、初回はthis.r.lastIndex == 0、それは1と一致し、lastIndexthis.r.lastIndex == 1を更新します。そのため、次回のregexは2などと一致します。

203
Kos

これは、グローバルスコープで以下を使用して達成できます。以下のコードでは、nodejsglobalの代わりにwindowを使用します。

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

この答えは、変数を取得するためのゲッターを定義することによって、実行コンテキスト内のグローバルスコープによって提供される暗黙の変数を悪用します。

186
jontro

これは、2人のWebワーカーがSharedArrayBufferといくつかのメインスクリプトを介してアクセスしている変数aの場合に可能です。可能性は低いですが、コードがマシンコードにコンパイルされるときに、Webワーカーが変数aを丁度間に合うように更新するので、条件a==1a==2およびa==3は満たされます。

これは、Webワーカーが提供するマルチスレッド環境での競合状態とJavaScriptのSharedArrayBufferの例です。

これが上記の基本的な実装です。

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

私のMacBook Airでは、最初の試みで約100億回繰り返した後に発生します。

enter image description here

2回目の試行:

enter image description here

私が言ったように、可能性は低いでしょう、しかし十分な時間を与えられて、それは状態を打つでしょう。

ヒント:システムに時間がかかりすぎる場合。 a == 1 && a == 2だけを試して、Math.random()*3Math.random()*2に変更してください。リストにもっと追加すると、ヒットする可能性が低くなります。

182
mehulmpt

これは、一連の自己上書き型ゲッターを使用しても可能です。

(これはjontroの解決策に似ていますが、カウンター変数を必要としません。)

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

私はこの答えがすでに投稿されているのを見ないので、私もこの答えをミックスに入れます。これは、 Jeffの答え の半角ハングルスペースの場合と似ています。

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

あなたは2番目のものとわずかな食い違いに気付くかもしれませんが、1番目と3番目は肉眼で同じです。 3つはすべて異なる文字です。

a - ラテン小文字A
- 全角ラテン小文字A
а - キリル文字の小文字A

これを表す一般的な用語は「ホモグリフ」です。同じように見える異なるUnicode文字です。一般的に入手困難な three は全く区別がつかないが、場合によっては運が悪くなることがある。 A、A、А、およびᎪはそれぞれうまく機能します(Latin-A、 ギリシャ語AlphaCyrillic-A 、および Cherokee-A 、残念ながらギリシャ語およびチェロキー小文字ラテン語のaαとはあまりにも異なるので、上のスニペットでは役に立ちません)。

Homoglyph Attacksのクラス全体が存在します。最も一般的なのは偽のドメイン名です(例:wikipediа.org(Cyrillic)vs wikipedia.org(Latin))。一般的には理解されていないと言われています(コメントで述べられているように、 [underhanded] 質問は _ ppcg _ については現在トピック外ですが、このようなことが示されるような種類の問題ですアップ)。私は このウェブサイト を使ってこの答えに使われているホモグリフを見つけました。

127
Draco18s

代わりに、あなたはそれのためのクラスとチェックのためのインスタンスを使うことができます。

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

var a = new A;

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

_編集_

ES6クラスを使用すると、このようになります

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

JavaScriptには、 整数 はなく、Numbersだけがあり、倍精度浮動小数点数として実装されています。

つまり、Number aが十分に大きい場合、それは3つの連続する整数に等しいと見なすことができます。

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

確かに、インタビュアーが要求したこととは異なります(a=0では機能しません)が、隠し関数や演算子のオーバーロードを伴うトリックは関係ありません。

他の言語

参考までに、RubyとPythonにはa==1 && a==2 && a==3ソリューションがあります。少し修正すると、Javaでも可能です。

ルビー

カスタムの==を使うと:

class A
  def ==(o)
    true
  end
end

a = A.new

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

あるいは増加する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

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

はい、可能です! ????

»JavaScript

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

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

上記のコードは短いバージョンで(コメントに注意してくれた@Forivinに感謝します)、次のコードはオリジナルです。

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

あなたが私のコードの上面を見てそれを実行するなら、あなたはWOWと言います、どうですか?

だからあなたにと言った人にはい、可能ですと言うだけで十分だと思います:不可能なことは何もありません

トリック:ifの後に隠し文字を使って、その名前がifに似た関数を作成しました。 JavaScriptでは、キーワードを上書きすることはできないため、この方法を使用することを強制しました。それは偽のifですが、この場合あなたのために働きます!


» C#

また、私はC#のバージョンを書いた(プロパティ値を増やすテクニック):

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

ライブデモ

90
RAM

これは @ Jeffの答え *の反転バージョンで、隠し文字(U + 115F、U + 1160、またはU + 3164)を使用して12および3のような変数を作成します。

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

*その答えは、ゼロ幅ノンジョイナ(U + 200C)とゼロ幅ジョイナ(U + 200D)を使用することで簡単にできます。これらの文字は両方とも識別子の内側で使用できますが、先頭には使用できません。

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

他のトリックは同じ考えを使用して可能である。 Unicodeのバリエーションセレクタを使用して、まったく同じように見える変数を作成します(a︀ = 1; a︁ = 2; a︀ == 1 && a︁ == 2; // true)。

78
Salman A

インタビューのルール番号1。不可能とは言わないで。

隠されたキャラクターのトリックの必要はありません。

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

正直に言って、それが本当かどうかを評価する方法があるかどうか(そして他の人が示しているように、複数の方法がある)、私が探している答えは何百ものインタビューを行った誰かとして次の行に沿って何か

「そうですね、私にはすぐにはわからないような奇妙な状況下では多分そうでしょう…でも、実際のコードでこれに遭遇した場合は、一般的なデバッグ手法を使用してそれが何をしていたのかそして、その状況を避けるためにすぐにコードをリファクタリングしてください。しかしもっと重要なのは、私は絶対にそのコードを絶対に絶対に書かないでください、そしてそれは絶対に複雑なコードを書かないことです。

面接官の中には明らかに非常に難しい質問であることが明らかにされているものを持つことに腹を立てる人もいると思いますが、意見を持っている開発者にとっては特に問題ありません。自分自身についての意味のある声明。

66

そのようなインタビューの質問を受け取った場合(またはコード内で同様に予期しない振る舞いに気付いた場合)、どのようなものが一見不可能に見える振る舞いを引き起こす可能性があるかを考えてください。

  1. Encoding:この場合、あなたが見ている変数はあなたが思っているものではありません。これは、変数の名前を別の変数のように見せるために homoglyphs または スペース文字 を使用して意図的にUnicodeを台無しにした場合に発生する可能性がありますが、エンコードの問題が誤って導入されることもあります。例えば予期しないUnicodeコードポイントを含むコードをWebからコピーして貼り付ける場合(たとえば、コンテンツ管理システムがflをUnicode 'LATIN SMALL LIGATURE FL'(U + FB02)に置き換えるなどの「自動フォーマット」を行ったため) 。

  2. レース条件レース条件 が発生する可能性があります。つまり、開発者が期待する順序でコードが実行されない状況。多くの場合、競合状態はマルチスレッドコードで発生しますが、競合状態を可能にするために複数のスレッドは必須ではありません。非同期性で十分です(混乱しないでください 非同期は複数のスレッドが内部で使用されることを意味しません )。

    したがって、JavaScriptはシングルスレッドであるという理由だけで競合状態から解放されないことに注意してください。単純なシングルスレッド(ただし非同期)の例については、 here を参照してください。ただし、単一のステートメントのコンテキストでは、JavaScriptで競合状態をヒットするのはかなり困難です。

    Web Workerを使用するJavaScriptは、複数のスレッドを持つことができるため、少し異なります。 @mehulmptは、素晴らしい Webワーカーを使用した概念実証 を示しています。

  3. 副作用:等値比較操作の副作用(この例のように明白である必要はなく、多くの場合、効果は非常に微妙です)。

この種の問題は、JavaScriptだけでなく多くのプログラミング言語で発生する可能性があるため、従来の JavaScript WTF のいずれも表示されません1

もちろん、インタビューの質問とここのサンプルはすべて非常に不自然に見えます。しかし、彼らはそれをよく思い出させます:

  • 副作用は本当に厄介なものになる可能性があり、適切に設計されたプログラムには望ましくない副作用がないはずです。
  • マルチスレッドと可変状態には問題があります。
  • 文字エンコードと文字列処理を正しく行わないと、厄介なバグが発生する可能性があります。

1 たとえば、副作用(明らかなもの) here を示すまったく異なるプログラミング言語(C#)の例を見つけることができます。

42
Dirk Vollmar

これは別のバリエーションです。配列を使用して、必要な値をポップします。

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

さて、ジェネレータを使ったもう一つのハック:

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

実際、質問の最初の部分に対する答えは、すべてのプログラミング言語で「はい」です。例えば、これは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

プロキシ を使用する

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

プロキシは基本的にターゲットオブジェクト(最初のパラメータ)のふりをしますが、ターゲットオブジェクトに対する操作(この場合は "get property"操作)を傍受するので、デフォルトのオブジェクトの動作以外のことをする機会があります。この場合、==が各数値と比較するためにその型を強制するときに、「get property」アクションがaに対して呼び出されます。これは起こります:

  1. ターゲットオブジェクト{ i: 0 }を作成します。ここで、iプロパティはそのカウンターです。
  2. ターゲットオブジェクト用のProxyを作成し、それをaに割り当てます。
  3. a ==比較ごとに、aの型はプリミティブ値に強制変換されます
  4. この型強制は内部的にa[Symbol.toPrimitive]()を呼び出すことになります
  5. プロキシは、 "get handler"を使用してa[Symbol.toPrimitive]関数を取得することを傍受します
  6. Proxyの「get handler」は、取得されているプロパティがSymbol.toPrimitiveであることを確認します。この場合、増加してからターゲットオブジェクトからカウンタを返します:++target.i。別のプロパティが取得されている場合は、デフォルトのプロパティ値target[name]を返すだけにします。

そう:

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

他のほとんどの回答と同様に、厳密な等価検査(==)はプロキシが代行できる型強制を行わないため、これは緩い等価検査(===)でのみ機能します。

27
IceCreamYou

同じだが違う、しかしまだ同じ(何度も「テスト」することができる):

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

私のアイデアは、Numberオブジェクト型の式がどのように機能するかから始まりました。

26
Preda7or

シンボルを利用したECMAScript 6の回答

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

==の使用法により、JavaScriptはaを2番目のオペランド(この場合は123)に近いものに強制変換することになっています。しかしJavaScriptが自分自身で強制的に数値化しようとする前に、 Symbol.toPrimitive を呼び出そうとします。 Symbol.toPrimitiveを指定した場合、JavaScriptは関数が返す値を使用します。そうでなければ、JavaScriptは valueOf を呼び出します。

23
Omar Alshaker

これはそれを実装するための最小限のコードだと思います。

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

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

呼び出しごとにグローバル変数valueOfを増分するカスタムiを使用してダミーオブジェクトを作成します。 23キャラクター!

23
Gaafar

これはdefinePropertyを使用して、グローバル変数を発生させるNice副作用があります。

var _a = 1

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

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

11
Ben Aubin

クラス宣言でvalueOfname__をオーバーライドすることで、それを行うことができます。

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

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

const a = new Thing();

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

何が起こるかはvalueOfname__が各比較演算子で呼び出されることです。 aname__が呼び出されるたびにaname__の値が増分されるため、最初のvalueOfname__は1に等しく、2番目のaname__は2に等しくなります。

そのため、console.logは(とにかく私の端末では)Thing: { value: 4}を起動して出力し、条件が正しいことを示します。

0
Jonathan Kuhl