คุณสมบัติหนึ่งของฟังก์ชัน Array.prototype.sort() คือเป็นอัลกอริทึมการเรียงลำดับแบบ in-place (ในที่เดิม) หมายความว่ามันไม่สร้างสำเนาใหม่ของอาร์เรย์ที่จะถูกเรียง มันเรียงอาร์เรย์โดยไม่ใช้พื้นที่เพิ่มเติม ทำให้มีประสิทธิภาพและประสิทธิภาพการทำงานที่ดีกว่า แต่คุณสมบัตินี้บางครั้งนำไปสู่สถานการณ์ที่ผิดปกติ
มาทำความเข้าใจด้วยตัวอย่าง สมมติว่าเรามีอาร์เรย์ names ที่มีสตริงลิเทอรัล เราต้องการรักษาลำดับของอาร์เรย์นี้ไว้ และต้องการอีกอาร์เรย์หนึ่งที่มีสมาชิกเหมือนกันแต่เรียงตามลำดับอักษร
เราอาจทำแบบนี้
const names = ['Rakesh', 'Mukesh', 'Ram', 'Anshul', 'Dheeraj']; let sortedNames = names; sortedNames = sortedNames.sort(); console.log(names); console.log(sortedNames);
แต่ใน JavaScript อาร์เรย์ก็เป็นอ็อบเจกต์ และอ็อบเจกต์ถูกคัดลอกโดยอ้างอิง (reference) ไม่ใช่โดยค่า (value) ดังนั้นการเรียงอาร์เรย์หนึ่งจึงทำให้อาร์เรย์ทั้งสองถูกเรียง ซึ่งเราไม่ต้องการ
วิธีแก้ไข
1. ใช้ slice() ขณะสร้างอาร์เรย์ใหม่
ตัวอย่าง
const names = ['Rakesh', 'Mukesh', 'Ram', 'Anshul', 'Dheeraj']; let sortedNames = names.slice(); sortedNames = sortedNames.sort(); console.log(names); console.log(sortedNames);
เมธอด slice() จริงๆ แล้วคืนค่าสำเนาลึกปาน (shallow copy) ที่คัดลอกเข้าไปในอาร์เรย์ใหม่ หากไม่ระบุอาร์กิวเมนต์ จะคัดลอกตั้งแต่ต้นจนจบ
แม้วิธีนี้จะไม่ได้มีประสิทธิภาพมากนัก เนื่องจากต้องสร้างอาร์เรย์ใหม่ และใช้ได้ดีเฉพาะกับอาร์เรย์ของสตริง/ตัวเลขลิเทอรัล วิธีที่สองมีประสิทธิภาพมากกว่าและใช้ได้กับอาร์เรย์ของอ็อบเจกต์ด้วย
2. ใช้ JSON.stringify() / JSON.parse()
ตัวอย่าง
const names = ['Rakesh', 'Mukesh', 'Ram', 'Anshul', 'Dheeraj']; let sortedNames = JSON.parse(JSON.stringify(names)); sortedNames = sortedNames.sort(); console.log(names); console.log(sortedNames);
การแปลงอาร์เรย์เป็นสตริง JSON และกลับมาอีกครั้ง บังคับให้คอมไพเลอร์ไม่คัดลอกโดยอ้างอิง
ผลลัพธ์ของทั้งสองวิธีจะเหมือนกันในคอนโซล
ผลลัพธ์
[ 'Rakesh', 'Mukesh', 'Ram', 'Anshul', 'Dheeraj' ] [ 'Anshul', 'Dheeraj', 'Mukesh', 'Rakesh', 'Ram' ]