JavaScript EP4: JavaScript Engine 🛠️⚙️


ໃນ EP ນີ້ເຮົາຈະມາເວົ້າເຖິງ JS Engine ວ່າມັນເຮັດວຽກຈັ່ງໃດ, ເພື່ອໃຫ້ເຮົາເຫັນພາບຂະບວນການເບື້ອງຫຼັງຂອງ JS ຫຼາຍຂຶ້ນ. ເຮົາເຄີຍສົງໄສບໍ່ວ່າບັນດາ machine ມັນເຂົ້າໃຈ code ທີ່ເຮົາຂຽນໄດ້ຈັ່ງໃດ?. JavaScript Developer ບໍ່ຈຳເປັນຕ້ອງຈັດການກັບ compilers ດ້ວຍຕົວເອງ, ເຊິ່ງນັກພັດທະນາມືໃໝ່ຫຼາຍໆຄົນກໍ່ບໍ່ໄດ້ສົນໃຈເລື່ອງນີ້ເລີຍ ແຕ່ມັນຍິ່ງເປັນການດີຖ້າເຮົາຈະສຶກສາ ແລະ ມີຄວາມຮູ້ພື້ນຖານກ່ຽວກັບ JavaScript Engine ແລະ ເບິ່ງວ່າມັນຈະຈັດການກັບ code ທີ່ເປັນ human-friendly ແລະ ປ່ຽນເປັນສິ່ງທີ່ machine ເຂົ້າໃຈ🚀.

ຕົວຢ່າງທີ່ໃຊ້ໃນບົດຄວາມນີ້ແມ່ນໃຊ້ NodeJS ທີ່ based on the V8 Engine ແລະ ໃຊ້ Browser ທີ່ເປັນ Chromium-based.

HTML parser ຈະພົບ script tag(<script></script>)ທີ່ມີການລະບຸ JS source(ອາດຈະຢູ່ໃນ script tag ຫຼື import ມາຈາກໄຟລ໌ນອກກໍ່ໄດ້). code ທີ່ຢູ່ໃນນັ້ນຈະຖືກ load ຈາກທຸກແຫຼ່ງທີ່ມາເຊັ່ນ: networkcache ແລະ service worker ທີ່ຖືກຕິດຕັ້ງໄວ້ແລ້ວ. response ທີ່ໄດ້ຈາກ requested script ແມ່ນຈະຢູ່ໃນຮູບແບບ stream of bytes ທີ່ຈະມີ byte stream decoder ເຮັດໜ້າທີ່ໃນການຖອດລະຫັດ stream of bytes ຫຼັງຈາກມັນໂຫຼດສຳເລັດ.

byte stream decoder ຈະທຳການສ້າງ token ຂຶ້ນມາຫຼັງຈາກທີ່ທຳການຖອດລະຫັດ stream of bytes ສຳເລັດ. ຕົວຢ່າງ, 0066 ທຳການ decode ໄດ້ f0075 ໄດ້ u006e ໄດ້ n0063 ໄດ້ c0074 ໄດ້ t0069 ໄດ້ i006f ໄດ້ o, ແລະ 006e ໄດ້ n ຕາມດ້ວຍ white space. ເບິ່ງຄືກັບວ່າເຮົາຂຽນ function ເຊິ່ງຄຳນີ້ກໍ່ເປັນ reserved keyword ຢູ່ໃນ JavaScript, ເຊິ່ງມັນຈະສ້າງ token ຂຶ້ນມາ ແລະ ສົ່ງໄປທີ່ parser(ແລະ pre-parser, ຈະອະທິບາຍພາຍຫຼັງ). ຂະບວນການທີ່ຍົກຕົວຢ່າງມາແມ່ນຈະເກີດຂຶ້ນກັບ byte stream ທີ່ເຫຼືອເຊັ່ນກັນ.

