อย่างที่เราทุกคนรู้ == ใน JavaScript เป็นการดำเนินการที่ค่อนข้างซับซ้อน กฎการดำเนินงานของมันแปลกมากและสามารถทำผิดพลาดได้อย่างง่ายดายทำให้เป็นหนึ่งใน "คุณสมบัติที่เลวร้ายที่สุด" ใน JavaScript
จากการอ่านข้อกำหนด ECMASCRIPT อย่างระมัดระวังฉันวาดภาพ ฉันคิดว่าหลังจากที่คุณเข้าใจภาพนี้คุณจะเข้าใจทุกอย่างเกี่ยวกับการดำเนินการ == ในเวลาเดียวกันฉันพยายามพิสูจน์ให้ทุกคนผ่านบทความนี้ว่า == ไม่ใช่สิ่งที่เลวร้าย เป็นเรื่องง่ายที่จะเชี่ยวชาญและดูสมเหตุสมผลและไม่เลว
ก่อนรูปภาพ:
== คำอธิบายที่แน่นอนของกฎการดำเนินการอยู่ที่นี่: อัลกอริทึมการเปรียบเทียบความเท่าเทียมกันของนามธรรม อย่างไรก็ตามด้วยคำอธิบายที่ซับซ้อนเช่นนี้คุณแน่ใจหรือว่าคุณจะไม่รู้สึกเวียนหัวหลังจากอ่าน คุณสามารถใช้เพื่อเป็นแนวทางในการฝึกฝนของคุณได้ทันทีหรือไม่?
มันไม่ได้ผลแน่นอน ท้ายที่สุดแล้วข้อมูลจำเพาะสำหรับนักพัฒนาสภาพแวดล้อมที่ใช้งาน JavaScript (เปรียบเทียบกับนักพัฒนาของเครื่องยนต์ V8) ไม่ใช่สำหรับผู้ใช้ภาษา ภาพด้านบนแปลข้อกำหนดเป็นแบบฟอร์มที่สะดวกสำหรับทุกคนที่จะเห็น
ก่อนที่จะแนะนำแต่ละส่วนในรูปที่ 1 ในรายละเอียดลองทบทวนความรู้เกี่ยวกับประเภทใน JS:
มีสองประเภทของค่าใน JS: ประเภทพื้นฐานและประเภทวัตถุ
ประเภทพื้นฐานรวมถึง: undefined, null, boolean, จำนวนและสตริง
ทั้งประเภทที่ไม่ได้กำหนดและประเภท NULL มีค่าเดียวเท่านั้นคือไม่ได้กำหนดและเป็นโมฆะ ประเภทบูลีนมีสองค่า: จริงและเท็จ; มีค่าจำนวนจำนวนมาก และประเภทสตริงมีค่านับไม่ถ้วน (ในทางทฤษฎี)
วัตถุทั้งหมดมีค่าของ () และวิธีการ toString () ซึ่งสืบทอดมาจากวัตถุและแน่นอนอาจถูกเขียนใหม่โดยคลาสย่อย
ตอนนี้พิจารณาการแสดงออก:
x == y
โดยที่ x และ y เป็นค่าของหนึ่งในหกประเภท
เมื่อประเภทของ x และ y เหมือนกัน x == y สามารถแปลงเป็น x === y และหลังนั้นง่ายมาก (สิ่งเดียวที่ควรทราบคือน่าน) ดังนั้นด้านล่างเราจะพิจารณากรณีที่ประเภทของ x และ y แตกต่างกัน
1. มีและไม่มี
ในรูปที่ 1 ค่า JavaScript หกประเภทจะถูกแทนด้วยรูปสี่เหลี่ยมผืนผ้าที่มีพื้นหลังสีน้ำเงิน ก่อนอื่นพวกเขาแบ่งออกเป็นสองกลุ่ม:
สตริงตัวเลขบูลีนและวัตถุ (สอดคล้องกับกล่องสี่เหลี่ยมขนาดใหญ่ทางด้านซ้าย)
undefined และ null (สอดคล้องกับกล่องสี่เหลี่ยมทางด้านขวา)
พื้นฐานสำหรับการจัดกลุ่มคืออะไร? มาดูกันเถอะ undefined และ null ทางด้านขวาใช้เพื่อระบุความไม่แน่นอนไม่ใช่หรือว่างเปล่าในขณะที่สี่ประเภททางด้านขวาถูกกำหนดทั้งหมดที่มีอยู่และไม่ว่างเปล่า เราสามารถพูดแบบนี้ได้:
ทางด้านซ้ายเป็นโลกแห่งการดำรงอยู่และทางด้านขวาเป็นโลกที่ว่างเปล่า
ดังนั้นจึงมีเหตุผลที่จะเปรียบเทียบคุณค่าใด ๆ ในโลกทั้งสองด้วยเท็จ (เช่นเส้นแนวนอนที่เชื่อมต่อสองสี่เหลี่ยมในรูปที่ 1 ถูกทำเครื่องหมายเท็จ)
2. ว่างเปล่าและว่างเปล่า
undefined และ null ใน JavaScript เป็นอีกสถานที่หนึ่งที่มักจะขัดข้องเรา มันมักจะถือว่าเป็นข้อบกพร่องในการออกแบบซึ่งเราจะไม่ขุดลงไป แต่ฉันเคยได้ยินมาว่าผู้เขียน JavaScript ในตอนแรกคิดเรื่องนี้:
หากคุณวางแผนที่จะกำหนดตัวแปรให้กับค่าของประเภทวัตถุ แต่ยังไม่ได้กำหนดค่าคุณสามารถใช้ null เพื่อเป็นตัวแทนของสถานะในเวลานี้ (หนึ่งในหลักฐานคือผลลัพธ์ของ typeof null คือ 'วัตถุ'); ในทางตรงกันข้ามหากคุณวางแผนที่จะกำหนดตัวแปรให้กับค่าของประเภทดั้งเดิม แต่ยังไม่ได้กำหนดค่าคุณสามารถใช้ที่ไม่ได้กำหนดเพื่อเป็นตัวแทนของสถานะในเวลานี้
ไม่ว่าข่าวลือนี้จะน่าเชื่อถือหรือไม่ก็มีเหตุผลว่าผลของการเปรียบเทียบระหว่างทั้งสองนั้นเป็นจริง (เช่นทำเครื่องหมายจริงบนเส้นแนวตั้งทางด้านขวาในรูปที่ 1)
ก่อนที่จะไปยังขั้นตอนต่อไปให้พูดคุยเกี่ยวกับสัญลักษณ์ทั้งสองในรูปที่ 1: ตัวอักษรตัวพิมพ์ใหญ่ N และ P สัญลักษณ์ทั้งสองนี้ไม่ได้หมายถึงบวกและลบในส่วน PN แทน:
n หมายถึงการทำงานของ Tonumber ซึ่งหมายถึงการแปลงตัวถูกดำเนินการเป็นตัวเลข มันเป็นการดำเนินการที่เป็นนามธรรมในข้อกำหนด ES แต่เราสามารถใช้ฟังก์ชั่น Number () ใน JS เพื่อแทนที่ได้อย่างเท่าเทียมกัน
P หมายถึงการดำเนินการ toprimitive นั่นคือการแปลงตัวถูกดำเนินการเป็นค่าของประเภทดั้งเดิม นอกจากนี้ยังเป็นการดำเนินการที่เป็นนามธรรมในข้อกำหนด ES และยังสามารถแปลเป็นรหัส JS ที่เทียบเท่า แต่มันซับซ้อนกว่าเล็กน้อย เพื่อนำไปใช้อย่างง่าย ๆ สำหรับวัตถุ obj:
Toprimitive (OBJ) เทียบเท่ากับ: คำนวณ OBJ.ValueOf () ครั้งแรกหากผลลัพธ์เป็นค่าดั้งเดิมผลลัพธ์นี้จะถูกส่งคืน; มิฉะนั้น obj.toString () จะถูกคำนวณและหากผลลัพธ์เป็นค่าดั้งเดิมผลลัพธ์นี้จะถูกส่งคืน; มิฉะนั้นจะมีการโยนข้อยกเว้น
หมายเหตุ: มีข้อยกเว้นที่นี่นั่นคือวัตถุของวันที่พิมพ์ซึ่งจะเรียกวิธี ToString () ก่อน
ในรูปที่ 1 บรรทัดที่ทำเครื่องหมาย N หรือ P ระบุว่าเมื่อข้อมูลทั้งสองประเภทนั้นเชื่อมต่อกับการดำเนินการ == ตัวถูกดำเนินการที่ด้านข้างที่ทำเครื่องหมาย N หรือ P จะต้องทำการแปลง tonumber หรือ tonumitive ก่อน
3. จริงและเท็จ
ดังที่เห็นได้จากรูปที่ 1 เมื่อเปรียบเทียบค่าบูลีนกับค่าประเภทอื่น ๆ ค่าบูลีนจะถูกแปลงเป็นตัวเลข โดยเฉพาะ
จริง -> 1
เท็จ -> 0
สิ่งนี้ไม่จำเป็นต้องใช้การละเมิดทางวาจามากเกินไป คิดเกี่ยวกับมันใน C ไม่มีประเภทบูลีนเลย จำนวนเต็ม 1 และ 0 มักจะใช้เพื่อแสดงถึงตรรกะจริงหรือเท็จ
iv. ลำดับของอักขระ
ในรูปที่ 1 เราแบ่งสตริงและตัวเลขออกเป็นกลุ่ม ทำไม ในบรรดาหกประเภทสตริงและจำนวนคือลำดับของอักขระ (อย่างน้อยตัวอักษร) สตริงเป็นลำดับของอักขระทางกฎหมายทั้งหมดในขณะที่ตัวเลขสามารถถือได้ว่าเป็นลำดับของอักขระที่ตรงตามเงื่อนไขบางประการ ดังนั้นตัวเลขจึงถือได้ว่าเป็นชุดย่อยของสตริง
ตามรูปที่ 1 เมื่อดำเนินการ == การดำเนินการของสตริงและตัวเลขคุณต้องใช้การดำเนินการ Tonumber เพื่อแปลงสตริงเป็นตัวเลข สมมติว่า x เป็นสตริงและ y คือตัวเลขแล้ว:
x == y -> number (x) == y
ดังนั้นกฎสำหรับการแปลงสตริงเป็นตัวเลขคืออะไร? ข้อมูลจำเพาะอธิบายไว้ในวิธีที่ซับซ้อนมาก แต่โดยทั่วไปแล้วการพูดคือการลบคำพูดทั้งสองด้านของสตริงและดูว่ามันสามารถสร้างหมายเลขทางกฎหมายได้หรือไม่ ถ้าเป็นเช่นนั้นผลการแปลงคือตัวเลขนี้ มิฉะนั้นผลลัพธ์คือน่าน ตัวอย่างเช่น:
หมายเลข ('123') // ผลลัพธ์ 123
หมายเลข ('1.2E3') // ผลลัพธ์ 1200
หมายเลข ('123ABC') // ผลลัพธ์ NAN
แน่นอนว่ามีข้อยกเว้นเช่นผลลัพธ์ของการแปลงสตริงว่างเป็นตัวเลขคือ 0 ตอนนี้
หมายเลข ('') // ผลลัพธ์ 0
V. ง่ายและซับซ้อน
ประเภทดั้งเดิมเป็นประเภทง่าย ๆ พวกเขาตรงไปตรงมาและเข้าใจง่าย อย่างไรก็ตามข้อเสียคือความสามารถในการแสดงออกมี จำกัด และยากที่จะขยายดังนั้นจึงมีวัตถุ วัตถุคือคอลเลกชันของแอตทริบิวต์และแอตทริบิวต์เองสามารถเป็นวัตถุได้ ดังนั้นวัตถุสามารถสร้างความซับซ้อนโดยพลการพอที่จะเป็นตัวแทนของสิ่งต่าง ๆ
แต่บางครั้งสิ่งต่าง ๆ ก็ซับซ้อนและไม่ใช่สิ่งที่ดี ตัวอย่างเช่นไม่ใช่ทุกคนที่มีเวลาความอดทนหรือความจำเป็นในการอ่านตั้งแต่ต้นจนจบ โดยปกติแล้วมันก็เพียงพอที่จะเข้าใจความคิดกลางเท่านั้น ดังนั้นกระดาษจึงมีคำหลักและภาพรวม เช่นเดียวกับวัตถุใน JavaScript เราจำเป็นต้องมีวิธีการที่จะเข้าใจคุณลักษณะหลักดังนั้นวัตถุจึงมีวิธีการ toString () และ valueof ()
วิธีการ TOSTRING () ใช้เพื่อรับคำอธิบายข้อความของวัตถุ และวิธีการของค่า () ใช้เพื่อรับค่าลักษณะเฉพาะของวัตถุ
แน่นอนว่านี่เป็นเพียงความเข้าใจของฉันเอง นอกจากนี้ตามชื่อหมายถึงวิธี ToString () มีแนวโน้มที่จะส่งคืนสตริง วิธีการเกี่ยวกับ valueof ()? ตามคำอธิบายในข้อกำหนดมันมีแนวโน้มที่จะส่งคืนตัวเลข - แม้ว่าในประเภทในตัววิธีการ valueof () จะส่งคืนตัวเลขและวันที่เท่านั้น
ตามรูปที่ 1 เมื่อเปรียบเทียบกับวัตถุที่ไม่ใช่วัตถุวัตถุจะต้องถูกแปลงเป็นประเภทดั้งเดิม (แม้ว่าเมื่อเปรียบเทียบกับชนิดบูลีนชนิดบูลีนจะต้องถูกแปลงเป็นประเภทตัวเลขก่อน แต่ประเภทวัตถุจะต้องถูกแปลงเป็นประเภทดั้งเดิม) นี่ก็สมเหตุสมผลเช่นกัน ท้ายที่สุด == ไม่ได้ทำการเปรียบเทียบเท่ากันอย่างเคร่งครัด เราจำเป็นต้องนำคุณสมบัติหลักของวัตถุออกมาเพื่อเข้าร่วมในการดำเนินการและวางคุณสมบัติรองไว้
หก. ทุกอย่างถูกนับ
ลองมองย้อนกลับไปที่รูปที่ 1 บรรทัดที่ทำเครื่องหมาย N หรือ P ภายในไม่มีทิศทาง หากเราทำเครื่องหมายลูกศรในบรรทัดเหล่านี้จุดเชื่อมต่อจากจุดสิ้นสุดที่ทำเครื่องหมาย N หรือ P ไปอีกด้านหนึ่งเราจะได้รับ (ไม่พิจารณาที่ไม่ได้กำหนดและเป็นโมฆะ):
คุณค้นพบอะไร? ใช่ในระหว่างกระบวนการคำนวณค่าทุกประเภทมีแนวโน้มที่จะแปลงเป็นประเภทตัวเลข ท้ายที่สุดคนดังเคยกล่าวไว้ว่า:
ทุกอย่างถูกนับ
7. แค่ให้ฉันเกาลัด
ในอดีตมีเรื่องไร้สาระมากเกินไปดังนั้นนี่คือตัวอย่างที่จะพิสูจน์ว่ารูปที่ 1 นั้นสะดวกและมีประสิทธิภาพในการเป็นแนวทางในการฝึกฝน
ตัวอย่างคำนวณสิ่งต่อไปนี้:
[''] == FALSE
ขั้นแรกตัวถูกดำเนินการทั้งสองเป็นประเภทวัตถุและประเภทบูลีนตามลำดับ ตามรูปที่ 1 จำเป็นต้องแปลงประเภทบูลีนเป็นประเภทตัวเลขและผลลัพธ์ของการแปลงเท็จเป็นตัวเลขคือ 0 ดังนั้นนิพจน์จะกลายเป็น:
[''] == 0
ตัวถูกดำเนินการทั้งสองกลายเป็นประเภทวัตถุและประเภทตัวเลข ตามรูปที่ 1 ประเภทวัตถุจะต้องถูกแปลงเป็นประเภทดั้งเดิม:
ก่อนอื่นโทร [] .ValueOf () เนื่องจากวิธีการของค่า () ของอาร์เรย์ส่งคืนตัวเองผลลัพธ์จึงไม่ใช่ประเภทดั้งเดิม โทรต่อไป [] .tostring ()
สำหรับอาร์เรย์อัลกอริทึมของวิธี TOSTRING () คือการแปลงแต่ละองค์ประกอบเป็นประเภทสตริงจากนั้นเชื่อมต่อกับ ',' ดังนั้นผลลัพธ์สุดท้ายคือสตริงว่างเปล่าซึ่งเป็นค่าของประเภทดั้งเดิม
ณ จุดนี้การแสดงออกจะกลายเป็น:
'' == 0
ตัวถูกดำเนินการทั้งสองกลายเป็นประเภทสตริงและประเภทตัวเลข ตามรูปที่ 1 ประเภทสตริงจะต้องถูกแปลงเป็นประเภทตัวเลข ดังที่ได้กล่าวไว้ก่อนหน้านี้สตริงที่ว่างเปล่ากลายเป็นจำนวน 0 ดังนั้นนิพจน์จะกลายเป็น:
0 == 0
จนถึงตอนนี้ประเภทของตัวถูกดำเนินการทั้งสองในที่สุดก็เหมือนกันและผลลัพธ์ที่ได้ก็เป็นจริง
จากตัวอย่างนี้เราจะเห็นได้ว่าเพื่อที่จะควบคุมกฎของการดำเนินการ == นอกเหนือจากการจดจำรูปที่ 1 เรายังต้องจดจำกฎของวิธีการ toString () และ valueof () ของวัตถุในตัวเหล่านั้น รวมถึงวัตถุ, อาร์เรย์, วันที่, หมายเลข, สตริง, บูลีน ฯลฯ
8. มาสรุปกันเถอะ
คำแถลงก่อนหน้านี้สับสนมาก ที่นี่ฉันจะสรุปกฎของ == การดำเนินการที่แสดงในรูปที่ 1:
ผลลัพธ์ของ undefined == null เป็นจริง ผลลัพธ์ของการเปรียบเทียบกับค่าอื่น ๆ ทั้งหมดเป็นเท็จ
เมื่อสตริง == หมายเลขสตริงจะถูกแปลงเป็นตัวเลข
ค่าบูลีน == เมื่อใช้ประเภทอื่น ๆ ค่าบูลีนจะถูกแปลงเป็นตัวเลข
เมื่อวัตถุ == ตัวเลข/สตริงวัตถุจะถูกแปลงเป็นประเภทดั้งเดิม
ในที่สุดฉันก็เปลี่ยนรูปภาพเป็นความบันเทิงเท่านั้น :)
ตกลงมันจบแล้ว หากคุณคิดว่าบทความนี้มีประโยชน์สำหรับคุณโปรดชอบเพื่อให้ผู้คนเห็นได้มากขึ้น
นอกจากนี้โปรดชี้ให้เห็นถึงความผิดพลาดในบทความ