ความแตกต่างระหว่าง (NaN! = NaN) และ (NaN! == NaN) คืออะไร


148

ก่อนอื่นฉันต้องการพูดถึงว่าฉันรู้วิธีisNaN()และการNumber.isNaN()ทำงาน ฉันกำลังอ่านคู่มือที่ชัดเจนโดย David Flanagan และเขายกตัวอย่างวิธีการตรวจสอบว่ามีค่าNaN:

x !== x

ซึ่งจะส่งผลtrueถ้าหากมีxNaN

แต่ตอนนี้ฉันมีคำถาม: ทำไมเขาใช้การเปรียบเทียบที่เข้มงวด เพราะดูเหมือนว่า

x != x

ทำงานในลักษณะเดียวกัน มันมีความปลอดภัยที่จะใช้ทั้งสองรุ่นหรือฉันหายไปค่าบางอย่าง (s) ใน JavaScript ที่จะกลับมาtrueสำหรับx !== xและfalseสำหรับx != x?


10
อาจเป็นไปได้ว่าฟลานาแกนชอบ!==เช็คมากกว่า!=เช็ค เท่าที่ผมทราบไม่มีค่าอื่น ๆ x != xที่ แต่มีกลุ่มนักพัฒนา JavaScript ที่แตกต่างกันสองกลุ่ม: กลุ่มที่ชอบ!=และกลุ่มที่ชอบ!==ไม่ว่าจะเป็นเพื่อความเร็วความชัดเจนความหมาย ฯลฯ
Steve Klösters

30
เหตุใดจึงต้องใช้การเปรียบเทียบแบบหลวมเมื่อการเปรียบเทียบที่เข้มงวดทำงานในลักษณะเดียวกัน
Ry-

3
@Raulucco: NaNไม่ใช่ประเภทที่ไม่ซ้ำกันมันเป็นตัวเลข มันเป็นคุณค่าที่ไม่เหมือนใคร
TJ Crowder

8
ดูเหมือนว่าชื่อนั้นจะทำให้ผู้คนเข้าใจผิด ฉันขอแนะนำให้เปลี่ยนเป็นแบบ "Is x! = x แตกต่างจาก x! == x หรือไม่"
TJ Crowder

6
@femmestem: Giorgi พูดว่า "ในกรณีนี้" มันเป็นเรื่องของสไตล์ และเขาก็พูดถูก มันไม่ใช่สไตล์เมื่อประเภทของตัวถูกดำเนินการแตกต่างกัน แต่เป็นรูปแบบเมื่อพวกมันเหมือนกัน ต่างหาก: ฟลานาแกนกำลังทำการเปรียบเทียบ===กับ NaN เพื่อให้ประเด็นที่ว่าแนนนั้นไม่เท่ากับตัวของมันเอง เขาไม่ได้ "ผิด" เขากำลังทำแบบฝึกหัดการสอนซึ่งแสดงว่ามันไม่ได้ผล
TJ Crowder

คำตอบ:


128

ก่อนอื่นให้ฉันชี้ให้เห็นว่าNaNเป็นค่าพิเศษมาก: ตามคำนิยามมันไม่เท่ากับตัวเอง มาจากมาตรฐาน IEEE-754 ที่ใช้จาวาสคริปต์ ค่า "ไม่ใช่ตัวเลข" จะไม่เท่ากับตัวเองแม้ว่าบิตจะเป็นการจับคู่ที่ตรงกัน (ซึ่งไม่จำเป็นต้องอยู่ใน IEEE-754 แต่จะอนุญาตให้ใช้ค่า "ไม่ใช่ตัวเลข" ได้หลายค่า) ซึ่งเป็นสาเหตุที่ทำให้เกิดสิ่งนี้ขึ้นมา ค่าอื่น ๆ ทั้งหมดในจาวาสคริปต์นั้นมีค่าเท่ากับตัวเองNaNเป็นพิเศษ

... ฉันไม่มีค่าบางอย่างใน JavaScript ที่จะคืนค่าจริงสำหรับ x! == x และ false สำหรับ x! = x?

ไม่คุณไม่ได้ ความแตกต่างเพียงอย่างเดียวระหว่าง!==และ!=คือว่าหลังจะบีบบังคับประเภทถ้าจำเป็นที่จะได้รับประเภทของตัวถูกดำเนินการจะเหมือนกัน ในประเภทของตัวถูกดำเนินการที่มีความเหมือนกันและดังนั้นจึงเป็นเหมือนกับx != xx !== x

