หนึ่งในคำศัพท์ที่ใหญ่ที่สุดในภาษา JavaScript คือการปิด เป็นเรื่องของคำถามสัมภาษณ์งานที่บริษัท FAANG มากมาย ในบทความนี้ เราจะพูดถึงการปิดและขอบเขต แสดงแนวคิดด้วยตัวอย่างง่ายๆ แล้วปิดท้ายด้วยคำถามตัวอย่างจากการสัมภาษณ์หนึ่งในยักษ์ใหญ่ด้านเทคโนโลยีที่ใหญ่กว่า
ขอบเขต
เมื่อมีคนบอกคุณว่ามีบางอย่างอยู่ในขอบเขตของโครงการหรือไม่ นั่นหมายความว่าอย่างไร
ฉันต้องการนึกถึงกล้องปริทรรศน์หรือกล้องโทรทรรศน์เมื่อคิดถึงคำตอบนี้ เครื่องมือเหล่านี้แสดงให้เราเห็นทุกสิ่งภายในขอบเขตของเลนส์ที่มีอยู่:อยู่ในขอบเขต . หากอยู่นอกขอบเขต คุณไม่สามารถมองผ่านเส้นผ่านศูนย์กลางของเลนส์ได้ และไม่สามารถฉายแสงบนวัตถุที่อยู่นอกเส้นผ่านศูนย์กลางได้ คุณควรคิดถึงเรื่องนี้ในขณะที่เราพูดถึงขอบเขตที่สำคัญและแตกต่างกันสามประเภทใน JavaScript:โลคัล โกลบอล และศัพท์
ขอบเขตท้องถิ่น
ขอบเขตท้องถิ่นคือขอบเขตที่เล็กที่สุดในสามขอบเขตที่เราจะพูดถึงในวันนี้ เมื่อเราประกาศฟังก์ชัน สิ่งใดก็ตามในวงเล็บ ({}) จะถือว่าเป็นฟังก์ชันภายในเครื่อง เมื่อเอ็นจิ้น JavaScript อ่านฟังก์ชัน จะประกาศตัวแปร เมื่อมันจบลง มันจะทำลายตัวแปร
function greeting() { var websiteName = 'Career Karma'; return `Hello ${websiteName}`; } console.log(greeting()); // Hello Career Karma console.log(websiteName); // ReferenceError: websiteName is not defined
อย่างที่คุณเห็น เมื่อเรา "console.log()" ผลลัพธ์ของฟังก์ชันทักทายที่เรียก เราสามารถเข้าถึงชื่อเว็บไซต์ได้หลังจากดำเนินการฟังก์ชันแล้ว สิ่งนี้ทำให้เรามีสตริง 'Hello Career Karma' ที่เรากำลังมองหา console.log()
ของตัวแปรที่ถูกประกาศภายในฟังก์ชันทำให้เกิดข้อผิดพลาดเนื่องจากไม่ได้กำหนดไว้
ดังที่กล่าวไปแล้ว สาเหตุที่ชื่อเว็บไซต์ไม่ได้กำหนดไว้นั้นเป็นเพราะว่าตัวแปรถูกสร้างขึ้นภายในฟังก์ชันเมื่อมีการเรียกใช้งานและถูกทำลายเมื่อคำสั่งเทอร์มินัลทำงาน สิ่งใดก็ตามที่อยู่นอกฟังก์ชันจะไม่สามารถเข้าถึงสิ่งต่างๆ ภายในฟังก์ชันได้ เว้นแต่จะมีการตั้งค่าพิเศษ
ขอบเขตสากล
ขอบเขตถัดไปนี้เป็นการแปลตามตัวอักษรของวลี ขอบเขตสากลนำรายการที่ประกาศไว้นอกฟังก์ชันและสำรองไว้ในพื้นที่ที่สคริปต์และเมธอดและฟังก์ชันทั้งหมดสามารถเข้าถึงและใช้สำหรับตรรกะ
let counter = 0; // global -- declared outside function const add = () => { // function declaration let counter = 0; // local -- declared inside function counter += 1; // counter increased by 1 -- which counter variable increased? return counter; } add(); // invoke add(); // three add(); // times console.log(counter) // is this 3 or 0? Why?
โค้ดด้านบนทำอะไรถ้าเรา console.log()
เคาน์เตอร์ท้ายโค้ด? คุณคาดหวังว่าจะเกิดอะไรขึ้น?
81% ของผู้เข้าร่วมกล่าวว่าพวกเขารู้สึกมั่นใจมากขึ้นเกี่ยวกับโอกาสในการทำงานด้านเทคโนโลยีหลังจากเข้าร่วม bootcamp จับคู่กับ Bootcamp วันนี้
ผู้สำเร็จการศึกษาจากหลักสูตร bootcamp โดยเฉลี่ยใช้เวลาน้อยกว่าหกเดือนในการเปลี่ยนอาชีพ ตั้งแต่เริ่มต้น bootcamp ไปจนถึงหางานแรก
มาดูรหัสกัน:
- ตัวแปรตัวนับที่ประกาศและเริ่มต้นในสภาพแวดล้อมส่วนกลาง
- เพิ่มฟังก์ชันที่ประกาศในสภาพแวดล้อมส่วนกลาง
- เรียกเพิ่ม
- ตัวแปรตัวนับที่ประกาศและเริ่มต้นในสภาพแวดล้อมท้องถิ่น
- ตัวนับในพื้นที่เพิ่มขึ้น 1 ⇐ ทำไมในเครื่องถึงไม่ใช่ทั่วโลก?
- เคาน์เตอร์ถูกส่งกลับ ฟังก์ชันสิ้นสุดลง
- เรียกเพิ่มอีกครั้ง
- ทำตามขั้นตอนที่ 4 ถึง 6 อีกครั้ง
- ทำซ้ำขั้นตอนที่ 3 ถึง 6 อีกครั้ง
console.log(counter)
; ⇐ ได้อะไรกลับมาบ้าง?
เนื่องจากฟังก์ชันจะสิ้นสุดเมื่อตัวนับอยู่ที่ 1 ทุกครั้ง ตัวแปรตัวนับในพื้นที่ของเราจึงถูกประกาศใหม่และเริ่มต้นใหม่เป็น 0 ทุกครั้งที่ฟังก์ชันทำงาน ไม่ว่าจะเกิดอะไรขึ้น เคาน์เตอร์จะหยุดที่ 1 ในระดับท้องถิ่นเสมอ
หากฟังก์ชันพบตัวแปรภายในขอบเขต ฟังก์ชันจะไม่มองไปที่ขอบเขตส่วนกลางของตัวแปร ดังนั้นตัวแปรส่วนกลางจะไม่เปลี่ยนแปลง ดังนั้น console.log()
. ของเรา จะส่งออก 0 เนื่องจากตัวแปรที่กำหนดไว้ที่ใกล้เคียงที่สุดของเราภายในสภาพแวดล้อมของคำสั่งนั้นอยู่ในสภาพแวดล้อมส่วนกลาง
ขอบเขตคำศัพท์
ขอบเขตคำศัพท์เป็นหนึ่งในแนวคิดพื้นฐานที่สุดใน JavaScript เป็นแนวคิดที่ว่าการสร้างฟังก์ชันหรือตัวแปรจะสามารถเข้าถึงได้ในบางส่วนของโค้ด และจากนั้นจะไม่สามารถเข้าถึงส่วนอื่นๆ ของโค้ดได้ ทุกอย่างขึ้นอยู่กับการประกาศของตัวแปรและฟังก์ชันแต่ละตัว
ลองดูที่บล็อกของรหัสนี้:
const init = () => { // <== This is our outer function const var1 = 'Career'; // outer scope const second = () => { // <== This is our inner function const var2 = 'Karma'; // inner scope console.log(var1); // Career console.log(var2); // Karma return var1 + " " + var2; }; // console.log(var2); // undefined return second(); }; init();
ที่นี่เรามีชุดของฟังก์ชันที่ซ้อนกัน init()
ฟังก์ชั่นประกาศตัวแปรที่เรียกว่า var1 ประกาศฟังก์ชั่นที่เรียกว่าวินาทีและเรียกใช้ second()
.
เมื่อคอมไพเลอร์ส่งผ่านโค้ดนี้ในครั้งแรก จะพิจารณาอย่างสูงว่าเรามีอะไรบ้าง:
init()
ฟังก์ชัน- เรียกใช้
init()
ณ จุดนี้ เราไม่เห็นสิ่งอื่นใดในฟังก์ชัน init() เราเพิ่งรู้ว่ามีฟังก์ชันนี้อยู่ เมื่อเรียกใช้ init() func ของเรา คอมไพเลอร์จะพิจารณาระดับสูงอีกครั้งว่ามีอะไรอยู่ในฟังก์ชัน:
var1
second()
ฟังก์ชัน- เรียกใช้
second()
init()
ฟังก์ชั่นไม่รู้อะไรเลยเกี่ยวกับสิ่งที่เกิดขึ้นภายใน second()
บล็อก. มองเห็นได้เฉพาะสิ่งที่อยู่ในสภาพแวดล้อมของคำศัพท์ – สภาพโดยรอบ
แต่ละฟังก์ชันที่ซ้อนกันอยู่ในภาชนะขนาดเล็ก เช่น ชุดตุ๊กตาแม่ลูกดกของรัสเซีย (ดูด้านบนสุดของหน้า เช่น หากคุณไม่แน่ใจว่ามันคืออะไร) ตุ๊กตาเท่านั้นที่รู้เกี่ยวกับสิ่งที่เกิดขึ้นภายในภาชนะและสิ่งที่เกิดขึ้นแล้วหรือประกาศ/อ่านในผู้ปกครอง ตัวอย่างเช่น ตุ๊กตาที่ใหญ่ที่สุดรู้เพียงว่าตุ๊กตาตัวต่อไปในภาชนะนั้นมีอยู่จริง มันไม่รู้เกี่ยวกับตุ๊กตาตัวอื่นๆ ในชุด ว่ามีอะไรอยู่ในสภาพแวดล้อมของคำศัพท์ (สถานะของมัน) และสิ่งที่เกิดขึ้นแล้ว (ขอบเขตภายนอก)
โดยพื้นฐานแล้ว เรารู้สองสิ่ง:
- ขอบเขตภายนอกมองไม่เห็นขอบเขตภายใน
- ขอบเขตภายในสามารถเข้าถึงขอบเขตภายนอกได้
เนื่องจากการรับมือภายนอกไม่สามารถมองเห็นสิ่งที่เกิดขึ้นภายในขอบเขต เราสามารถพูดได้อย่างปลอดภัยว่านี่เป็นความสัมพันธ์แบบทางเดียว ภายในมองเห็นและใช้ตัวแปรจากขอบเขตภายนอกได้ แต่ภายนอกจะมองไม่เห็นภายใน สิ่งนี้เรียกว่า ขอบเขตคำศัพท์ .
ความสวยงามของการกำหนดขอบเขตคำศัพท์คือค่าของตัวแปรถูกกำหนดโดยตำแหน่งในโค้ด ฟังก์ชันจะค้นหาความหมายของตัวแปรภายในสภาพแวดล้อมโลคัลก่อน หากหาไม่พบ ฟังก์ชันจะย้ายไปยังฟังก์ชันที่กำหนดฟังก์ชันนั้น หากไม่พบที่นั่น มันจะย้ายสายไปยังฟังก์ชันที่กำหนดไว้ถัดไป
สิ่งนี้กลายเป็นแนวคิดที่สำคัญมากใน JavaScript ที่จะเกิดขึ้นครั้งแล้วครั้งเล่าเมื่อคุณเรียนรู้เพิ่มเติมเกี่ยวกับเฟรมเวิร์ก JavaScript และวิธีการทำงาน คุณสามารถผ่านลงมาจากด้านนอกได้ แต่คุณไม่สามารถผ่าน "ขึ้น" ไปอีกทางหนึ่งได้ สิ่งนี้สำคัญมากเมื่อเราไปถึงหัวข้อหลัก:ปิด .
ปิด
คำจำกัดความของการปิดนั้นคล้ายกับขอบเขตคำศัพท์มาก ข้อแตกต่างที่สำคัญระหว่างทั้งสองคือ การปิดเป็นฟังก์ชันลำดับที่สูงกว่า และการกำหนดขอบเขตคำศัพท์ไม่ใช่ ฟังก์ชันลำดับที่สูงกว่ามีลักษณะพื้นฐานหนึ่งอย่าง:จะคืนค่าฟังก์ชันหรือใช้ฟังก์ชันเป็นพารามิเตอร์
การปิดเป็นฟังก์ชันที่สามารถเข้าถึงขอบเขตคำศัพท์ได้ แม้ว่าฟังก์ชันนั้นจะถูกเรียกใช้ในภายหลัง
ทั้งขอบเขตการปิดและคำศัพท์มีตัวแปรของตัวเอง สามารถเข้าถึงตัวแปรและพารามิเตอร์ของฟังก์ชันหลัก และสามารถใช้ตัวแปรส่วนกลางได้ มาดูรหัสต่อไปนี้กัน:
function greeting() { //outer scope (parent function) const userName = "CrrKrma1952"; // parent variable function welcomeGreeting() { // inner function console.log("Hello, " + userName); // accesses parent var return "Hello, " + userName; // terminal statement } return welcomeGreeting; // returns a function (which makes it HOF) } // end of greeting() const greetUser = greeting(); // greetUser(); // Hello, CrrKrma1952
greeting()
มีฟังก์ชันอยู่แต่เรายังไม่ทราบเนื้อหาgreetUser
มีอยู่แต่ยังไม่รู้เนื้อหาgreetUser()
– เรียกใช้บรรทัดก่อนหน้า ซึ่งในทางกลับกัน จะเรียกใช้ฟังก์ชัน greeting()- ชื่อผู้ใช้ถูกประกาศ
welcomeGreeting()
มีอยู่แต่ยังไม่รู้เนื้อหา- ส่งคืนคำสั่งด้านล่าง
welcomeGreeting()
block ส่งคืนฟังก์ชันเดียวกันนั้นมาก console.log(‘Hello, ‘ + userName)
; console.log ของเราที่นี่สามารถเข้าถึงขอบเขตหลักเพื่อรับค่าของชื่อผู้ใช้- คำสั่งเทอร์มินัลที่สิ้นสุดฟังก์ชันและทำลายความหมายของตัวแปรภายในบล็อคโค้ด
ในโค้ดนี้ เรากำลังส่งข้อมูลโดยการซ้อนฟังก์ชันเข้าด้วยกัน เพื่อให้สามารถเข้าถึงขอบเขตหลักได้ในภายหลัง
บทสรุป
ในบทความนี้ เราได้พูดถึงหัวข้อ JavaScript ที่ค่อนข้างแข็งแกร่ง:ขอบเขตและการปิด ฉันอยากจะแนะนำให้แยกย่อยและอ่านบทความต่าง ๆ เกี่ยวกับเรื่องนี้ วิธีการสอนนี้สามารถมาจากมุมมองที่หลากหลาย ซึ่งหมายความว่ามีวิธีมากมายในการเรียนรู้ ฉันหวังว่าไพรเมอร์นี้จะเป็นประโยชน์กับคุณ! ขอให้โชคดีในการศึกษาต่อเกี่ยวกับการปิดทำการ!