ทำไม! {} [true] ถึงประเมินเป็นจริงใน JavaScript


131

{}[true]เป็น[true]และควรจะเป็น![true]false

แล้วทำไมถึง !{}[true]ประเมินเป็นtrue?


30
var o = {}; o[true] === undefined.
azz

2
คำอธิบายที่นี่น่าจะคล้ายกับความแปลกประหลาดที่กล่าวถึงในคำถามก่อนหน้านี้มาก
IMSoP

45
"เพราะ Javascript โง่" อาจไม่ใช่คำตอบที่คุณกำลังมองหา
georg

2
ตามที่กล่าวไว้หากคุณได้รับ{}[true] === [true]จากคอนโซลนั่นเป็นเพราะมันถือว่า{}เป็นบล็อกโค้ดว่างไม่ใช่วัตถุ
azz

3
หากสามารถช่วยได้ลองเปรียบเทียบ{}และ({})ในคอนโซลของคุณ (หรือ{}[true]และ({})[true]) นอกจากนี้ในขณะที่ไม่มีใครพูดถึงวัตถุ [true] จะถูกประเมินว่าเป็น object ["true"]
BiAiB

คำตอบ:


172

ผมเชื่อว่าเพราะธรรมดา{}[true]จะแยกเป็นบล็อกที่ว่างเปล่าคำสั่ง (ไม่ได้เป็นตัวอักษรวัตถุ) ตามด้วยอาร์เรย์ที่มีซึ่งเป็นtruetrue

ในทางกลับกันการใช้ตัว!ดำเนินการทำให้ parser ตีความ{}ว่าเป็น object literal ดังนั้นสิ่งต่อไปนี้{}[true]จะกลายเป็นการเข้าถึงแบบสมาชิกที่ส่งคืนundefinedและ!{}[true]เป็นจริงtrue(ตามที่!undefinedเป็นtrue)


25
ความจริงที่ว่า! undefined เป็นความจริงในทางกลับกันยังคงไม่สามารถแก้ไขได้
evilcandybag

87
@evilcandybag: มันไม่ใช่อย่างแน่นอน undefinedเป็นเท็จ (สิ่งที่เราพึ่งพาบ่อยๆ - if (obj.maybeExists) ...) ดังนั้นมันจึงทำให้เกิดความรู้สึกเชิงตรรกะที่สมบูรณ์แบบซึ่ง!undefinedเป็นความจริง
josh3736

8
@ Josh ฉันคิดว่า evilcandybag ต้องการพฤติกรรมที่คล้ายกับnullในบางภาษาโดยมี!undefinedค่าเท่ากับundefined. นั่นไม่ใช่กรณีของ Javascript
Frédéric Hamidi

6
@evilcandybag: มันสมเหตุสมผลเท่านั้นที่จะบอกว่าสิ่งที่not undefined( !undefined) ต้องถูกกำหนดไว้ trueหากสิ่งที่ถูกกำหนดไว้แล้วก็มักจะตีความว่าเป็น
OozeMeister

7
@Cruncher ถ้า a ไม่ได้กำหนดและ b ไม่ได้กำหนดเราจะรู้ได้อย่างไรว่า a! = b? โดยเฉพาะอย่างยิ่งเมื่อคุณสมบัติที่ทราบเพียงอย่างเดียวของตัวแปรทั้งสองนั้นเหมือนกันทุกประการ
LJ2

44

เนื่องจาก{}[true]ไม่ส่งคืนtrueแต่undefinedและundefinedได้รับการประเมินเป็นfalse:

http://jsfiddle.net/67GEu/

'use strict';
var b = {}[true];
alert(b); // undefined
b = !{}[true];
alert(b); // true

21
หากคุณประเมิน{}[true]ในคอนโซลคุณจะได้รับ[true]เนื่องจาก{}ถูกตีความว่าเป็นบล็อกโค้ดว่างไม่ใช่อ็อบเจ็กต์ {}มันคือทั้งหมดที่เกี่ยวกับบริบทและความคลุมเครือของ
IMSoP

