モデレータノート: /コードを編集するかこの通知を削除するという衝動に抵抗してください。空白のパターンは問題の一部である可能性があるため、不必要に改ざんされるべきではありません。あなたが「空白は重要ではない」キャンプにいるなら、あなたはそのコードをそのまま受け入れることができるはずです。
JavaScriptで(a== 1 && a ==2 && a==3)
がtrue
に評価される可能性はありますか?
これは大手ハイテク企業が尋ねたインタビューの質問です。それは2週間前に起こりました、しかし私はまだ答えを見つけようとしています。私たちが日々の仕事でそのようなコードを書くことは決してないことを私は知っていますが、私は興味があります。
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
から文字列を返した場合、エンジンはその文字列を数値に変換しようとしましたが、パスは多少長くなりますが同じ結果になります。
私は抵抗することができませんでした - 他の答えは間違いなく本当です、しかし、あなたは本当に以下のコードを通り過ぎることができません:
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!")
}
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.");
}
ゲッターまたは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);
可能かどうかを尋ねられた場合(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;
}
}
}
正規表現がないと何もできない場合
__コード__
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
と一致し、lastIndex
:this.r.lastIndex == 1
を更新します。そのため、次回のregexは2
などと一致します。
これは、グローバルスコープで以下を使用して達成できます。以下のコードでは、nodejs
にglobal
の代わりにwindow
を使用します。
var val = 0;
Object.defineProperty(window, 'a', {
get: function() {
return ++val;
}
});
if (a == 1 && a == 2 && a == 3) {
console.log('yay');
}
この答えは、変数を取得するためのゲッターを定義することによって、実行コンテキスト内のグローバルスコープによって提供される暗黙の変数を悪用します。
これは、2人のWebワーカーがSharedArrayBufferといくつかのメインスクリプトを介してアクセスしている変数a
の場合に可能です。可能性は低いですが、コードがマシンコードにコンパイルされるときに、Webワーカーが変数a
を丁度間に合うように更新するので、条件a==1
、a==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億回繰り返した後に発生します。
2回目の試行:
私が言ったように、可能性は低いでしょう、しかし十分な時間を与えられて、それは状態を打つでしょう。
ヒント:システムに時間がかかりすぎる場合。 a == 1 && a == 2
だけを試して、Math.random()*3
をMath.random()*2
に変更してください。リストにもっと追加すると、ヒットする可能性が低くなります。
これは、一連の自己上書き型ゲッターを使用しても可能です。
(これは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.");
}
})();
私はこの答えがすでに投稿されているのを見ないので、私もこの答えをミックスに入れます。これは、 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
- ラテン小文字Aa
- 全角ラテン小文字Aа
- キリル文字の小文字A
これを表す一般的な用語は「ホモグリフ」です。同じように見える異なるUnicode文字です。一般的に入手困難な three は全く区別がつかないが、場合によっては運が悪くなることがある。 A、A、А、およびᎪはそれぞれうまく機能します(Latin-A、 ギリシャ語Alpha 、 Cyrillic-A 、および Cherokee-A 、残念ながらギリシャ語およびチェロキー小文字ラテン語のa
:α
、ꭺ
とはあまりにも異なるので、上のスニペットでは役に立ちません)。
Homoglyph Attacksのクラス全体が存在します。最も一般的なのは偽のドメイン名です(例:wikipediа.org
(Cyrillic)vs wikipedia.org
(Latin))。一般的には理解されていないと言われています(コメントで述べられているように、 [underhanded] 質問は _ ppcg _ については現在トピック外ですが、このようなことが示されるような種類の問題ですアップ)。私は このウェブサイト を使ってこの答えに使われているホモグリフを見つけました。
代わりに、あなたはそれのためのクラスとチェックのためのインスタンスを使うことができます。
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!');
}
JavaScriptには、 整数 はなく、Number
sだけがあり、倍精度浮動小数点数として実装されています。
つまり、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
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 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.");
}
}
}
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#のバージョンを書いた(プロパティ値を増やすテクニック):
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!????");
}
}
これは @ Jeffの答え *の反転バージョンで、隠し文字(U + 115F、U + 1160、またはU + 3164)を使用して1
、2
および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
)。
インタビューのルール番号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?' );
}
正直に言って、それが本当かどうかを評価する方法があるかどうか(そして他の人が示しているように、複数の方法がある)、私が探している答えは何百ものインタビューを行った誰かとして次の行に沿って何か
「そうですね、私にはすぐにはわからないような奇妙な状況下では多分そうでしょう…でも、実際のコードでこれに遭遇した場合は、一般的なデバッグ手法を使用してそれが何をしていたのかそして、その状況を避けるためにすぐにコードをリファクタリングしてください。しかしもっと重要なのは、私は絶対にそのコードを絶対に絶対に書かないでください、そしてそれは絶対に複雑なコードを書かないことです。
面接官の中には明らかに非常に難しい質問であることが明らかにされているものを持つことに腹を立てる人もいると思いますが、意見を持っている開発者にとっては特に問題ありません。自分自身についての意味のある声明。
そのようなインタビューの質問を受け取った場合(またはコード内で同様に予期しない振る舞いに気付いた場合)、どのようなものが一見不可能に見える振る舞いを引き起こす可能性があるかを考えてください。
Encoding:この場合、あなたが見ている変数はあなたが思っているものではありません。これは、変数の名前を別の変数のように見せるために homoglyphs または スペース文字 を使用して意図的にUnicodeを台無しにした場合に発生する可能性がありますが、エンコードの問題が誤って導入されることもあります。例えば予期しないUnicodeコードポイントを含むコードをWebからコピーして貼り付ける場合(たとえば、コンテンツ管理システムがfl
をUnicode 'LATIN SMALL LIGATURE FL'(U + FB02)に置き換えるなどの「自動フォーマット」を行ったため) 。
レース条件: レース条件 が発生する可能性があります。つまり、開発者が期待する順序でコードが実行されない状況。多くの場合、競合状態はマルチスレッドコードで発生しますが、競合状態を可能にするために複数のスレッドは必須ではありません。非同期性で十分です(混乱しないでください 非同期は複数のスレッドが内部で使用されることを意味しません )。
したがって、JavaScriptはシングルスレッドであるという理由だけで競合状態から解放されないことに注意してください。単純なシングルスレッド(ただし非同期)の例については、 here を参照してください。ただし、単一のステートメントのコンテキストでは、JavaScriptで競合状態をヒットするのはかなり困難です。
Web Workerを使用するJavaScriptは、複数のスレッドを持つことができるため、少し異なります。 @mehulmptは、素晴らしい Webワーカーを使用した概念実証 を示しています。
副作用:等値比較操作の副作用(この例のように明白である必要はなく、多くの場合、効果は非常に微妙です)。
この種の問題は、JavaScriptだけでなく多くのプログラミング言語で発生する可能性があるため、従来の JavaScript WTF のいずれも表示されません1。
もちろん、インタビューの質問とここのサンプルはすべて非常に不自然に見えます。しかし、彼らはそれをよく思い出させます:
1 たとえば、副作用(明らかなもの) here を示すまったく異なるプログラミング言語(C#)の例を見つけることができます。
これは別のバリエーションです。配列を使用して、必要な値をポップします。
const a = {
n: [3,2,1],
toString: function () {
return a.n.pop();
}
}
if(a == 1 && a == 2 && a == 3) {
console.log('Yes');
}
さて、ジェネレータを使ったもう一つのハック:
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!');
}
実際、質問の最初の部分に対する答えは、すべてのプログラミング言語で「はい」です。例えば、これは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;
}
プロキシ を使用する
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
に対して呼び出されます。これは起こります:
{ i: 0 }
を作成します。ここで、i
プロパティはそのカウンターです。a
に割り当てます。a ==
比較ごとに、a
の型はプリミティブ値に強制変換されますa[Symbol.toPrimitive]()
を呼び出すことになりますa[Symbol.toPrimitive]
関数を取得することを傍受します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
他のほとんどの回答と同様に、厳密な等価検査(==
)はプロキシが代行できる型強制を行わないため、これは緩い等価検査(===
)でのみ機能します。
同じだが違う、しかしまだ同じ(何度も「テスト」することができる):
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オブジェクト型の式がどのように機能するかから始まりました。
シンボルを利用したECMAScript 6の回答
const a = {value: 1};
a[Symbol.toPrimitive] = function() { return this.value++ };
console.log((a == 1 && a == 2 && a == 3));
==
の使用法により、JavaScriptはa
を2番目のオペランド(この場合は1
、2
、3
)に近いものに強制変換することになっています。しかしJavaScriptが自分自身で強制的に数値化しようとする前に、 Symbol.toPrimitive
を呼び出そうとします。 Symbol.toPrimitive
を指定した場合、JavaScriptは関数が返す値を使用します。そうでなければ、JavaScriptは valueOf
を呼び出します。
これはそれを実装するための最小限のコードだと思います。
i=0,a={valueOf:()=>++i}
if (a == 1 && a == 2 && a == 3) {
console.log('Mind === Blown');
}
呼び出しごとにグローバル変数valueOf
を増分するカスタムi
を使用してダミーオブジェクトを作成します。 23キャラクター!
これはdefinePropertyを使用して、グローバル変数を発生させるNice副作用があります。
var _a = 1
Object.defineProperty(this, "a", {
"get": () => {
return _a++;
},
configurable: true
});
console.log(a)
console.log(a)
console.log(a)
クラス宣言でvalueOf
name__をオーバーライドすることで、それを行うことができます。
class Thing {
constructor() {
this.value = 1;
}
valueOf() {
return this.value++;
}
}
const a = new Thing();
if(a == 1 && a == 2 && a == 3) {
console.log(a);
}
何が起こるかはvalueOf
name__が各比較演算子で呼び出されることです。 a
name__が呼び出されるたびにa
name__の値が増分されるため、最初のvalueOf
name__は1
に等しく、2番目のa
name__は2
に等しくなります。
そのため、console.logは(とにかく私の端末では)Thing: { value: 4}
を起動して出力し、条件が正しいことを示します。