JavaScript EP3: Scope (Chain)⛓️🚧
Tags: webdev, javascript, beginners, programming
Published: 2023-04-03 13:20:47

ມາຮອດ ep ນີ້, ເຊື່ອວ່າຫຼາຍໆຄົນກໍ່ອາດຈະເຄີຍຂຽນ JS ບໍ່ຫຼາຍກໍ່ໜ້ອຍ, ແຕ່ຈະພະຍາຍາມຍົກຕົວຢ່າງທີ່ງ່າຍ ແລະ ເຂົ້າໃຈໄດ້ພາຍໃນ 3 ວິນາທີ🥹🥹🥹.

ເຮົາມາເບິ່ງ code ກັນກ່ອນເລີຍ:

```javascript
const name = "Lydia"
const age = 21
const city = "San Francisco"

function getPersonInfo() {
  const name = "Sarah"
  const age = 22

  return `${name} is ${age} and lives in ${city}`
}

console.log(getPersonInfo())
```
ຈາກ code ຈະເຫັນວ່າເຮົາໄດ້ທຳການ invoke `getPersonInfo` function ທີ່ມີການ return ຄ່າເປັນ string ອອກມາ, ໃນນັ້ນປະກອບດ້ວຍຕົວປ່ຽນ `name`, `age` ແລະ `city`. ຜົນທີ່ໄດ້ຈາກ code ດ້ານເທິງແມ່ນ `Sarah is 22 and lives in San Francisco`, ແຕ່ໃນ `getPersonInfo` function ບໍ່ໄດ້ບັນຈຸຕົວປ່ຽນ `city` ແລ້ວມັນຮູ້ຈັກຕົວປ່ຽນ `city` ໄດ້ແນວໃດ?🤔.

ທຳອິດຈະມີການຈັດການພື້ນທີ່ memory ໃນຫຼາຍໆ context ທີ່ແຕກຕ່າງກັນ, ເຮົາມີ **global context** ເປັນ default(`window` ໃນ browser ແລະ `global` ໃນ Node) ແລະ **local context** ສຳລັບ `getPersonInfo` function ທີ່ຖືກ invoke. ແຕ່ລະ context ກໍ່ຈະມີ **scope chain** ເຊັ່ນກັນ.