1
@IMSoP แต่ทำไม{key:"value"}[1,2,3];ยังประเมินถึง[1,2,3]?
t.niese

3
@ t.niese เนื่องจากแยกวิเคราะห์เป็นบล็อกคำสั่งที่มี label ( key:) และสตริงลิเทอรัล ( "value") ตามด้วยอาร์เรย์ ตัวแยกวิเคราะห์ยังไม่เห็นตัวอักษรของวัตถุ
Frédéric Hamidi

1
@ Frédéric Hamidi อาใช่นั่นมัน ฉันอัดอั้นป้าย ^^
t.niese

1
@dooxe อ่านคำตอบอื่น ๆ ; ทุกอย่างเกี่ยวกับบริบทที่ตีความ หากคุณห่อไว้ในalert()หรือconsole.log()หรือกำหนดให้ตัวแปรคุณมีการเปลี่ยนแปลงบริบทซึ่งเป็นเหตุผลที่มันไม่ได้ทำงานในลักษณะเดียวกับที่พิมพ์บนของตัวเองในคอนโซล
IMSoP

27

เพราะ

{}[true]

ประเมินundefinedและเป็น!undefinedtrue

จาก @schlingel:

trueใช้เป็นคีย์และ{}เป็นแผนที่แฮช มีไม่ได้อยู่กับคุณสมบัติที่สำคัญเพื่อให้มันกลับtrue undefinedไม่undefinedเป็นtrueไปตามที่คาดไว้

เซสชันคอนโซล ( Node.js [0.10.17] ):

> {}[true]
undefined
> !{}[true]
true
> [true]
[ true ]
> ![true]
false
>

อย่างไรก็ตามในคอนโซลGoogle Chrome :

> !{}[true]
true

ดังนั้นไม่มีความไม่สอดคล้องกัน คุณอาจใช้ JavaScript VM เวอร์ชันเก่า สำหรับผู้ที่ต้องการหลักฐานเพิ่มเติม:

ใส่คำอธิบายภาพที่นี่

อัปเดต

ด้วยFirefoxมันยังประเมินเป็นtrue:

ใส่คำอธิบายภาพที่นี่


ไม่ใช่ถ้าคุณทำeval('{}[true]')หรือพิมพ์ลงในคอนโซล แล้วเช่น ALS {}"test"เป็นtestหรือแม้กระทั่งถูก{key:"value"}"test" test
t.niese

น่าสนใจเอ็นจิ้น js ตัวไหนที่คุณทดสอบ
t.niese

@ t.niese ฉันเพิ่งพิมพ์ลงในคอนโซลโหนดของฉันและนี่คือสิ่งที่ฉันได้รับ
Games Brainiac

เพียงเพื่อความอยากรู้ ไม่{}[true];(กับ;) ผลตอบแทน[true]สำหรับคุณเพราะที่นี่มันไม่?
t.niese

2
เหตุผลในการลดคะแนน? มีคำตอบที่เหมือนกันเกือบทั้งหมดกับ 8 คะแนนและฉันได้รับการโหวตลงคะแนน? ผมทำอะไรผิด?
Games Brainiac

23

สาเหตุของความสับสนเกิดจากความเข้าใจผิดในการยืนยันครั้งแรกของคุณ:

{}[true] คือ [true]

สิ่งที่คุณเห็นเมื่อคุณเรียกใช้มันเป็นผลมาจากความไม่ชัดเจน Javascript มีชุดกฎที่กำหนดไว้สำหรับวิธีจัดการกับความคลุมเครือเช่นนี้และในกรณีนี้จะแบ่งสิ่งที่คุณเห็นว่าเป็นคำสั่ง signle ออกเป็นสองคำสั่งแยกกัน

ดังนั้น Javascript เห็นโค้ดข้างต้นเป็นสองงบเฉพาะกิจการ: ประการแรกมีแล้วมีการแยก{} [true]คำสั่งที่สองคือสิ่งที่ให้ผลลัพธ์[true]แก่คุณ คำสั่งแรก{}ถูกละเว้นอย่างสิ้นเชิง

