Scope เป็นหนึ่งในแนวคิดที่สำคัญที่สุดใน JavaScript หากคุณต้องการเรียนรู้ JavaScript ให้ดี คุณต้องเข้าใจว่า JavaScript scope และ scope chain ทำงานอย่างไร บทความวันนี้มีการแนะนำโดยย่อเกี่ยวกับขอบเขตและห่วงโซ่ขอบเขตของ JavaScript โดยหวังว่าจะช่วยให้ทุกคนเรียนรู้ JavaScript ได้ดีขึ้น
ขอบเขตจาวาสคริปต์
ภาษาการเขียนโปรแกรมใดๆ ก็มีแนวคิดเกี่ยวกับขอบเขต พูดง่ายๆ ก็คือขอบเขตคือช่วงของตัวแปรและฟังก์ชันที่สามารถเข้าถึงได้ ใน JavaScript มีขอบเขตตัวแปรสองประเภท: ขอบเขตส่วนกลางและขอบเขตท้องถิ่น
1. ขอบเขตทั่วโลก
วัตถุที่สามารถเข้าถึงได้ทุกที่ในโค้ดจะมีขอบเขตทั่วโลก โดยทั่วไปแล้ว สถานการณ์ต่อไปนี้จะมีขอบเขตทั่วโลก:
(1) ฟังก์ชันภายนอกสุดและตัวแปรที่กำหนดภายนอกฟังก์ชันภายนอกสุดมีขอบเขตรวม ตัวอย่างเช่น
คัดลอกรหัสรหัสดังต่อไปนี้:
varauthorName="กระแสภูเขา";
ฟังก์ชั่นทำบางสิ่งบางอย่าง () {
varblogName="";
functioninnerSay(){
การแจ้งเตือน (ชื่อบล็อก);
-
ภายในพูด();
-
alert(ชื่อผู้เขียน);//ลำห้วยไหล่เขา
alert(blogName);//ข้อผิดพลาดของสคริปต์
ทำอะไรสักอย่าง();//
innerSay()//ข้อผิดพลาดของสคริปต์
(2) ตัวแปรทั้งหมดที่ไม่ได้กำหนดและกำหนดโดยตรงจะถูกประกาศโดยอัตโนมัติให้มีขอบเขตทั่วโลก ตัวอย่างเช่น:
คัดลอกรหัสรหัสดังต่อไปนี้:
ฟังก์ชั่นทำบางสิ่งบางอย่าง () {
varauthorName="กระแสภูเขา";
ชื่อบล็อก = "";
การแจ้งเตือน(ชื่อผู้เขียน);
-
การแจ้งเตือน(ชื่อบล็อก);//
alert(authorName);//ข้อผิดพลาดของสคริปต์
ตัวแปร blogName มีขอบเขตส่วนกลาง แต่ไม่สามารถเข้าถึง authorName ภายนอกฟังก์ชันได้
(3) คุณสมบัติทั้งหมดของวัตถุหน้าต่างมีขอบเขตทั่วโลก
โดยทั่วไป คุณสมบัติบิวท์อินของวัตถุหน้าต่างจะมีขอบเขตส่วนกลาง เช่น window.name, window.location, window.top เป็นต้น
2. ขอบเขตท้องถิ่น
ตรงกันข้ามกับขอบเขตส่วนกลาง โดยทั่วไปขอบเขตท้องถิ่นจะเข้าถึงได้เฉพาะภายในส่วนโค้ดคงที่เท่านั้น โดยทั่วไปจะอยู่ภายในฟังก์ชัน ดังนั้นในบางที่ คุณจะเห็นผู้คนอ้างถึงขอบเขตนี้เป็นขอบเขตฟังก์ชัน เช่น โค้ดต่อไปนี้ ทั้งสอง ชื่อบล็อกและฟังก์ชัน innerSay ในมีเฉพาะขอบเขตท้องถิ่นเท่านั้น
คัดลอกรหัสรหัสดังต่อไปนี้:
ฟังก์ชั่นทำบางสิ่งบางอย่าง () {
varblogName="";
functioninnerSay(){
การแจ้งเตือน (ชื่อบล็อก);
-
ภายในพูด();
-
alert(blogName);//ข้อผิดพลาดของสคริปต์
innerSay();//ข้อผิดพลาดของสคริปต์
ขอบเขตห่วงโซ่
ใน JavaScript ฟังก์ชันก็เป็นวัตถุเช่นกัน จริงๆ แล้วทุกสิ่งใน JavaScript ก็เป็นวัตถุ ออบเจ็กต์ฟังก์ชัน เช่นเดียวกับออบเจ็กต์อื่นๆ มีคุณสมบัติที่สามารถเข้าถึงได้ผ่านโค้ดและชุดคุณสมบัติภายในที่สามารถเข้าถึงได้โดยกลไก JavaScript เท่านั้น คุณสมบัติภายในประการหนึ่งคือ [[ขอบเขต]] ซึ่งกำหนดโดยรุ่นที่สามมาตรฐานของ ECMA-262 ประกอบด้วยคอลเลกชันของออบเจ็กต์ในขอบเขตที่สร้างฟังก์ชันนี้ ซึ่งกำหนดว่าฟังก์ชันใดสามารถเข้าถึงข้อมูลใดได้
เมื่อฟังก์ชันถูกสร้างขึ้น สายขอบเขตของฟังก์ชันจะถูกเติมด้วยออบเจ็กต์ข้อมูลที่สามารถเข้าถึงได้จากขอบเขตที่สร้างฟังก์ชันนั้น ตัวอย่างเช่น กำหนดฟังก์ชันต่อไปนี้:
คัดลอกรหัสรหัสดังต่อไปนี้:
ฟังก์ชั่นเพิ่ม (num1,num2){
วาร์ซัม=num1+num2;
ผลตอบแทน;
-
เมื่อเพิ่มฟังก์ชันถูกสร้างขึ้น ขอบเขตขอบเขตจะเต็มไปด้วยอ็อบเจ็กต์โกลบอลซึ่งมีตัวแปรโกลบอลทั้งหมด ดังแสดงในรูปต่อไปนี้ (หมายเหตุ: รูปภาพแสดงเพียงส่วนหนึ่งของตัวแปรทั้งหมด):
ขอบเขตของการเพิ่มฟังก์ชันจะถูกใช้ระหว่างการดำเนินการ ตัวอย่างเช่น รันโค้ดต่อไปนี้:
คัดลอกรหัสรหัสดังต่อไปนี้:
ผลรวม var = เพิ่ม (5,10);
เมื่อฟังก์ชันนี้ถูกดำเนินการ ออบเจ็กต์ภายในที่เรียกว่า "บริบทการดำเนินการ" จะถูกสร้างขึ้น บริบทรันไทม์จะกำหนดสภาพแวดล้อมที่ฟังก์ชันถูกดำเนินการ บริบทรันไทม์แต่ละรายการมีสายโซ่ขอบเขตของตัวเองสำหรับการแก้ไขตัวระบุ เมื่อมีการสร้างบริบทรันไทม์ สายโซ่ขอบเขตจะเริ่มต้นกับออบเจ็กต์ที่อยู่ใน [[ขอบเขต]] ของฟังก์ชันที่กำลังทำงานอยู่ในปัจจุบัน
ค่าจะถูกคัดลอกลงในห่วงโซ่ขอบเขตของบริบทรันไทม์ตามลำดับที่ปรากฏในฟังก์ชัน พวกเขาร่วมกันสร้างวัตถุใหม่ที่เรียกว่า "วัตถุการเปิดใช้งาน" วัตถุนี้ประกอบด้วยตัวแปรท้องถิ่น พารามิเตอร์ที่มีชื่อ คอลเลกชันพารามิเตอร์ และฟังก์ชันนี้ จากนั้นวัตถุนี้จะถูกผลักไปที่ส่วนหน้าของห่วงโซ่ขอบเขต ถูกทำลาย วัตถุที่ใช้งานอยู่ก็ถูกทำลายด้วย ห่วงโซ่ขอบเขตใหม่แสดงอยู่ด้านล่าง:
ในระหว่างการทำงานของฟังก์ชัน หากไม่พบตัวแปร ตัวแปรจะต้องผ่านกระบวนการแก้ไขตัวระบุเพื่อกำหนดตำแหน่งที่จะรับและจัดเก็บข้อมูล กระบวนการนี้เริ่มต้นจากส่วนหัวของห่วงโซ่ขอบเขต นั่นคือ จากวัตถุที่ใช้งานอยู่ และค้นหาตัวระบุที่มีชื่อเดียวกัน หากพบ ให้ใช้ตัวแปรที่สอดคล้องกับตัวระบุนี้ หากไม่พบ ให้ดำเนินการต่อ ค้นหาวัตถุถัดไปในห่วงโซ่ขอบเขต หากไม่พบวัตถุหลังจากค้นหา ตัวระบุจะถือว่าไม่ได้กำหนด ในระหว่างการทำงานของฟังก์ชัน ตัวระบุแต่ละตัวจะผ่านกระบวนการค้นหาดังกล่าว
การผูกมัดขอบเขตและการเพิ่มประสิทธิภาพโค้ด
จะเห็นได้จากโครงสร้างของห่วงโซ่ขอบเขตว่า ยิ่งตัวระบุอยู่ในห่วงโซ่ขอบเขตของบริบทรันไทม์ลึกเพียงใด ความเร็วในการอ่านและการเขียนก็จะยิ่งช้าลงเท่านั้น ดังแสดงในรูปด้านบน เนื่องจากตัวแปรส่วนกลางจะมีอยู่ที่ส่วนท้ายของห่วงโซ่ขอบเขตบริบทรันไทม์เสมอ การค้นหาตัวแปรส่วนกลางจะช้าที่สุดในระหว่างการแก้ไขตัวระบุ ดังนั้นเมื่อเขียนโค้ด คุณควรใช้ตัวแปรส่วนกลางให้น้อยที่สุดเท่าที่จะเป็นไปได้ และใช้ตัวแปรท้องถิ่นให้มากที่สุด หลักการทั่วไปที่ดีคือ: หากมีการอ้างอิงออบเจ็กต์ข้ามขอบเขตมากกว่าหนึ่งครั้ง ให้จัดเก็บไว้ในตัวแปรท้องถิ่นก่อนใช้งาน ตัวอย่างเช่นรหัสต่อไปนี้:
คัดลอกรหัสรหัสดังต่อไปนี้:
ฟังก์ชั่นเปลี่ยนสี(){
document.getElementById("btnChange").onclick=function(){
document.getElementById("targetCanvas").style.พื้นหลังสี="สีแดง";
-
-
ฟังก์ชันนี้อ้างอิงเอกสารตัวแปรส่วนกลางสองครั้ง หากต้องการค้นหาตัวแปร คุณต้องข้ามห่วงโซ่ขอบเขตทั้งหมดจนกว่าจะพบในวัตถุส่วนกลางในที่สุด รหัสนี้สามารถเขียนใหม่ได้ดังนี้:
คัดลอกรหัสรหัสดังต่อไปนี้:
ฟังก์ชั่นเปลี่ยนสี(){
vardoc=เอกสาร;
doc.getElementById("btnChange").onclick=function(){
doc.getElementById("targetCanvas").style.พื้นหลังสี="สีแดง";
-
-
โค้ดนี้ค่อนข้างง่ายและจะไม่แสดงการปรับปรุงประสิทธิภาพอย่างมากหลังจากเขียนใหม่ อย่างไรก็ตาม หากมีตัวแปรส่วนกลางจำนวนมากในโปรแกรมที่มีการเข้าถึงซ้ำๆ ประสิทธิภาพของโค้ดที่เขียนใหม่จะได้รับการปรับปรุงอย่างมาก
เปลี่ยนห่วงโซ่ขอบเขต
บริบทรันไทม์ที่สอดคล้องกันจะไม่ซ้ำกันในแต่ละครั้งที่มีการดำเนินการฟังก์ชัน ดังนั้นการเรียกใช้ฟังก์ชันเดียวกันหลายครั้งจะส่งผลให้มีการสร้างบริบทรันไทม์หลายรายการ เมื่อฟังก์ชันดำเนินการเสร็จสิ้น บริบทการดำเนินการจะถูกทำลาย บริบทรันไทม์แต่ละรายการเชื่อมโยงกับห่วงโซ่ขอบเขต ภายใต้สถานการณ์ปกติ ในระหว่างการดำเนินการบริบทรันไทม์ ห่วงโซ่ขอบเขตจะได้รับผลกระทบจากคำสั่ง with และคำสั่ง catch เท่านั้น
คำสั่ง with เป็นวิธีลัดในการใช้อ็อบเจ็กต์เพื่อหลีกเลี่ยงการเขียนโค้ดซ้ำ ตัวอย่างเช่น:
คัดลอกรหัสรหัสดังต่อไปนี้:
functioninitUI(){
ด้วย(เอกสาร){
varbd=ร่างกาย,
ลิงค์=getElementsByTagName("a"),
ฉัน=0,
len=links.ความยาว;
ในขณะที่ (ฉัน <เลน) {
อัปเดต (ลิงก์ [i++]);
-
getElementById("btnInit").onclick=function(){
ทำอะไรสักอย่าง();
-
-
-
คำสั่งความกว้างถูกใช้ที่นี่เพื่อหลีกเลี่ยงการเขียนเอกสารหลายครั้ง ซึ่งดูเหมือนมีประสิทธิภาพมากกว่า แต่จริงๆ แล้วทำให้เกิดปัญหาด้านประสิทธิภาพ
เมื่อโค้ดไปถึงคำสั่ง with ขอบเขตขอบเขตของบริบทรันไทม์จะเปลี่ยนไปชั่วคราว ออบเจ็กต์ที่ไม่แน่นอนใหม่จะถูกสร้างขึ้นที่มีคุณสมบัติทั้งหมดของออบเจ็กต์ที่ระบุโดยพารามิเตอร์ ออบเจ็กต์นี้จะถูกผลักไปที่ส่วนหัวของลูกโซ่ขอบเขต ซึ่งหมายความว่าขณะนี้ตัวแปรภายในเครื่องทั้งหมดของฟังก์ชันอยู่ในออบเจ็กต์ลูกโซ่ขอบเขตที่สอง และดังนั้นจึงมีราคาแพงกว่าในการเข้าถึง ดังที่แสดงด้านล่าง:
ดังนั้น คุณควรหลีกเลี่ยงการใช้คำสั่ง with ในโปรแกรมของคุณ ในตัวอย่างนี้ การจัดเก็บเอกสารในตัวแปรโลคัลสามารถปรับปรุงประสิทธิภาพได้
อีกสิ่งหนึ่งที่เปลี่ยนห่วงโซ่ขอบเขตคือคำสั่ง catch ในคำสั่ง try-catch เมื่อมีข้อผิดพลาดเกิดขึ้นในบล็อกโค้ดลอง กระบวนการดำเนินการจะข้ามไปที่คำสั่ง catch จากนั้นวัตถุข้อยกเว้นจะถูกผลักเข้าไปในวัตถุที่ไม่แน่นอนและวางไว้ที่ส่วนหัวของขอบเขต ภายใน catch block ตัวแปรท้องถิ่นทั้งหมดของฟังก์ชันจะถูกวางไว้ในอ็อบเจ็กต์ลูกโซ่ขอบเขตที่สอง รหัสตัวอย่าง:
คัดลอกรหัสรหัสดังต่อไปนี้:
พยายาม{
ทำอะไรสักอย่าง();
} จับ (เช่น) {
alert(ex.message);//ขอบเขตการเปลี่ยนแปลงที่นี่
-
โปรดทราบว่าเมื่อมีการดำเนินการคำสั่ง catch ขอบเขตขอบเขตจะกลับสู่สถานะก่อนหน้า คำสั่ง try-catch มีประโยชน์อย่างมากในการดีบักโค้ดและการจัดการข้อยกเว้น ดังนั้นจึงไม่แนะนำให้หลีกเลี่ยงโดยสิ้นเชิง คุณสามารถลดผลกระทบด้านประสิทธิภาพของคำสั่ง catch ได้โดยการเพิ่มประสิทธิภาพโค้ดของคุณ รูปแบบที่ดีคือการมอบหมายการจัดการข้อผิดพลาดให้กับฟังก์ชัน ตัวอย่างเช่น:
คัดลอกรหัสรหัสดังต่อไปนี้:
พยายาม{
ทำอะไรสักอย่าง();
} จับ (เช่น) {
handleError(ex);//มอบหมายให้กับวิธีการประมวลผล
-
ในโค้ดที่ปรับให้เหมาะสม เมธอด handleError เป็นโค้ดเดียวที่ดำเนินการในส่วนคำสั่ง catch ฟังก์ชันนี้รับออบเจ็กต์ข้อยกเว้นเป็นพารามิเตอร์ เพื่อให้คุณสามารถจัดการกับข้อผิดพลาดได้อย่างยืดหยุ่นและสม่ำเสมอมากขึ้น เนื่องจากมีการดำเนินการคำสั่งเดียวเท่านั้นและไม่มีการเข้าถึงตัวแปรในเครื่อง การเปลี่ยนแปลงชั่วคราวในห่วงโซ่ขอบเขตจะไม่ส่งผลกระทบต่อประสิทธิภาพของโค้ด