ຕົວ engine ຈະໃຊ້ parser 2 ຕົວຄື: pre-parser ແລະ parser. ເພື່ອຫຼຸດເວລາໃນການໂຫຼດໜ້າເວັບ, engine ຈະພະຍາຍາມຫຼີກລ່ຽງການ parsing code ທີ່ບໍ່ຈຳເປັນໃຫ້. pre-parser ຈະ handles code ທີ່ອາດຈະໄດ້ໃຊ້ໄວ້ ໃນຂະນະທີ່ຕົວ parser ຈະ handles code ທີ່ຈຳເປັນ. ຖ້າຫາກ function ບາງຢ່າງຖືກເອີ້ນໃຊ້ຫຼັງຈາກທີ່ user ກົດປຸ່ມ,​ ມັນກໍ່ບໍ່ຈຳເປັນທີ່ code ຈະຖືກ compile ໃນທັນທີພຽງເພື່ອໂຫຼດໜ້າເວັບຢ່າງດຽວ. ແຕ່ຖ້າຫາກ user ກົດປຸ່ມ ແລະ ມີການ require code ບາງສ່ວນ, ມັນຈະຖືກສົ່ງໄປ parser.

Parser ຈະທຳການສ້າງ node ຕາມ token ທີ່ໄດ້ຮັບຈາກ byte stream decoder, ຈາກນັ້ນມັນຈະສ້າງ Abstract Syntax Tree ຫຼື AST ດ້ວຍ node ເຫຼົ່ານັ້ນ 🌲🌳.

ຕໍ່ມາຈະເປັນໜ້າທີ່ຂອງ interpreter, ເຊິ່ງ interpreter ຈະດຳເນີນການຜ່ານ AST ແລະ ທຳການສ້າງ byte code ໂດຍອີງຕາມຂໍ້ມູນທີ່ AST ມີ. ເມື່ອ byte code ຖືກສ້າງສຳເລັດແລ້ວ, AST ຈະຖືກລຶບ ແລະ ລ້າງພື້ນທີ່ໜ່ວຍຄວາມຈຳ(memory space) ຫຼັງຈາກນັ້ນເຮົາກໍ່ມີ byte code ທີ່ machine ເຂົ້າໃຈແລ້ວ 🚀.

ເຖິງ byte code ຈະໄວຢູ່ກໍ່ຕາມ, ແຕ່ມັນຍັງໄວກວ່ານີ້ໄດ້ອີກ. ໃນຂະນະທີ່ byte code ນີ້ເຮັດວຽກ, ຂໍ້ມູນຕ່າງໆຈະຖືກ generate ຂຶ້ນ, ເຊິ່ງມັນຈະສາມາດ detect ໄດ້ວ່າພຶດຕິກຳໃດເກີດຂຶ້ນເລື້ອຍໆ ແລະ ປະເພດຂອງ data ຈະຖືກນຳມາໃຊ້. ບາງເທື່ອເຮົາກໍ່ເອີ້ນໃຊ້ function ເປັນສິບໆຮອບ, ເຊິ່ງໃນຈຸດນີ້ເຮົາຈະທຳການ optimize ເພື່ອໃຫ້ມັນເຮັດວຽກໄດ້ໄວ້ຂຶ້ນ.
byte code ແລະ type feedback ທີ່ຖືກ generate ຂຶ້ນແມ່ນຈະຖືກສົ່ງໄປ optimizing compiler ພ້ອມໆກັນ. optimizing compiler ຈະນຳທັງ 2 ຢ່າງນັ້ນມາທຳການ optimized ແລະ generate ອອກມາເປັນ machine code.

JavaScript ເປັນ dynamic type language, ໝາຍຄວາມວ່າ type ຂອງ data ສາມາດປ່ຽນແປງໄດ້ຕະຫຼອດເວລາ. ຖ້າຫາກ JS Engine ຕ້ອງກວດສອບ data type ຢູ່ຕະຫຼອດເວລາ(ແຕ່ໃນຂະບວນການພັດທະນາ, ການໃຊ້ data type ແມ່ນຈະເຮັດໃຫ້ການພັດທະນາມີປະສິດທິພາບຫຼາຍ-ແນະນຳໄປເບິ່ງ TypeScript).

