TLDR
การบังคับประเภทหรือการแปลงประเภทโดยปริยายช่วยให้การพิมพ์อ่อนแอและใช้ตลอดจาวาสคริปต์ ตัวดำเนินการส่วนใหญ่ (ยกเว้นที่น่าสังเกตของตัวดำเนินการความเสมอภาคที่เข้มงวด===และ!==) และการดำเนินการตรวจสอบค่า (เช่นif(value)...) จะบังคับใช้ค่าที่ให้มาหากประเภทของค่าเหล่านั้นไม่สามารถเข้ากันได้กับการดำเนินการในทันที
กลไกที่แม่นยำที่ใช้ในการบังคับค่าขึ้นอยู่กับนิพจน์ที่กำลังประเมิน ในคำถามนี้กำลังใช้ตัวดำเนินการเพิ่มเติม
ขั้นแรกตัวดำเนินการเพิ่มเติมจะตรวจสอบให้แน่ใจว่าตัวถูกดำเนินการทั้งสองเป็นแบบดั้งเดิมซึ่งในกรณีนี้เกี่ยวข้องกับการเรียกใช้valueOfเมธอด toStringวิธีการไม่ได้เรียกว่าในกรณีนี้เพราะแทนที่valueOfวิธีการบนวัตถุที่xส่งกลับค่าดั้งเดิม
จากนั้นเนื่องจากหนึ่งในตัวถูกดำเนินการในคำถามเป็นสตริงตัวถูกดำเนินการทั้งสองจะถูกแปลงเป็นสตริง กระบวนการนี้ใช้นามธรรมการดำเนินการภายในToString(หมายเหตุ: ตัวพิมพ์ใหญ่) และแตกต่างจากtoStringวิธีการบนวัตถุ (หรือห่วงโซ่ต้นแบบ)
สุดท้ายสตริงผลลัพธ์จะถูกเชื่อมต่อกัน
รายละเอียด
บนต้นแบบของอ็อบเจ็กต์ฟังก์ชันคอนสตรัคเตอร์ทุกตัวที่สอดคล้องกับทุกประเภทภาษาใน JavaScript (เช่น Number, BigInt, String, Boolean, Symbol และ Object) มีสองวิธี: valueOfและtoString.
จุดประสงค์valueOfคือเพื่อดึงค่าดั้งเดิมที่เกี่ยวข้องกับอ็อบเจ็กต์ (ถ้ามี) หากออบเจ็กต์ไม่มีค่าดั้งเดิมพื้นฐานก็จะส่งคืนวัตถุ
หากvalueOfมีการเรียกใช้กับไพรเมทีฟดั้งเดิมจะถูกใส่กล่องอัตโนมัติตามวิธีปกติและค่าดั้งเดิมที่อยู่เบื้องหลังจะส่งกลับ โปรดสังเกตว่าสำหรับสตริงค่าดั้งเดิมที่อยู่เบื้องหลัง (เช่นค่าที่ส่งคืนโดยvalueOf) คือการแทนค่าสตริงเอง
โค้ดต่อไปนี้แสดงให้เห็นว่าvalueOfเมธอดส่งคืนค่าพื้นฐานจากอ็อบเจ็กต์ wrapper และแสดงให้เห็นว่าอินสแตนซ์อ็อบเจ็กต์ที่ไม่ได้ปรับเปลี่ยนที่ไม่สอดคล้องกับไพรเมทีฟไม่มีค่าดั้งเดิมที่จะส่งคืนอย่างไรดังนั้นพวกมันจึงส่งคืนตัวเอง
console.log(typeof new Boolean(true)) // 'object'
console.log(typeof new Boolean(true).valueOf()) // 'boolean'
console.log(({}).valueOf()) // {} (no primitive value to return)
ในทางกลับกันจุดประสงค์toStringคือส่งคืนการแสดงสตริงของวัตถุ
ตัวอย่างเช่น:
console.log({}.toString()) // '[object Object]'
console.log(new Number(1).toString()) // '1'
สำหรับการดำเนินการส่วนใหญ่ JavaScript จะพยายามแปลงตัวถูกดำเนินการอย่างน้อยหนึ่งรายการเป็นประเภทที่ต้องการ พฤติกรรมนี้ถูกเลือกเพื่อทำให้ JavaScript ใช้งานได้ง่ายขึ้น JavaScript ในตอนแรกไม่มีข้อยกเว้นและสิ่งนี้อาจมีบทบาทในการตัดสินใจออกแบบนี้ด้วย การแปลงประเภทโดยนัยประเภทนี้เรียกว่าการบังคับประเภทและเป็นพื้นฐานของระบบประเภทหลวม (อ่อนแอ) ของ JavaScript กฎที่ซับซ้อนเบื้องหลังพฤติกรรมนี้มีจุดมุ่งหมายเพื่อย้ายความซับซ้อนของการพิมพ์เป็นภาษาตัวเองและออกจากโค้ดของคุณ
ในระหว่างกระบวนการบีบบังคับมีสองโหมดของการแปลงที่สามารถเกิดขึ้นได้:
- การแปลงออบเจ็กต์เป็นแบบดั้งเดิม (ซึ่งอาจเกี่ยวข้องกับการแปลงประเภทเอง) และ
- แปลงโดยตรงตัวอย่างประเภทที่เฉพาะเจาะจงโดยใช้วัตถุที่ฟังก์ชั่นคอนสตรัคของหนึ่งในรูปแบบดั้งเดิม (เช่น.
Number(), Boolean(), String()ฯลฯ )
การแปลงเป็นดั้งเดิม
เมื่อพยายามแปลงชนิดที่ไม่ใช่แบบดั้งเดิมไปเป็นแบบดั้งเดิมที่จะดำเนินการการดำเนินการนามธรรมToPrimitiveจะถูกเรียกโดยใช้ "คำใบ้" ของ "จำนวน" หรือ "สตริง" ที่เป็นทางเลือก หากคำใบ้ถูกละไว้คำใบ้เริ่มต้นคือ 'number' (เว้นแต่จะ@@toPrimitiveมีการแทนที่เมธอดนั้น) หากคำใบ้เป็น 'สตริง' toStringจะถูกลองก่อนและvalueOfครั้งที่สองถ้าtoStringไม่ส่งคืนค่าดั้งเดิม อื่น ๆ ในทางกลับกัน คำใบ้ขึ้นอยู่กับการดำเนินการขอการแปลง
ตัวดำเนินการเพิ่มเติมไม่มีคำใบ้จึงvalueOfพยายามก่อน ตัวดำเนินการลบให้คำใบ้ของ 'จำนวน' ดังนั้นจึงvalueOfต้องพยายามก่อน สถานการณ์เดียวที่ฉันพบในข้อมูลจำเพาะซึ่งคำใบ้คือ 'สตริง' คือ:
Object#toString
- การดำเนินการนามธรรม
ToPropertyKeyซึ่งแปลงอาร์กิวเมนต์เป็นค่าที่อาจใช้เป็นคีย์คุณสมบัติ
การแปลงประเภทโดยตรง
แต่ละตัวดำเนินการมีกฎของตัวเองในการดำเนินการให้เสร็จสิ้น ตัวดำเนินการเพิ่มเติมจะใช้ToPrimitiveเพื่อให้แน่ใจว่าแต่ละตัวถูกดำเนินการเป็นแบบดั้งเดิม จากนั้นถ้าตัวถูกดำเนินการใด ๆ เป็นสตริงจากนั้นก็จะเรียกใช้การดำเนินการนามธรรมToStringกับตัวถูกดำเนินการแต่ละตัวโดยเจตนาเพื่อส่งมอบพฤติกรรมการต่อสายอักขระที่เราคาดหวังด้วยสตริง ถ้าหลังจากToPrimitiveขั้นตอนตัวถูกดำเนินการทั้งสองไม่ใช่สตริงการบวกเลขคณิตจะดำเนินการ
ซึ่งแตกต่างนอกจากนี้ผู้ประกอบการลบไม่ได้มากเกินไปพฤติกรรมและอื่น ๆ จะเรียกในแต่ละตัวถูกดำเนินการมีแปลงแรกพวกเขาที่จะใช้วิทยาการtoNumericToPrimitive
ดังนั้น:
1 + 1 // 2
'1' + 1 // '11' Both already primitives, RHS converted to string, '1' + '1', '11'
1 + [2] // '12' [2].valueOf() returns an object, so `toString` fallback is used, 1 + String([2]), '1' + '2', 12
1 + {} // '1[object Object]' {}.valueOf() is not a primitive, so toString fallback used, String(1) + String({}), '1' + '[object Object]', '1[object Object]'
2 - {} // NaN {}.valueOf() is not a primitive, so toString fallback used => 2 - Number('[object Object]'), NaN
+'a' // NaN `ToPrimitive` passed 'number' hint), Number('a'), NaN
+'' // 0 `ToPrimitive` passed 'number' hint), Number(''), 0
+'-1' // -1 `ToPrimitive` passed 'number' hint), Number('-1'), -1
+{} // NaN `ToPrimitive` passed 'number' hint', `valueOf` returns an object, so falls back to `toString`, Number('[Object object]'), NaN
1 + 'a' // '1a' Both are primitives, one is a string, String(1) + 'a'
1 + {} // '1[object Object]' One primitive, one object, `ToPrimitive` passed no hint, meaning conversion to string will occur, one of the operands is now a string, String(1) + String({}), `1[object Object]`
[] + [] // '' Two objects, `ToPrimitive` passed no hint, String([]) + String([]), '' (empty string)
1 - 'a' // NaN Both are primitives, one is a string, `ToPrimitive` passed 'number' hint, 1-Number('a'), 1-NaN, NaN
1 - {} // NaN One primitive, one is an object, `ToPrimitive` passed 'number' hint, `valueOf` returns object, so falls back to `toString`, 1-Number([object Object]), 1-NaN, NaN
[] - [] // 0 Two objects, `ToPrimitive` passed 'number' hint => `valueOf` returns array instance, so falls back to `toString`, Number('')-Number(''), 0-0, 0
โปรดสังเกตว่าDateวัตถุที่อยู่ภายในนั้นไม่ซ้ำกันเนื่องจากเป็นวัตถุภายในเท่านั้นที่จะแทนที่@@toPrimitiveเมธอดเริ่มต้นซึ่งคำใบ้เริ่มต้นสันนิษฐานว่าเป็น 'สตริง' (แทนที่จะเป็น 'ตัวเลข') เหตุผลในการมีสิ่งนี้คือการให้Dateอินสแตนซ์แปลเป็นสตริงที่อ่านได้ตามค่าเริ่มต้นแทนที่จะเป็นค่าตัวเลขเพื่อความสะดวกของโปรแกรมเมอร์ คุณสามารถแทนที่@@toPrimitiveในวัตถุของคุณเองโดยใช้Symbol.toPrimitive.
ตารางต่อไปนี้แสดงผลการบีบบังคับสำหรับตัวดำเนินการความเท่าเทียมเชิงนามธรรม ( ==) (ที่มา ):

ดูยัง
window.console.log (x);หรือยังalert (x);?