คุณสามารถพิสูจน์ได้โดยลองทำสิ่งต่อไปนี้:

({}[true])

คือการห่อสิ่งทั้งหมดไว้ในวงเล็บเพื่อบังคับให้ล่ามอ่านเป็นคำสั่งเดียว

undefinedตอนนี้คุณจะเห็นว่าค่าที่แท้จริงของคำสั่งของคุณ (สิ่งนี้จะช่วยให้เราเข้าใจส่วนถัดไปในภายหลัง)

ตอนนี้เรารู้แล้วว่าส่วนเริ่มต้นของคำถามของคุณคือปลาเฮอริ่งแดงดังนั้นเรามาดูส่วนสุดท้ายของคำถาม:

แล้วทำไม! {} [true] ถึงประเมินค่าเป็นจริง?

ที่นี่เรามีคำสั่งเดียวกัน แต่มีการ!ผนวกไว้ด้านหน้า

ในกรณีนี้กฎของ Javascript บอกให้ประเมินสิ่งทั้งหมดเป็นคำสั่งเดียว

อ้างถึงสิ่งที่เกิดขึ้นเมื่อเรารวมข้อความก่อนหน้านี้ไว้ในวงเล็บ undefinedเราได้ คราวนี้เรากำลังทำสิ่งเดียวกันอย่างมีประสิทธิภาพ แต่เอา!หน้ามัน ดังนั้นรหัสของคุณได้ง่ายเป็นซึ่งเป็น!undefinedtrue

หวังว่าจะอธิบายได้เล็กน้อย

มันเป็นสัตว์ร้ายที่ซับซ้อน แต่บทเรียนที่ต้องเรียนรู้ที่นี่คือการใช้วงเล็บรอบคำพูดของคุณเมื่อประเมินในคอนโซลเพื่อหลีกเลี่ยงผลลัพธ์ที่น่ากลัวเช่นนี้


2
ฉันไม่คิดว่า{}[true]เป็นที่ไม่ถูกต้องตรงเพียงคลุมเครือ สามารถตีความได้ว่าเป็น "บล็อกโค้ดว่างตามด้วยอาร์เรย์ลิเทอรัล" หรือ "อ็อบเจ็กต์ลิเทอรัลที่ไม่มีคุณสมบัติซึ่งมีการเข้าถึงคุณสมบัติ" ฉันไม่รู้ว่าในทางเทคนิคเป็นกรณีของ ASI หรือไม่ (หลายภาษาจะไม่ใส่เครื่องหมายอัฒภาค) แต่เป็นการตีความที่คำนึงถึงบริบทซึ่งเป็นหัวใจของปัญหา
IMSoP

@IMSoP - ฉันได้แก้ไขคำตอบแล้วก่อนที่คุณจะโพสต์ความคิดเห็น :)
Spudley

1
แต่ยังคงระบุว่า "{} [true] ใช้ไม่ได้จริงเลย" ที่จุดเริ่มต้นของคำตอบ
IMSoP

นอกจากนี้ OP ไม่ได้ระบุว่า " {}[true]เป็นtrue" ที่พวกเขากล่าวว่า " {}[true]เป็น[true]" ซึ่งเป็นหนึ่งในสองการตีความที่ถูกต้องของข้อความที่คลุมเครือ
IMSoP

14

{}[true]คือundefined. ในการค้นหาว่าเขียนสิ่งนี้:

a = {};
a[true] === undefined // true

หรือเพียงแค่:

({})[true] === undefined // true

เรารู้ว่าเป็น!undefinedtrue


จากคำตอบของ @Benjamin Gruenbaum :

เครื่องมือ Chrome dveloper ทำสิ่งต่อไปนี้ :

  try {
      if (injectCommandLineAPI && inspectedWindow.console) {
          inspectedWindow.console._commandLineAPI = new CommandLineAPI(this._commandLineAPIImpl, isEvalOnCallFrame ? object : null);
          expression = "with ((window && window.console && window.console._commandLineAPI) || {}) {\n" + expression + "\n}";
      }
      var result = evalFunction.call(object, expression);
      if (objectGroup === "console")
          this._lastResult = result;
      return result;
  } 
  finally {
      if (injectCommandLineAPI && inspectedWindow.console)
          delete inspectedWindow.console._commandLineAPI;
  }