สิ่งนี้ชัดเจนตั้งแต่จุดเริ่มต้นของคำนิยามของการดำเนินงานความเสมอภาคทางนามธรรม :

  1. ReturnIfAbrupt (x)
  2. ReturnIfAbrupt (y)
  3. หาก Type (x) เหมือนกับ Type (y) ดังนั้น

    ส่งคืนผลลัพธ์ของการทำการเปรียบเทียบความเข้มงวดที่เข้มงวด x === y

  4. ...

สองขั้นตอนแรกคือการประปาขั้นพื้นฐาน ดังนั้นในขั้นตอนแรกของ==การดูว่าประเภทเดียวกันหรือไม่และหากเป็นเช่นนั้นให้ทำ===แทน !=และ!==เป็นรุ่นเมื่อตะกี้ของที่

ดังนั้นหากฟลานาแกนถูกต้องที่เพียงNaNให้ความจริงx !== xเราก็มั่นใจได้ว่ามันจะเป็นจริงที่NaNจะให้ความจริงx != xเท่านั้น

โปรแกรมเมอร์จาวาสคริปต์หลายคนเริ่มต้นที่จะใช้===และ!==หลีกเลี่ยงข้อผิดพลาดบางอย่างเกี่ยวกับการบีบบังคับแบบหลวม ๆ ที่ผู้ประกอบการทำ


ฉันอ่านซ้ำ4.9.1 - Equality and Inequality Operatorsและนี่น่าจะเป็นคำตอบ จุดสำคัญสำหรับการเปรียบเทียบคือ:=== If the two values have the same type, test them for strict equality as described above. If they are strictly equal, they are equal. If they are not strictly equal, they are not equal
Giorgi Nakeuri

@GiorgiNakeuri: ฉันไม่แน่ใจว่าคุณหมายถึงอะไร 4.9.1 บางทีหนังสือของฟลานาแกน? แต่โดยทั่วไปแล้วสิ่งที่พูดจากสเป็คข้างต้นพูดใช่
TJ Crowder

2
ฉันยอมรับสิ่งนี้เพราะนี่ตอบคำถามของฉันอย่างเป็นทางการและแม่นยำ ขอบคุณสำหรับคำอธิบาย!
Giorgi Nakeuri

1
@Moshe: คุณหมายถึงอะไรโดย "การผูกสด" (คำนั้นไม่ปรากฏในข้อมูลจำเพาะ) คุณหมายถึงตัวอย่างเช่น GOTO 0ซึ่งaเป็นฟังก์ชันจริงและไม่ส่งคืนค่าเดิมสองครั้งหรือไม่? นั่นไม่ใช่สิ่งเดียวกับค่าที่!==จะเป็นจริงซึ่งเป็นสิ่งที่ OP ถาม เป็นเพียงฟังก์ชันที่คืนค่าต่างกัน foo() !== foo()ไม่จำเป็นจริงเช่นกันเนื่องจากfooอาจส่งคืนค่าที่แตกต่างกันในการโทรแต่ละครั้ง
TJ Crowder

1
@Moshe นั่นเป็นวิธีที่น่ารังเกียจอย่างยิ่งที่จะยุ่งกับคุณสมบัติและผู้ได้รับ แต่มันดูเหมือนจะเป็นแบบเดียวกันกับตัวอย่างของ GOTO 0 เพียงแค่มีการเพิ่มเลเยอร์ทางอ้อม
JAB

37

สำหรับวัตถุประสงค์ของ NaN !=และ!==ทำสิ่งเดียวกัน

อย่างไรก็ตามโปรแกรมเมอร์จำนวนมากหลีกเลี่ยง==หรือ!=ใช้ JavaScript ตัวอย่างเช่น Douglas Crockford ถือว่าพวกเขาอยู่ใน " ส่วนที่ไม่ดี " ของภาษา JavaScript เพราะพวกเขาทำงานในลักษณะที่ไม่คาดคิดและสับสน:

JavaScript มีสองชุดของผู้ประกอบการเท่าเทียมกัน: ===และ!==และฝาแฝดที่ชั่วร้ายของพวกเขาและ== !=คนดีทำงานในแบบที่คุณคาดหวัง

