บทความนี้มีไว้เพื่อทำความเข้าใจสคริปต์เก่า
ข้อมูลในบทความนี้มีประโยชน์สำหรับการทำความเข้าใจสคริปต์เก่า
นั่นไม่ใช่วิธีที่เราเขียนโค้ดใหม่
ในบทแรกเกี่ยวกับตัวแปร เราได้กล่าวถึงการประกาศตัวแปรสามวิธี:
let
const
var
การประกาศ var
คล้ายกับ let
โดยส่วนใหญ่แล้วเราสามารถแทนที่ let
ด้วย var
หรือในทางกลับกัน และคาดหวังว่าสิ่งต่างๆ จะทำงาน:
ข้อความ var = "สวัสดี"; การแจ้งเตือน(ข้อความ); // สวัสดี
แต่ภายใน var
นั้นเป็นสัตว์ร้ายที่แตกต่างออกไปมากซึ่งมีต้นกำเนิดมาจากสมัยก่อน โดยทั่วไปจะไม่ใช้ในสคริปต์สมัยใหม่ แต่ยังคงแฝงอยู่ในสคริปต์เก่า
หากคุณไม่ได้วางแผนที่จะพบกับสคริปต์ดังกล่าว คุณอาจข้ามบทนี้หรือเลื่อนออกไปก็ได้
ในทางกลับกัน สิ่งสำคัญคือต้องเข้าใจความแตกต่างเมื่อย้ายสคริปต์เก่าจาก var
ไปยัง let
เพื่อหลีกเลี่ยงข้อผิดพลาดแปลกๆ
ตัวแปรที่ประกาศด้วย var
มีทั้งแบบกำหนดขอบเขตฟังก์ชันหรือกำหนดขอบเขตทั่วโลก มองเห็นได้ผ่านบล็อก
ตัวอย่างเช่น:
ถ้า (จริง) { การทดสอบ var = จริง; // ใช้ "var" แทน "let" - การแจ้งเตือน (ทดสอบ); // จริง ตัวแปรจะคงอยู่หลังจาก if
เนื่องจาก var
ละเว้นการบล็อกโค้ด เราจึงมี test
ตัวแปรโกลบอล
หากเราใช้ let test
แทน var test
ตัวแปรจะมองเห็นได้ภายใน if
:
ถ้า (จริง) { ให้ทดสอบ = จริง; // ใช้ "ให้" - การแจ้งเตือน (ทดสอบ); // ReferenceError: ไม่ได้กำหนดการทดสอบ
สิ่งเดียวกันสำหรับลูป: var
ไม่สามารถเป็น block- หรือ loop-local ได้:
สำหรับ (var i = 0; i < 10; i++) { วาร์หนึ่ง = 1; - - การแจ้งเตือน (ฉัน); // 10, "i" มองเห็นได้หลังลูป ซึ่งเป็นตัวแปรโกลบอล การแจ้งเตือน (หนึ่ง); // 1, "หนึ่ง" มองเห็นได้หลังลูป ซึ่งเป็นตัวแปรโกลบอล
หากบล็อกโค้ดอยู่ภายในฟังก์ชัน var
จะกลายเป็นตัวแปรระดับฟังก์ชัน:
ฟังก์ชั่น sayHi() { ถ้า (จริง) { var วลี = "สวัสดี"; - การแจ้งเตือน(วลี); //ทำงาน - พูดว่าสวัสดี(); การแจ้งเตือน(วลี); // ReferenceError: ไม่ได้กำหนดวลี
ดังที่เราเห็น var
ทะลุผ่าน if
, for
หรือบล็อกโค้ดอื่นๆ นั่นเป็นเพราะว่าเมื่อนานมาแล้วใน JavaScript บล็อกไม่มีสภาพแวดล้อมของคำศัพท์ และ var
ก็เป็นส่วนที่เหลืออยู่ของสิ่งนั้น
หากเราประกาศตัวแปรเดียวกันกับ let
สองครั้งในขอบเขตเดียวกัน นั่นถือเป็นข้อผิดพลาด:
ให้ผู้ใช้; ให้ผู้ใช้; // SyntaxError: 'ผู้ใช้' ได้รับการประกาศแล้ว
ด้วย var
เราสามารถประกาศตัวแปรใหม่กี่ครั้งก็ได้ หากเราใช้ var
กับตัวแปรที่ประกาศไว้แล้ว มันก็จะถูกละเว้น:
ผู้ใช้ var = "พีท"; ผู้ใช้ var = "จอห์น"; // "var" นี้ไม่ได้ทำอะไรเลย (ประกาศแล้ว) // ...มันไม่ทำให้เกิดข้อผิดพลาด การแจ้งเตือน (ผู้ใช้); // จอห์น
การประกาศ var
จะถูกประมวลผลเมื่อฟังก์ชันเริ่มทำงาน (หรือสคริปต์เริ่มทำงานสำหรับ globals)
กล่าวอีกนัยหนึ่ง ตัวแปร var
ถูกกำหนดไว้ตั้งแต่จุดเริ่มต้นของฟังก์ชัน ไม่ว่าคำจำกัดความจะอยู่ที่ใด (สมมติว่าคำจำกัดความไม่อยู่ในฟังก์ชันที่ซ้อนกัน)
ดังนั้นรหัสนี้:
ฟังก์ชั่น sayHi() { วลี = "สวัสดี"; การแจ้งเตือน(วลี); วลี var; - พูดว่าสวัสดี();
…ในทางเทคนิคแล้วเหมือนกับสิ่งนี้ (ย้าย var phrase
ด้านบน):
ฟังก์ชั่น sayHi() { วลี var; วลี = "สวัสดี"; การแจ้งเตือน(วลี); - พูดว่าสวัสดี();
…หรือแม้กระทั่งเช่นนี้ (จำไว้ว่า บล็อกโค้ดจะถูกละเว้น):
ฟังก์ชั่น sayHi() { วลี = "สวัสดี"; - ถ้า (เท็จ) { วลี var; - การแจ้งเตือน(วลี); - พูดว่าสวัสดี();
ผู้คนยังเรียกพฤติกรรมดังกล่าวว่า "การยก" (การยก) เนื่องจาก var
ทั้งหมดถูก "ยก" (ยกขึ้น) ไปที่ด้านบนของฟังก์ชัน
ดังนั้นในตัวอย่างข้างต้น if (false)
ไม่เคยดำเนินการ แต่นั่นไม่สำคัญ var
ที่อยู่ข้างในจะถูกประมวลผลในช่วงเริ่มต้นของฟังก์ชัน ดังนั้น ณ ช่วงเวลาของ (*)
ตัวแปรจึงมีอยู่
ประกาศถูกยกขึ้น แต่การมอบหมายงานไม่ได้เป็นเช่นนั้น
แสดงให้เห็นได้ดีที่สุดด้วยตัวอย่าง:
ฟังก์ชั่น sayHi() { การแจ้งเตือน(วลี); var วลี = "สวัสดี"; - พูดว่าสวัสดี();
บรรทัด var phrase = "Hello"
มีสองการกระทำในนั้น:
การประกาศตัวแปร var
การกำหนดตัวแปร =
.
การประกาศจะถูกประมวลผลเมื่อเริ่มต้นการทำงานของฟังก์ชัน (“ยกขึ้น”) แต่การมอบหมายจะทำงานในตำแหน่งที่ปรากฏขึ้นเสมอ ดังนั้นโค้ดจึงทำงานในลักษณะนี้:
ฟังก์ชั่น sayHi() { วลี var; // การประกาศใช้งานได้ตั้งแต่เริ่มต้น... การแจ้งเตือน(วลี); // ไม่ได้กำหนด วลี = "สวัสดี"; // ...การมอบหมาย - เมื่อการดำเนินการถึงจุดนั้น - พูดว่าสวัสดี();
เนื่องจากการประกาศ var
ทั้งหมดได้รับการประมวลผลเมื่อเริ่มต้นฟังก์ชัน เราจึงสามารถอ้างอิงได้ทุกที่ แต่ตัวแปรไม่ได้ถูกกำหนดไว้จนกว่าจะได้รับมอบหมาย
ในทั้งสองตัวอย่างข้างต้น alert
จะทำงานโดยไม่มีข้อผิดพลาด เนื่องจากมี phrase
ตัวแปรอยู่ แต่ค่าของมันยังไม่ได้ถูกกำหนด ดังนั้นจึงแสดงเป็น undefined
ในอดีต เนื่องจากมีเพียง var
และไม่มีการมองเห็นระดับบล็อก โปรแกรมเมอร์จึงคิดค้นวิธีที่จะเลียนแบบมัน สิ่งที่พวกเขาทำเรียกว่า "นิพจน์ฟังก์ชันที่เรียกใช้ทันที" (ตัวย่อว่า IIFE)
นั่นไม่ใช่สิ่งที่เราควรใช้ในปัจจุบัน แต่คุณสามารถค้นหาได้ในสคริปต์เก่า
IIFE มีลักษณะดังนี้:
(การทำงาน() { ข้อความ var = "สวัสดี"; การแจ้งเตือน(ข้อความ); // สวัสดี -
ที่นี่ Function Expression จะถูกสร้างขึ้นและเรียกใช้ทันที ดังนั้นโค้ดจะทำงานทันทีและมีตัวแปรส่วนตัวของตัวเอง
Function Expression อยู่ในวงเล็บ (function {...})
เนื่องจากเมื่อ JavaScript engine พบ "function"
ในโค้ดหลัก ก็จะเข้าใจว่าเป็นจุดเริ่มต้นของการประกาศฟังก์ชัน แต่การประกาศฟังก์ชันต้องมีชื่อ ดังนั้นโค้ดประเภทนี้จะทำให้เกิดข้อผิดพลาด:
// พยายามประกาศและเรียกใช้ฟังก์ชันทันที function() { // <-- SyntaxError: คำสั่งฟังก์ชันจำเป็นต้องมีชื่อฟังก์ชัน ข้อความ var = "สวัสดี"; การแจ้งเตือน(ข้อความ); // สวัสดี -
แม้ว่าเราจะพูดว่า: "เอาล่ะ มาเพิ่มชื่อกันเถอะ" แต่นั่นจะไม่ทำงาน เนื่องจาก JavaScript ไม่อนุญาตให้เรียกใช้การประกาศฟังก์ชันทันที:
// ข้อผิดพลาดทางไวยากรณ์เนื่องจากวงเล็บด้านล่าง ฟังก์ชั่นไป () { - // <-- ไม่สามารถเรียกใช้ Function Declaration ได้ทันที
ดังนั้น วงเล็บรอบฟังก์ชันจึงเป็นกลลวงในการแสดง JavaScript ว่าฟังก์ชันนั้นถูกสร้างขึ้นในบริบทของนิพจน์อื่น และด้วยเหตุนี้จึงเป็นนิพจน์ฟังก์ชัน: ไม่จำเป็นต้องตั้งชื่อและสามารถเรียกได้ทันที
มีวิธีอื่นนอกเหนือจากวงเล็บในการบอก JavaScript ว่าเราหมายถึง Function Expression:
// วิธีสร้าง IIFE (การทำงาน() { alert("วงเล็บรอบฟังก์ชัน"); - (การทำงาน() { alert("วงเล็บล้อมรอบสิ่งทั้งหมด"); - !การทำงาน() { alert("ตัวดำเนินการ Bitwise NOT เริ่มนิพจน์"); - +ฟังก์ชั่น() { alert("Unary plus เริ่มนิพจน์"); -
ในกรณีทั้งหมดข้างต้น เราจะประกาศ Function Expression และเรียกใช้งานทันที โปรดทราบอีกครั้ง: ทุกวันนี้ไม่มีเหตุผลที่จะเขียนโค้ดดังกล่าว
มีความแตกต่างหลักสองประการของ var
เมื่อเปรียบเทียบกับ let/const
:
ตัวแปร var
ไม่มีขอบเขตของบล็อก การมองเห็นของตัวแปรจะกำหนดขอบเขตไว้ที่ฟังก์ชันปัจจุบันหรือส่วนกลาง หากมีการประกาศไว้ภายนอกฟังก์ชัน
การประกาศ var
จะถูกประมวลผลเมื่อเริ่มต้นฟังก์ชัน (การเริ่มสคริปต์สำหรับ globals)
มีความแตกต่างเล็กน้อยอีกประการหนึ่งที่เกี่ยวข้องกับออบเจ็กต์โกลบอล ซึ่งเราจะกล่าวถึงในบทถัดไป
ความแตกต่างเหล่านี้ทำให้ var
เลวร้ายยิ่งกว่า let
เวลาส่วนใหญ่ ตัวแปรระดับบล็อกเป็นสิ่งที่ยอดเยี่ยมมาก นั่นเป็นเหตุผลว่าทำไม let
ถูกนำมาใช้ในมาตรฐานเมื่อนานมาแล้ว และตอนนี้เป็นวิธีหลัก (พร้อมกับ const
) ในการประกาศตัวแปร