ສຳລັບ `getPersonInfo` function, scope chain ຈະມີຂະບວນການປະມານນີ້:
![scope chain](https://res.cloudinary.com/practicaldev/image/fetch/s--57Tstr0c--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/89b9buizhevs0jf6djyn.png)

ໂດຍພື້ນຖານແລ້ວ scope chain ເປັນ "chain of references" ໄປຫາ object ທີ່ມີການອ້າງອີງເຖິງ values(ແລະ scopes ອື່ນໆ)ທີ່ຖືກອ້າງອີງຢູ່ໃນ execution context. scope chain ຖືກສ້າງຂຶ້ນຕອນທີ່ execution context ຖືກສ້າງຂຶ້ນ, ໝາຍຄວາມວ່າມັນຖືກສ້າງຕອນ runtime.

ເຖິງຢ່າງໃດກໍ່ຕາມ, ເຮົາຈະຍັງບໍ່ເວົ້າເຖິງ activation object ຫຼື execution contexts ໃນບົດຄວາມນີ້ເທື່ອ(ໃນບົດຄວາມນີ້ເຮົາມາໂຟກັສທີ່ scope ກັນກ່ອນ). ດັ່ງຕົວຢ່າງດ້ານລຸ່ມ key/value ທີ່ຢູ່ນຳ execution contexts ຈະສະແດງເຖິງການອ້າງອີງທີ່ scope chain ມີຕໍ່ຕົວປ່ຽນ.
![key/value](https://res.cloudinary.com/practicaldev/image/fetch/s--3L9mEScA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/iala2et7bg9bgdj4c2lg.png)

scope chain ຂອງ global execution context ມີການອ້າງອີງເຖິງ 3 ຕົວປ່ຽນຄື: `name` ໂດຍມີຄ່າ `Lydia`, `age` ມີຄ່າ `21` ແລະ `city` ມີຄ່າ `San Francisco`. ໃນ local context ເຮົາມີການອ້າງອີງເຖິງ 2 ຕົວປ່ຽນຄື: `name` ມີຄ່າ `Sarah` ແລະ `age` ມີຄ່າ `22`.
ເມື່ອເຮົາ access ເຂົ້າເຖິງຕົວປ່ຽນທີ່ຢູ່ໃນ `getPersonInfo`, JS Engine ຈະທຳການກວດຢູ່ local scope chain ກ່ອນ.
![local scope chain](https://res.cloudinary.com/practicaldev/image/fetch/s--e_017_sT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/xn17f0t54acz8tiq7122.gif)

local scope chain ມີການອ້າງອີງເຖິງ `name` ແລະ `age`, `name` ມີຄ່າ `Sarah` ແລະ `age` ມີຄ່າ `22`. ເຮົາມາລອງສັງເກດເບິ່ງວ່າຖ້າເຮົາ access ຕົວປ່ຽນ `city` ຈະເປັນຈັ່ງໃດ?🫣
ເພື່ອຫາ value ຂອງຕົວປ່ຽນ `city`, JS Engine ຈະທຳການລົງໄປຄົ້ນຫາຕາມ scope chain ໝາຍຄວາມວ່າຖ້າມັນບໍ່ພົບ value ໃນ local scope chain ມັນກໍ່ຈະທຳການຫາຢູ່ໃນ outer scope(scope ທີ່ນອກ local scope)ທີ່ local scope ໄດ້ມີການອ້າງອີງເຖິງ ໃນຕົວຢ່າງດ້ານລຸ່ມຈະແມ່ນ **global object**.
![local scope](https://res.cloudinary.com/practicaldev/image/fetch/s--uE5KEpLT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/z9iclg23rmbpts7meoq6.gif)

ໃນ global context ເຮົາໄດ້ປະກາດຕົວປ່ຽນ `city` ມີຄ່າ `San Francisco`, ດັ່ງນັ້ນ ຈຶ່ງມີການອ້າງອີງເຖິງຕົວປ່ຽນ `city`. ຕອນນີ້ເຮົາມີ value ຂອງແຕ່ລະຕົວປ່ຽນແລ້ວຈຶ່ງເຮັດໃຫ້ `getPersonInfo` function ສາມາດ return ຄ່າທີ່ເປັນ string ອອກມາໄດ້ ແລະ ຜົນທີ່ໄດ້ແມ່ນ `Sarah is 22 and lives in San Francisco`.

---

ເຮົາສາມາດລົງໄປຊັ້ນລຸ່ມຂອງ scope chain ໄດ້, ແຕ່ບໍ່ສາມາດຂຶ້ນມາຊັ້ນເທິງຂອງ scope chain ໄດ້ ຫຼື ຈະເອີ້ນວ່າອອກນອກ scope ໄດ້ແຕ່ບໍ່ສາມາດເຂົ້າໄປໃນ scope ແບບນີ້ກໍ່ໄດ້ເຊັ່ນກັນ. ຖ້າຍັງງົງກໍ່ໄປເບິ່ງຕົວຢ່າງນຳກັນເລີຍ😵‍💫.
ໃນຕົວຢ່າງຈະອະທິບາຍໃນລັກສະນະຂອງຂະບວນການໄຫຼຂອງນ້ຳຕົກ(ໄຫຼຈາກເທິງລົງລຸ່ມ).
![waterfall](https://res.cloudinary.com/practicaldev/image/fetch/s--mJn1OY2F--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/doq46yc6nuiam51evy44.png)
ຫຼື ກໍລະນີທີ່ມີ function ຊ້ອນກັນອີກກໍ່ຈະມີໜ້າຕາ ແລະ ຊົງຜົມປະມານນີ້:
![deeper waterfall](https://res.cloudinary.com/practicaldev/image/fetch/s--ymRwRgkB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/rece2zj4pb4w1fn56q5k.png)

---

ເຮົາລອງມາສຶກສາເບິ່ງ code ຕົວຢ່າງນີ້ໄປນຳກັນ
![example code](https://res.cloudinary.com/practicaldev/image/fetch/s--HQ8cmkNI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/0z6342b72f3n6v6ufafk.png)
ມັນຄ້າຍຄືກັນກັບຕົວຢ່າງຕັ້ງແຕ່ທຳອິດແທບຈະທຸກຢ່າງ, ແຕ່ເຖິງຢ່າງໃດກໍຕາມມັນມີຈຸດແຕກຕ່າງໃຫຍ່ໆເລີຍກໍ່ຈະແມ່ນ: ເຮົາໄດ້ປະກາດຕົວປ່ຽນ `city` ຢູ່ໃນ `getPersonInfo` function ແລະ ບໍ່ຢູ່ໃນ global scope. ໃນທີ່ນີ້ເຮົາບໍ່ໄດ້ invoke `getPersonInfo` function, ດັ່ງນັ້ນ ມັນຈະບໍ່ມີການສ້າງ local context ເຊັ່ນກັນ. ເຮົາລອງ access ເຖິງ value ຂອງ `name`, `age` ແລະ `city` ໃນ global context ດັ່ງນີ້:
![call local scope from global](https://res.cloudinary.com/practicaldev/image/fetch/s--bqIqLoUF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/f3wvlo4c3gqf3mve1g0n.gif)
ຜົນທີ່ໄດ້ຄືມັນຈະທຳການໂຍນ `ReferenceError` ສູດດັ້ງເດີມເຈົ້າເກົ່າມາໃສ່ໜ້າເຮົາ, ສາເຫດກໍ່ຍ້ອນມັນບໍ່ເຫັນຕົວປ່ຽນ `city` ໃນ global scope, ອີກຢ່າງກໍ່ຄືເຮົາເອີນຈາກ global scope ຢູ່ແລ້ວມັນເລີຍບໍ່ມີ scope ຊັ້ນລຸ່ມໃຫ້ຫາອີກ ແລະ ມັນບໍ່ສາມາດຂຶ້ນໄປຊັ້ນເທິງຂອງ scope ໄດ້ນັ້ນເອງ.
ດ້ວຍວິທີນີ້ເຮົາສາມາດ protect ຕົວປ່ຽນຂອງເຮົາ ແລະ ສາມາດ re-use ຊື່ຕົວປ່ຽນຄືນໄດ້ອີກ.

---
ນອກຈາກ global scope ແລະ local scope ແລ້ວມັນຍັງມີ **block scope** ອີກ🤯. ຕົວປ່ຽນທີ່ປະກາດດ້ວຍ `let` ແລະ `const` keyword ຈະເປັນຕົວປ່ຽນທີ່ມີ scope ຢູ່ໃນ brackets (`{}`).

```javascript
const age = 21

function checkAge() {
  if (age < 21) {
    const message = "You cannot drink!"
    return message
  } else {
    const message = "You can drink!"
    return message
  }
} 
```
ຖ້າເຮົາລອງ visualize scope ອອກມາກໍ່ຈະໄດ້:
![block scope](https://res.cloudinary.com/practicaldev/image/fetch/s--t-azbfm3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/75n1vpm7z4d8924cnvje.png)
ເຮົາມີ global scope, function scope ແລະ block scopes 2 ອັນ. ເຊິ່ງເຮົາສາມາດປະກາດຕົວປ່ຽນ `message` ໄດ້ 2 ຄັ້ງ, ເນື່ອງຈາກຕົວປ່ຽນຖືກກຳນົດ scope ໄວ້ໃນຂອບເຂດຂອງ brackets.

---
ສະຫຼຸບ:
- ເຮົາຈະເຫັນວ່າ scope chain ເປັນ chain ຂອງການອ້າງອີງເຖິງ value ທີ່ເຮົາສາມາດ access ໄດ້ໃນ context ທີ່ເຮົາກຳລັງເຮັດຢູ່.
- scope ເຮັດໃຫ້ເຮົາສາມາດ re-use ຊື່ຂອງຕົວປ່ຽນທີ່ໄດ້ຖືກປະກາດໄວ້ຢູ່ໃນ scope chain, ເນື່ອງຈາກມັນສາມາດເຮັດວຽກແບບລົງຊັ້ນລຸ່ມໄດ້ຢ່າງດດຽວ ແລະ ບໍ່ສາມາດຂຶ້ນໄປຊັ້ນເທິງຂອງ scope ໄດ້.

ອ້າງອີງ:
[⚡️⛓JavaScript Visualized: Scope (Chain)](https://dev.to/lydiahallie/javascript-visualized-scope-chain-13pd)