โดยพื้นฐานแล้วมันจะดำเนินการcallกับวัตถุด้วยนิพจน์ การแสดงออกคือ:

with ((window && window.console && window.console._commandLineAPI) || {}) {
    {}+{};// <-- This is your code
}

อย่างที่คุณเห็นนิพจน์กำลังได้รับการประเมินโดยตรงโดยไม่มีวงเล็บปิด

สามารถดูข้อมูลเพิ่มเติมได้ในคำถามนี้


10

คำตอบที่นี่เป็นสิ่งที่ดีนี่คือรายละเอียดของรหัสเทียม:

  • {}['whatever'] = บล็อกว่าง, NewArray ('อะไรก็ได้') = NewArray ('อะไรก็ได้')
  • {}[true] = บล็อกว่าง, NewArray (จริง) = NewArray (จริง)
  • !{}['whatever'] = LogicalNOT (convertToBool (NewObject.whatever)) = LogicalNOT (convertToBool (undefined)) = LogicalNOT (false) = true
  • ({}['whatever']) = การจัดกลุ่ม (NewObject.whatever) = การจัดกลุ่ม (ไม่ได้กำหนด) = ไม่ได้กำหนด

8

สิ่งนี้เกิดขึ้นเนื่องจาก{}ในความหมายของคุณไม่ใช่การนำเสนอตามตัวอักษรObjectแต่เป็นขอบเขตว่างเปล่า (หรือบล็อกโค้ดว่าง):

{ var a = 1 }[true] // [true] (do the same thing)

เพียงแค่ประเมินโค้ดภายในขอบเขตแล้วแสดงอาร์เรย์ของคุณ

และจากไฟล์

!{}[true]

เพียงแค่แปลงเป็น int ขอบเขตนี้และส่งคืนอาร์เรย์เดียวกันจริง ไม่มีการตรวจสอบบูลในรหัสนี้

และหากคุณจะลองตรวจสอบผลลัพธ์{}[true]คุณจะได้รับfalse:

{}[true] -> [true] -> ![true] -> false

เนื่องจากไม่มีขอบเขตใด ๆ อีกแล้ว

ดังนั้น!ในคำถามของคุณให้ทำเช่นเดียวกับ:

!function() {
   //...
}

var x = {}; x[true]นี้จะเห็นได้ง่ายขึ้นถ้าคุณทำ
Chris Hayes

1
ฉันไม่แน่ใจว่าคุณหมายถึงอะไร "แปลงเป็น int ขอบเขตนี้"; ฉันคิดว่าด้วยการนำหน้า!มันถูกตีความว่าเป็นวัตถุว่างเปล่าไม่ใช่ขอบเขตและนี่คือความคลาดเคลื่อน
IMSoP

6
  • {} เป็นวัตถุที่ไม่มีคุณสมบัติ
  • เนื่องจาก[]ทันทีที่ติดตามวัตถุหมายความว่า "เข้าถึงคุณสมบัติของชื่อนี้" ไม่ใช่ "สร้างอาร์เรย์"
  • trueเป็นบูลีน แต่ถูกใช้เป็นชื่อคุณสมบัติดังนั้นจึงถูกส่งไปยังสตริง ( "true")
  • วัตถุไม่มีคุณสมบัติที่เรียกว่าtrue(เนื่องจากไม่มีคุณสมบัติ) ดังนั้นจึง{}['true']เป็นundefined
  • !undefinedส่งundefinedไปยังบูลีน ( false)
  • ผลัดกันไม่ได้ผู้ประกอบการเข้าfalsetrue

2
ในกรณีของ{}[true](ไม่มีบริบทอื่น ๆ ) {}คือไม่ได้วัตถุที่มีคุณสมบัติที่ไม่มีมันเป็นการป้องกันรหัสที่ว่างเปล่า
IMSoP