ເພື່ອຫຼຸດເວລາທີ່ໃຊ້ໃນການ interpret code. ໃນຂະນະທີ່ run byte code, machine code ທີ່ຖືກ optimized ແລ້ວຈະ handle ສະເພາະ case ທີ່ engine ເຄີຍເຫັນມາກ່ອນເທົ່ານັ້ນ. ຖ້າເຮົາໃຊ້ code ບາງສ່ວນທີ່ມັນການ return data type ດຽວກັນຊ້ຳໆ, machine code ທີ່ຖືກ optimized ແລ້ວມັນສາມາດນຳມາໃຊ້ໃໝ່ເພື່ອເພີ່ມຄວາມໄວໄດດ້ຕື່ມອີກ. ແຕ່ເຖິງຢ່າງໃດກໍ່ຕາມ, ເນື່ອງຈາກ JavaScript ຖືກອອກແບບມາໃຫ້ເປັນ dynamic type ມັນອາດຈະມີບາງເຫດການທີ່ວ່າ code ບາງສ່ວນທີ່ຄືກັນແຕ່ມັນ return ຄົນລະ data type ອອກມາ. ຖ້າເກີດເຫດການແບບນີ້ຂຶ້ນ machine code ຈະທຳການ de-optimized ແລະ engine ຈະທຳການກັບໄປ interpreting ເພື່ອ generate byte code ໃໝ່.

ສົມມຸດວ່າເຮົາໄດ້ເອີ້ນໃຊ້ບາງ function ເປັນຈຳນວນ 100 ຄັ້ງ ແລະ ມັນຈະ return ຄ່າເດີມມາສະເໝີ, ມັນຈະຖືວ່າມັນຈະທຳການ return ຄ່າເດີມມາຖ້າຫາກເຮົາເອີ້ນໃຊ້ຮອບທີ 101.

ຖ້າຍັງງົງມາເບິ່ງອີກຕົວຢ່າງ: ສົມມຸດວ່າເຮົາມີ sum function ດັ່ງຕໍ່ໄປນີ້, ເຊິ່ງມັນຈະຖືກເອີ້ນໃຊ້ດ້ວຍການປ້ອນ arguments ທີ່ເປັນຄ່າຕົວເລກທຸກຄັ້ງ:

ໃນ function ດ້ານເທິງມັນຈະ return ຄ່າທີ່ເປັນຕົວເລກອອກມາຄື 3, ຄັ້ງຕໍ່ໄປທີ່ເຮົາເອີ້ນໃຊ້ມັນຈະຖືວ່າເຮົາກຳລັງເອີ້ນໃຊ້ອີກຄັ້ງດ້ວຍ arguments ທີ່ເປັນຕົວເລກ 2 ຄ່າ.

ຖ້າມັນເປັນແບບນັ້ນກໍ່ບໍ່ຈຳເປັນຕ້ອງຄົ້ນຫາແບບ dynamic ແລະ ສາມາດ re-use machine code ທີ່ຖືກ optimized ໄດ້ເລີຍ. ແຕ່ຖ້າບໍ່ເປັນແບບນັ້ນ ມັນກໍ່ຈະ revert ໄປເປັນ original byte code ຂອງ machine code ທີ່ຖືກ optimized.

ຕົວຢ່າງ: ຄັ້ງຕໍ່ໄປທີ່ເຮົາເອີ້ນໃຊ້ເຮົາຈະສົ່ງຄ່າທີ່ເປັນ string ແທນຕົວເລກ, ເນື່ອງຈາກ JavaScript ເປັນ dynamic type ເຮົາຈຶ່ງສາມາດເຮັດແບບນີ້ໄດ້ໂດຍທີ່ບໍ່ມີ Error ໃດໆ.

ໝາຍຄວາມວ່າເລກ 2 ຈະຖືກບີບໃຫ້ເປັນ string ແລະ function ຈະ return ຄ່າ "12" ອອກມາແທນ. ທີ່ເປັນແບບນີ້ກໍ່ຍ້ອນມັນກັບໄປ execute byte code ທີ່ຖືກ interpreted ແລ້ວ ແລະ ທຳການ update type ກັບໄປ.

ອ້າງອີງ: 🚀⚙️ JavaScript Visualized: the JavaScript Engine

Web Web Development JavaScript Javascript Engine Js Engine