... คำแนะนำของฉันคืออย่าใช้ฝาแฝดที่ชั่วร้าย แต่มักจะใช้และ===!==


2
คำถามไม่ได้เกี่ยวกับ NaN (แม้จะมีชื่อเรื่อง) คำถามคือ"ฉันไม่มีค่าบางอย่างใน JavaScript ที่จะคืนค่าจริงสำหรับ x! == x และ false สำหรับ x! = x?"
TJ Crowder

@TJCrowder สองคำถามจริง ๆ คำถามแรกคือ "มีความปลอดภัยในการใช้ทั้งสองเวอร์ชันหรือไม่" และคำตอบก็คือทั้งสองรุ่นนั้นเทียบเท่ากัน ฉันชอบคำตอบ "ภายใต้ประทุน" ซึ่งอธิบายทุกอย่างในรายละเอียด
jkdev

22

เพื่อความสนุกผมขอแสดงตัวอย่างที่xไม่ได้อยู่ตรงหน้าNaNแต่ตัวดำเนินการทำงานแตกต่างออกไป ก่อนกำหนด:

Object.defineProperty(
  self,
  'x',
  { get: function() { return self.y = self.y ? 0 : '0'; } }
);

ถ้าอย่างนั้นเราก็มี

x != x // false

แต่

x !== x // true

9
ฮา! :-) แต่นั่นก็มีประสิทธิภาพfoo() != foo()เมื่อ foo คืนค่า 1 แล้ว 2 เช่นค่าไม่เหมือนกันมันแค่เปรียบเทียบค่าที่ต่างกัน
TJ Crowder

2

ฉันแค่ต้องการชี้ให้เห็นNaNไม่ใช่สิ่งเดียวที่ผลิตx !== xโดยไม่ใช้วัตถุระดับโลก มีวิธีที่ชาญฉลาดมากมายในการกระตุ้นให้เกิดพฤติกรรมนี้ นี่คือสิ่งที่ใช้ getters:

var i = 0, obj = { get x() { return i++; }};
with(obj) // force dynamic context, this is evil. 
console.log(x === x); // false

ดังที่คำตอบอื่น ๆ ชี้ให้เห็น==ดำเนินการพิมพ์ดีด แต่ในภาษาอื่น ๆ และมาตรฐาน - NaN บ่งบอกถึงความล้มเหลวในการคำนวณและด้วยเหตุผลที่ดีไม่เท่ากับตัวเอง

ด้วยเหตุผลบางอย่างนอกเหนือจากฉันคนที่เห็นปัญหานี้กับ JS แต่ภาษาส่วนใหญ่ที่มีสองเท่า (เช่น C, Java, C ++, C #, Python และอื่น ๆ ) แสดงพฤติกรรมที่แน่นอนนี้และผู้คนก็ไม่เป็นไร


2
ใช่นั่นคือสิ่งที่ @TJCrowder พูดถึงในความคิดเห็นต่อคำตอบของ GOTO_0 ใช่ไหม?
Giorgi Nakeuri

คุณช่วยอธิบายวิธีการข่มขู่ประเภทที่ไม่ชัดเจนในภาษาอื่น ๆ เหล่านี้ได้ไหม?
chicocvenancio

0

บางครั้งรูปภาพก็ดีกว่าคำตรวจสอบตารางนี้(ซึ่งเป็นเหตุผลสำหรับฉันที่จะทำให้คำตอบนี้แทนความคิดเห็นเพราะมันทำให้ทัศนวิสัยดีขึ้น)

คุณจะเห็นได้ว่าการเปรียบเทียบความเท่าเทียมที่เข้มงวด (===) จะส่งกลับค่าจริงหากประเภทและเนื้อหาตรงกันเท่านั้น

var f = "-1" === -1; //false

ในขณะที่การเปรียบเทียบความเท่าเทียมกันทางนามธรรม (==) ตรวจสอบเฉพาะเนื้อหา * โดยการแปลงประเภทแล้วทำการเปรียบเทียบอย่างเคร่งครัด:

var t = "-1" == -1; //true

แม้ว่ามันจะไม่ชัดเจนหากไม่ได้ปรึกษาECMAแต่ JavaScript จะพิจารณาสิ่งใดเมื่อเปรียบเทียบในลักษณะที่การร้องของรหัสประเมินเป็นจริง

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