4

มาเล่นกันอีกหน่อย!

ก่อนอื่นมาสนุกกันเถอะ!:

//----------#01#-----------
{}[true]; //[true]

//----------#02#-----------
var a = {}[true]; 
      console.log(a); //undefined

//----------#03#-----------
{ b: 12345 }[true]; //[true]

//----------#04#-----------
{ b: 12345 }["b"]; //evaluates to ["b"] ?!?

//----------#05#-----------
{ b: 12345 }.b; // "Unexpected token ."

//----------#06#-----------
({ b: 12345 }).b; //12345

//----------#07#-----------
var c = { b: 12345 }.b; 
      console.log(c); //12345

//----------#08#-----------
var c = { b: 12345 }["b"];
      console.log(c); //12345

//----------#09#-----------
{ true: 54321 }[true]; // "SyntaxError: Unexpected token : "

//----------#10#-----------
var d = { true: 54321 }[true]; //No error here ¬¬
      console.log(d); //54321

//----------#11#-----------
!{}[true]; // true

เอาล่ะเรามาลองทำความเข้าใจกับพฤติกรรมบ้าๆเหล่านี้ทีละคน:

1) ที่นี่{}จะแยกวิเคราะห์เป็นบล็อกรหัสว่าง หากไม่มีการกำหนดการปฏิเสธการจัดกลุ่ม (พร้อมวงเล็บ) หรือไวยากรณ์ใด ๆ ที่บ่งชี้ให้ผู้แยกวิเคราะห์ทราบว่านี่{}เป็นตัวอักษรตามตัวอักษรสมมติฐานเริ่มต้นคือคิดว่าเป็นเพียงบล็อกว่างเปล่าที่ไร้ประโยชน์

นี่เป็นข้อพิสูจน์ถึงพฤติกรรมนี้:

{ alert(123) }[true]

โค้ดด้านบนจะแสดงการแจ้งเตือนตามปกติและจะได้รับการประเมิน[true]ในลักษณะเดียวกัน{}[true]คือ

คำสั่งบล็อกที่ไม่มีอัฒภาค

คำสั่งประเภทบล็อกไม่จำเป็นต้องมีเครื่องหมายอัฒภาคตามหลัง

ตัวอย่างเช่น:

for(var i=0; i < 1; i++){}function a(){};alert("Passed here!");if(true){}alert("Passed here too!")

การแจ้งเตือนทั้งสองจะปรากฏขึ้น

ดังนั้นเราจะเห็นว่าคำสั่งบล็อกว่างโดยไม่มีอัฒภาคนั้นถูกต้องและไม่ทำอะไรเลย วิธีนี้เมื่อคุณใส่{}[true]ในเครื่องมือสำหรับนักพัฒนา (หรือ Firebug) Console ค่าประเมินจะเป็นค่าของที่ผ่านมางบการแสดงออก [true]ในกรณีนี้คำสั่งสุดท้ายคือการแสดงออก

2)ในบริบทการกำหนดตัววิเคราะห์จะตรวจสอบให้แน่ใจว่า{}เป็นอ็อบเจ็กต์ลิเทอรัล เมื่อคุณทำ var a = {}[true]คุณจะลบความคลุมเครือและดึงตัวแยกวิเคราะห์ออกซึ่ง{}ไม่ใช่คำสั่งบล็อก
ที่นี่คุณกำลังพยายามหาค่าด้วยคีย์"true"จากวัตถุว่างเปล่า เห็นได้ชัดว่าไม่มีคู่คีย์ - ค่ากับชื่อคีย์นี้ ด้วยวิธีนี้ตัวแปรไม่ได้กำหนดไว้

สงวนคำเป็นคีย์ออบเจ็กต์

ECMAScript 5อนุญาตให้คีย์ออบเจ็กต์เป็นคำสงวน ดังนั้นคีย์ต่อไปนี้ถูกต้องตามกฎหมาย:

