Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> JavaScript

Web Fundamentals: Scope และ JavaScript Closure - คู่มือสำหรับมือใหม่

Closure เป็นคำสำคัญที่สุดใน JavaScript เป็นคำถามสัมภาษณ์งานที่ FAANG companies บ่อยมาก บทความนี้จะอธิบาย closure และ scope พร้อมตัวอย่างง่ายๆ และคำถามสัมภาษณ์ตัวอย่าง

Scope (ขอบเขต)

เมื่อพูดว่า "ในขอบเขต" หรือ "นอกขอบเขต" ของโปรเจกต์ หมายถึงอะไร?

คิดถึงกล้องโทรทะเล (telescope) หรือกล้องสิบปืน (periscope) เครื่องมือเหล่านี้แสดงสิ่งต่างๆ ภายในเลนส์: อยู่ ในขอบเขต (in scope) หากอยู่นอกเลนส์ จะมองไม่เห็น แสงก็ไม่สามารถส่องผ่านได้ คิดในลักษณะนี้ขณะอ่านเกี่ยวกับ 3 ประเภท scope สำคัญใน JavaScript: Local, Global และ Lexical

Local Scope (ขอบเขตท้องถิ่น)

Local scope คือ scope ที่เล็กที่สุด เมื่อประกาศฟังก์ชัน ทุกอย่างภายในวงเล็บปีกกา {} ถือเป็น local ของฟังก์ชันนั้น ตัวแปรถูกสร้างเมื่อฟังก์ชันถูกเรียก และทำลายเมื่อฟังก์ชันสิ้นสุด

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() ผลลัพธ์ของฟังก์ชัน เราเข้าถึง websiteName ได้ แต่นอกฟังก์ชันเข้าถึงไม่ได้ เพราะตัวแปรถูกทำลายเมื่อฟังก์ชันจบ

Global Scope (ขอบเขตทั่วไป)

Global scope คือตัวแปรที่ประกาศนอกฟังก์ชัน สคริปทุกตัว ฟังก์ชันทุกตัว และเมธอดทุกตัวเข้าถึงได้

let counter = 0; // global
const add = () => {
  let counter = 0; // local
  counter += 1;
  return counter;
}
add(); add(); add();
console.log(counter); // 0 หรือ 3? ทำไม?

console.log(counter) จะได้ 0 เพราะ:

  1. Global counter = 0
  2. ฟังก์ชัน add มี local counter ของตัวเอง
  3. ทุกครั้งที่เรียก add() local counter เริ่มที่ 0 ใหม่ เพิ่มเป็น 1 คืนค่า 1 แล้วทำลาย
  4. Global counter ไม่มีการเปลี่ยนแปลงเลย

Lexical Scope (ขอบเขตเชิงลัทธิ)

Lexical scope คือแนวคิดพื้นฐานสำคัญที่สุดใน JavaScript คือ การสร้างฟังก์ชันหรือตัวแปรจะเข้าถึงได้ในบางส่วนของโค้ด และเข้าถึงไม่ได้ในส่วนอื่น ขึ้นอยู่กับตำแหน่งที่ประกาศ

const init = () => {
  const var1 = 'Career'; // outer scope
  const second = () => {
    const var2 = 'Karma'; // inner scope
    console.log(var1); // Career
    console.log(var2); // Karma
    return var1 + " " + var2;
  };
  return second();
};
init();

ฟังก์ชัน init() รู้จัก var1 และ second() แต่ไม่รู้จักสิ่งภายใน second() (inner scope) แต่ second() เข้าถึง var1 ของ outer scope ได้

เหมือนตุ๊กตา Matryoshka (ตุ๊กตาซ้อนใน) ตุ๊กตาใหญ่รู้แค่ว่ามีตุ๊กตาย่อยอยู่ในตัว แต่ไม่รู้รายละเอียดภายใน

กฎสำคัญ 2 ข้อ:

  1. Outer scope เข้าถึง inner scope ไม่ได้
  2. Inner scope เข้าถึง outer scope ได้

นี่คือ one-way relationship เรียกว่า Lexical Scope

Closure

Closure คล้าย lexical scope แต่ต่างตรงที่ Closure เป็น higher-order function (คืนค่าฟังก์ชัน หรือรับฟังก์ชันเป็นพารามิเตอร์)

Closure คือฟังก์ชันที่เข้าถึง lexical scope ของมันได้ แม้ว่าจะถูกเรียกใช้ภายหลัง (หลังจากฟังก์ชัน parent จบการทำงานแล้ว)

function greeting() {
  const userName = "CrrKrma1952";
  function welcomeGreeting() {
    console.log("Hello, " + userName);
    return "Hello, " + userName;
  }
  return welcomeGreeting; // คืนค่าฟังก์ชัน -> HOF
}
const greetUser = greeting();
greetUser(); // Hello, CrrKrma1952

แม้ greeting() จบการทำงานแล้ว welcomeGreeting() ยังเข้าถึง userName ได้ เพราะมัน "ปิดกวด" (close over) ตัวแปรของ parent ไว้ นี่คือ closure

สรุป

บทความนี้ครอบคลุม Scope (Local, Global, Lexical) และ Closure แนะนำอ่านบทความอื่นๆ เพิ่มเติม เพราะมีมุมมองสอนหลายแบบ โชคดีนะครับ!