ความรู้ขั้นสูง
ส่วนนี้เจาะลึกเข้าไปในสตริงภายใน ความรู้นี้จะเป็นประโยชน์สำหรับคุณหากคุณวางแผนที่จะจัดการกับอีโมจิ อักขระทางคณิตศาสตร์หรืออักษรอียิปต์โบราณที่หายาก หรือสัญลักษณ์ที่หายากอื่นๆ
ดังที่เราทราบแล้วว่าสตริง JavaScript เป็นไปตาม Unicode: อักขระแต่ละตัวจะแสดงด้วยลำดับไบต์ขนาด 1-4 ไบต์
JavaScript ช่วยให้เราสามารถแทรกอักขระลงในสตริงโดยการระบุรหัส Unicode เลขฐานสิบหกด้วยหนึ่งในสามสัญลักษณ์เหล่านี้:
xXX
XX
ต้องเป็นเลขฐานสิบหกสองหลักที่มีค่าระหว่าง 00
ถึง FF
ดังนั้น xXX
จะเป็นอักขระที่มีรหัส Unicode คือ XX
เนื่องจากรูปแบบ xXX
รองรับเลขฐานสิบหกเพียงสองหลัก จึงสามารถใช้ได้เฉพาะกับอักขระ Unicode 256 ตัวแรกเท่านั้น
อักขระ 256 ตัวแรกเหล่านี้ประกอบด้วยตัวอักษรละติน อักขระไวยากรณ์พื้นฐานส่วนใหญ่ และอื่นๆ บางส่วน ตัวอย่างเช่น "x7A"
เหมือนกับ "z"
(Unicode U+007A
)
alert( "x7A" ); // ซี alert( "xA9" ); // © สัญลักษณ์ลิขสิทธิ์
uXXXX
XXXX
ต้องเป็นเลขฐานสิบหก 4 หลักโดยมีค่าระหว่าง 0000
ถึง FFFF
ดังนั้น uXXXX
จะเป็นอักขระที่มีรหัส Unicode คือ XXXX
อักขระที่มีค่า Unicode มากกว่า U+FFFF
ยังสามารถแสดงด้วยสัญลักษณ์นี้ได้ แต่ในกรณีนี้ เราจำเป็นต้องใช้สิ่งที่เรียกว่าคู่ตัวแทน (เราจะพูดถึงคู่ตัวแทนในภายหลังในบทนี้)
การแจ้งเตือน( "u00A9" ); // © เหมือนกับ xA9 โดยใช้เครื่องหมายฐานสิบหก 4 หลัก การแจ้งเตือน( "u044F" ); // я ตัวอักษรซีริลลิก alert( "u2191" ); // ↑ สัญลักษณ์ลูกศรขึ้น
u{X…XXXXXX}
X…XXXXXX
ต้องเป็นค่าเลขฐานสิบหก 1 ถึง 6 ไบต์ระหว่าง 0
ถึง 10FFFF
(จุดโค้ดสูงสุดที่กำหนดโดย Unicode) สัญกรณ์นี้ช่วยให้เราสามารถแสดงอักขระ Unicode ที่มีอยู่ทั้งหมดได้อย่างง่ายดาย
alert( "u{20331}" ); // 佫 ตัวอักษรจีนหายาก (Unicode ขนาดยาว) การแจ้งเตือน( "u{1F60D}" ); // ? สัญลักษณ์หน้ายิ้ม (Unicode ยาวๆ อีกอัน)
อักขระที่ใช้บ่อยทั้งหมดมีรหัส 2 ไบต์ (เลขฐานสิบหก 4 หลัก) ตัวอักษรในภาษายุโรปส่วนใหญ่ ตัวเลข และชุดอุดมการณ์ CJK พื้นฐานที่เป็นหนึ่งเดียว (CJK – จากระบบการเขียนภาษาจีน ญี่ปุ่น และเกาหลี) มีขนาด 2 ไบต์
เริ่มแรก JavaScript ใช้การเข้ารหัส UTF-16 ซึ่งอนุญาตเพียง 2 ไบต์ต่ออักขระ แต่ 2 ไบต์อนุญาตเฉพาะชุดค่าผสม 65536 เท่านั้น และนั่นไม่เพียงพอสำหรับสัญลักษณ์ Unicode ทุกตัวที่เป็นไปได้
สัญลักษณ์หายากที่ต้องใช้มากกว่า 2 ไบต์จะถูกเข้ารหัสด้วยอักขระ 2 ไบต์คู่ที่เรียกว่า "คู่ตัวแทน"
ผลข้างเคียงคือความยาวของสัญลักษณ์ดังกล่าวคือ 2
:
alert( '?'.length ); // 2 สคริปต์ทางคณิตศาสตร์ตัวพิมพ์ใหญ่ X alert( '?'.length ); // 2 เผชิญน้ำตาแห่งความสุข alert( '?'.length ); // 2 ตัวอักษรจีนหายาก
นั่นเป็นเพราะว่าไม่มีคู่ตัวแทนในขณะที่สร้าง JavaScript และทำให้ภาษาประมวลผลไม่ถูกต้อง!
จริงๆ แล้ว เรามีสัญลักษณ์เดียวในแต่ละสตริงด้านบน แต่คุณสมบัติ length
แสดงความยาวเป็น 2
การได้รับสัญลักษณ์อาจเป็นเรื่องยาก เนื่องจากคุณลักษณะทางภาษาส่วนใหญ่จะถือว่าคู่ตัวแทนเสมือนเป็นอักขระสองตัว
ตัวอย่างเช่น ที่นี่เราจะเห็นอักขระแปลก ๆ สองตัวในเอาต์พุต:
การแจ้งเตือน( '?'[0] ); // แสดงสัญลักษณ์แปลกๆ... การแจ้งเตือน( '?'[1] ); // ...ชิ้นส่วนของคู่ตัวแทน
ชิ้นส่วนของคู่ตัวแทนไม่มีความหมายหากไม่มีกันและกัน ดังนั้นการแจ้งเตือนในตัวอย่างด้านบนจะแสดงขยะจริงๆ
ในทางเทคนิค คู่ตัวแทนยังสามารถตรวจจับได้ด้วยรหัส: หากอักขระมีรหัสในช่วง 0xd800..0xdbff
แสดงว่าเป็นส่วนแรกของคู่ตัวแทน อักขระถัดไป (ส่วนที่สอง) ต้องมีโค้ดอยู่ในช่วง 0xdc00..0xdfff
ช่วงเวลาเหล่านี้สงวนไว้เฉพาะสำหรับคู่ตัวแทนตามมาตรฐาน
ดังนั้นเมธอด String.fromCodePoint และ str.codePointAt จึงถูกเพิ่มใน JavaScript เพื่อจัดการกับคู่ตัวแทน
โดยพื้นฐานแล้วจะเหมือนกับ String.fromCharCode และ str.charCodeAt แต่ปฏิบัติต่อคู่ตัวแทนอย่างถูกต้อง
เราสามารถเห็นความแตกต่างได้ที่นี่:
// charCodeAt ไม่รู้จักคู่ตัวแทน ดังนั้นจึงให้โค้ดสำหรับส่วนที่ 1 ของ ?: alert( '?'.charCodeAt(0).toString(16) ); //d835 // codePointAt รับรู้ถึงคู่ตัวแทน alert( '?'.codePointAt(0).toString(16) ); // 1d4b3 อ่านทั้งสองส่วนของคู่ตัวแทน
ดังที่กล่าวไว้ว่า หากเราดึงจากตำแหน่งที่ 1 (และค่อนข้างไม่ถูกต้องในที่นี้) ทั้งคู่ก็จะส่งคืนเฉพาะส่วนที่ 2 ของคู่เท่านั้น:
alert( '?'.charCodeAt(1).toString(16) ); //dcb3 alert( '?'.codePointAt(1).toString(16) ); //dcb3 //ครึ่งหลังไร้ความหมาย
คุณจะพบวิธีเพิ่มเติมในการจัดการกับคู่ตัวแทนในภายหลังในบท Iterables อาจมีห้องสมุดพิเศษเกี่ยวกับเรื่องนั้นด้วย แต่ไม่มีอะไรมีชื่อเสียงมากพอที่จะแนะนำที่นี่
ประเด็นสำคัญ: การแยกสายโดยจุดใดจุดหนึ่งเป็นสิ่งที่อันตราย
เราไม่สามารถแยกสตริงในตำแหน่งที่ต้องการได้ เช่น ใช้ str.slice(0, 4)
และคาดหวังว่ามันจะเป็นสตริงที่ถูกต้อง เช่น:
การแจ้งเตือน ( 'สวัสดี ?'.slice(0, 4) ); // สวัสดี [?]
ที่นี่เราจะเห็นอักขระขยะ (ครึ่งแรกของคู่ตัวแทนยิ้ม) ในเอาต์พุต
เพียงแค่ระวังไว้หากคุณตั้งใจที่จะทำงานร่วมกับคู่ตัวแทนอย่างน่าเชื่อถือ อาจไม่ใช่ปัญหาใหญ่แต่อย่างน้อยคุณควรเข้าใจว่าจะเกิดอะไรขึ้น
ในหลายภาษา มีสัญลักษณ์ที่ประกอบด้วยอักขระฐานโดยมีเครื่องหมายอยู่ด้านบน/ด้านล่าง
ตัวอย่างเช่น ตัวอักษร a
อาจเป็นอักขระฐานสำหรับอักขระเหล่านี้: àáâäãåā
อักขระ "คอมโพสิต" ที่พบบ่อยที่สุดจะมีรหัสของตัวเองในตาราง Unicode แต่ไม่ใช่ทั้งหมด เนื่องจากมีชุดค่าผสมที่เป็นไปได้มากเกินไป
เพื่อรองรับการเรียบเรียงตามอำเภอใจ มาตรฐาน Unicode อนุญาตให้เราใช้อักขระ Unicode หลายตัว: อักขระฐานตามด้วยอักขระ "mark" หนึ่งตัวหรือหลายตัวที่ "ตกแต่ง"
ตัวอย่างเช่น หากเรามี S
ตามด้วยอักขระพิเศษ "จุดเหนือ" (รหัส u0307
) ก็จะแสดงเป็น Ṡ
alert( 'Su0307' ); // Ṡ
หากเราต้องการเครื่องหมายเพิ่มเติมเหนือตัวอักษร (หรือด้านล่าง) ไม่มีปัญหา เพียงเพิ่มอักขระเครื่องหมายที่จำเป็น
ตัวอย่างเช่น หากเราเติมอักขระ “dot below” (รหัส u0323
) ต่อท้าย เราจะได้ “S ที่มีจุดด้านบนและด้านล่าง”: Ṩ
.
ตัวอย่างเช่น:
alert( 'Su0307u0323' ); // Ṩ
สิ่งนี้ให้ความยืดหยุ่นอย่างมาก แต่ก็เป็นปัญหาที่น่าสนใจเช่นกัน: อักขระสองตัวอาจดูเหมือนกัน แต่แสดงด้วยองค์ประกอบ Unicode ที่แตกต่างกัน
ตัวอย่างเช่น:
ให้ s1 = 'Su0307u0323'; // Ṩ, S + จุดที่ด้านบน + จุดที่ด้านล่าง ให้ s2 = 'Su0323u0307'; // Ṩ, S + จุดด้านล่าง + จุดด้านบน การแจ้งเตือน ( `s1: ${s1}, s2: ${s2}` ); การแจ้งเตือน( s1 == s2 ); // เท็จแม้ว่าตัวละครจะดูเหมือนกัน (?!)
เพื่อแก้ปัญหานี้ มีอัลกอริธึม "การทำให้เป็นมาตรฐานของ Unicode" ที่นำแต่ละสตริงมาอยู่ในรูปแบบ "ปกติ" เดียว
มันถูกนำไปใช้โดย str.normalize()
alert( "Su0307u0323".normalize() == "Su0323u0307".normalize() ); // จริง
มันตลกดีที่ในสถานการณ์ของเรา normalize()
รวบรวมลำดับของอักขระ 3 ตัวมารวมกันเป็นหนึ่งตัว: u1e68
(S ที่มีจุดสองจุด)
alert( "Su0307u0323".normalize().length ); // 1 alert( "Su0307u0323".normalize() == "u1e68" ); // จริง
ในความเป็นจริงมันไม่ได้เป็นเช่นนั้นเสมอไป เหตุผลก็คือสัญลักษณ์ Ṩ
นั้น “ธรรมดาพอ” ดังนั้นผู้สร้าง Unicode จึงรวมมันไว้ในตารางหลักและให้โค้ดแก่มัน
หากคุณต้องการเรียนรู้เพิ่มเติมเกี่ยวกับกฎและตัวแปรการทำให้เป็นมาตรฐาน - กฎเหล่านี้อธิบายไว้ในภาคผนวกของมาตรฐาน Unicode: แบบฟอร์มการทำให้เป็นมาตรฐานของ Unicode แต่สำหรับวัตถุประสงค์ในทางปฏิบัติส่วนใหญ่ ข้อมูลจากส่วนนี้ก็เพียงพอแล้ว