var obj = {if: 111, for: 222, switch: 333, function: 444, true: 555}

3)คำอธิบายเดียวกันของตัวอย่างที่1 แต่ ... ถ้า{ b: 12345 }ส่วนนั้นถือว่าเป็น block statement ประเภทของb: 12345statement คืออะไร ??

... (?????)

มันเป็นคำสั่งฉลากแล้วคุณเห็นมันมาก่อน ... มันใช้ใน loops switchและใน ลิงค์ที่น่าสนใจเกี่ยวกับคำสั่ง label มีดังนี้1 , (2) [ วิธีที่ดีที่สุดในการแยกจากลูปที่ซ้อนกันใน Javascript? , (3) [ วิธีแบ่งลูปที่ซ้อนกันในจาวาสคริปต์? .

หมายเหตุ:ลองประเมินสิ่งนี้:

{a: 1, b: 2} //=>>>SyntaxError: Unexpected token :

คำสั่งป้ายกำกับไม่สามารถแยกออกจากตัวดำเนินการลูกน้ำได้คุณจะต้องคั่นด้วยเครื่องหมายอัฒภาค สิ่งนี้ถูกต้อง:{a: 1; b: 2}

4)ดูคำอธิบายสำหรับตัวอย่างที่1และ3 ...

5)อีกครั้งหนึ่งเรา{ b: 12345 }ถูกถือว่าเป็นบล็อกรหัสและคุณกำลังพยายามเข้าถึงคุณสมบัติของบล็อกรหัสโดยใช้สัญกรณ์จุดและเห็นได้ชัดว่าสิ่งนี้ไม่ได้รับอนุญาตและตัวแยกวิเคราะห์จะแสดง"Unexpected token :"ข้อยกเว้น

6)รหัสเกือบจะเหมือนกับตัวอย่างข้างต้น แต่โดยรอบ{ b: 12345 }คำสั่งที่มีตัวดำเนินการการจัดกลุ่มนิพจน์ตัวแยกวิเคราะห์จะรู้ว่าเป็นวัตถุ ด้วยวิธีนี้คุณจะสามารถเข้าถึง"b"อสังหาริมทรัพย์ได้ตามปกติ

7)จำตัวอย่างที่2เรามีการมอบหมายที่นี่ตัวแยกวิเคราะห์รู้ว่า{ b: 12345 }เป็นวัตถุ

8)เหมือนกับตัวอย่างข้างต้น แต่แทนที่จะสัญกรณ์ดอทนี่เรากำลังใช้สัญกรณ์วงเล็บ

9)ฉันได้กล่าวไปแล้วว่า"identifier: value"ไวยากรณ์นี้ในคำสั่งบล็อกเป็นป้ายกำกับ แต่คุณต้องรู้ด้วยว่าชื่อป้ายกำกับไม่สามารถเป็นคีย์เวิร์ดที่สงวนไว้ได้ (ตรงข้ามกับชื่อคุณสมบัติออบเจ็กต์) เมื่อเราพยายามกำหนดป้ายกำกับที่เรียกว่า"true"เราได้ไฟล์SyntaxError.

10)อีกครั้งเรากำลังจัดการกับวัตถุ ไม่มีปัญหาในการใช้คำสงวนที่นี่ =)

11)สุดท้ายเรามีสิ่งนี้:!{}[true]

มาแยกสิ่งต่างๆที่นี่:

ก) โดยการทำเช่นการปฏิเสธเราจะแจ้งความ parser ที่{}เป็นวัตถุ

ข) ตามที่ปรากฏในตัวอย่างที่ 2ซึ่งเป็น{}วัตถุที่ไม่ได้มีคุณสมบัติที่เรียกว่าเพื่อให้การแสดงออกนี้จะประเมินtrueundefined

c) ผลลัพธ์สุดท้ายคือการปฏิเสธundefinedมูลค่า จาวาสคริดำเนินการประเภท implicity แปลงและundefinedค่า falsy

ง) ดังนั้นการปฏิเสธfalseคือ ... true!

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.