จากคำจำกัดความถึงการดำเนินการเอ็นจิ้น JS ทำงานการเริ่มต้นจำนวนมากในเลเยอร์การใช้งาน ดังนั้นก่อนที่จะเรียนรู้กลไกการทำงานของเอ็นจิ้น JS เราจำเป็นต้องแนะนำแนวคิดที่เกี่ยวข้องหลายประการ: สภาพแวดล้อมการดำเนินการสแต็กวัตถุระดับโลกสภาพแวดล้อมการดำเนินการวัตถุตัวแปรวัตถุที่ใช้งานขอบเขตขอบเขตและขอบเขต ฯลฯ แนวคิดเหล่านี้เป็นองค์ประกอบหลักของการทำงานของเครื่องยนต์ JS จุดประสงค์ของบทความนี้ไม่ได้อธิบายแต่ละแนวคิดให้คุณโดดเดี่ยว แต่เพื่อวิเคราะห์ผ่านการสาธิตอย่างง่ายอธิบายรายละเอียดของเครื่องยนต์ JS จากคำจำกัดความถึงการดำเนินการและบทบาทที่แนวคิดเหล่านี้มีอยู่ในนั้น
var x = 1; // กำหนดตัวแปรส่วนกลาง xfunction a (y) {var x = 2; // กำหนดฟังก์ชันตัวแปร x (z) {// กำหนดฟังก์ชันภายใน b console.log (x+y+z); } return b; // ส่งคืนการอ้างอิงไปยังฟังก์ชั่น b} var c = a (1); // ดำเนินการ a, return bc (1); // เรียกใช้ฟังก์ชัน Bการสาธิตนี้เป็นการปิดและผลการดำเนินการคือ 4 ด้านล่างเราจะวิเคราะห์กลไกการทำงานของเครื่องยนต์ JS ในสามขั้นตอน: การเริ่มต้นทั่วโลก, ฟังก์ชั่นการดำเนินการ A และฟังก์ชั่นการดำเนินการ B :
1. การเริ่มต้นทั่วโลก
เมื่อเครื่องยนต์ JS เข้าสู่รหัสที่ใช้งานได้มันจะต้องทำงานให้เสร็จสามงานต่อไปนี้:
อันดับแรกให้สร้างวัตถุระดับโลก (วัตถุทั่วโลก) มีสำเนาทั่วโลกเพียงเล่มเดียวของวัตถุนี้คุณสมบัติของมันสามารถเข้าถึงได้ทุกที่และการมีอยู่ของมันจะมาพร้อมกับวงจรชีวิตทั้งหมดของแอปพลิเคชัน เมื่อสร้างวัตถุทั่วโลกให้ใช้วัตถุ JS ที่ใช้กันทั่วไปเช่นคณิตศาสตร์สตริงวันที่เอกสารจะใช้เป็นคุณสมบัติของพวกเขา เนื่องจากวัตถุทั่วโลกนี้ไม่สามารถเข้าถึงได้โดยตรงด้วยชื่อจึงมีหน้าต่างคุณสมบัติอื่นและชี้ไปที่หน้าต่างไปยังตัวเองเพื่อให้สามารถเข้าถึงวัตถุทั่วโลกผ่านหน้าต่าง โครงสร้างทั่วไปของการใช้รหัสหลอกเพื่อจำลองวัตถุทั่วโลกมีดังนี้:
// สร้างวัตถุโกลบอล var globalObject = {math: {}, string: {}, วันที่: {}, เอกสาร: {}, // การทำงานของ DOM ... หน้าต่าง: นี่ // ปล่อยให้แอตทริบิวต์หน้าต่างชี้ไปที่ตัวเอง}จากนั้นเครื่องยนต์ JS จำเป็นต้องสร้างสแต็กบริบทการดำเนินการ ในขณะเดียวกันก็จำเป็นต้องสร้างบริบทการดำเนินการทั่วโลก EC และผลักดันสภาพแวดล้อมการดำเนินการทั่วโลกนี้ EC ไปยังสแต็กสภาพแวดล้อมการดำเนินการ ฟังก์ชั่นของสแต็กสภาพแวดล้อมการดำเนินการคือเพื่อให้แน่ใจว่าโปรแกรมสามารถดำเนินการตามลำดับที่ถูกต้อง ใน JavaScript แต่ละฟังก์ชั่นมีสภาพแวดล้อมการดำเนินการของตัวเอง เมื่อดำเนินการฟังก์ชั่นสภาพแวดล้อมการดำเนินการของฟังก์ชั่นจะถูกผลักไปที่ด้านบนของสแต็กสภาพแวดล้อมการดำเนินการและได้รับสิทธิ์ในการดำเนินการ เมื่อฟังก์ชั่นนี้ถูกดำเนินการสภาพแวดล้อมการดำเนินการจะถูกลบออกจากด้านบนของสแต็กและสิทธิการดำเนินการจะถูกส่งกลับไปยังสภาพแวดล้อมการดำเนินการก่อนหน้านี้ เราใช้ pseudocode เพื่อจำลองความสัมพันธ์ระหว่างสแต็กสภาพแวดล้อมการดำเนินการกับ EC:
var ecstack = []; // กำหนดสแต็กสภาพแวดล้อมการดำเนินการคล้ายกับอาร์เรย์ var ec = {}; // สร้างพื้นที่ดำเนินการตามข้อกำหนด // ECMA-262 ไม่ได้กำหนดโครงสร้างข้อมูลของ EC อย่างชัดเจนคุณสามารถเข้าใจได้ว่าเป็นส่วนหนึ่งของพื้นที่ที่จัดสรรในหน่วยความจำ ecstack.push (EC); // ป้อนฟังก์ชั่นและผลักดันสภาพแวดล้อมการดำเนินการ ecstack.pop (EC); // หลังจากฟังก์ชั่นส่งคืนให้ลบสภาพแวดล้อมการดำเนินการในที่สุดเครื่องยนต์ JS ยังสร้างวัตถุตัวแปรทั่วโลก (วัตถุ Varibale) VO ที่เกี่ยวข้องกับ EC และคะแนน VO ไปยังวัตถุทั่วโลก VO ไม่เพียง แต่มีคุณสมบัติดั้งเดิมของวัตถุทั่วโลกเท่านั้น แต่ยังรวมถึงตัวแปร X และฟังก์ชั่นที่กำหนดไว้ทั่วโลก ในเวลาเดียวกันเมื่อกำหนดฟังก์ชั่น A จะมีการเพิ่มขอบเขตแอตทริบิวต์ภายในลงในขอบเขต A และคะแนนให้กับ VO เมื่อแต่ละฟังก์ชั่นถูกกำหนดแอตทริบิวต์ขอบเขตที่เกี่ยวข้องจะถูกสร้างขึ้นและขอบเขตจะชี้ไปที่สภาพแวดล้อมที่ฟังก์ชันถูกกำหนดไว้เสมอ โครงสร้าง ecstack ในเวลานี้มีดังนี้:
ecStack = [// สภาพแวดล้อมการดำเนินการ stack ec (g) = {// สภาพแวดล้อมการดำเนินการทั่วโลก vo (g): {// กำหนดวัตถุตัวแปรส่วนกลาง ... // มีแอตทริบิวต์ดั้งเดิมของวัตถุโกลบอล x = 1; // กำหนดตัวแปร x a = function () {... }; // กำหนดฟังก์ชั่น aa [[ขอบเขต]] = สิ่งนี้; // กำหนดขอบเขตของ A และกำหนดค่าให้กับ VO เอง}}];2. ฟังก์ชั่นก
เมื่อการดำเนินการเข้าสู่ (1) เครื่องยนต์ JS ต้องทำสิ่งต่อไปนี้:
ก่อนอื่นเอ็นจิ้น JS จะสร้างสภาพแวดล้อมการดำเนินการ EC ของฟังก์ชั่น A และจากนั้น EC จะผลักดันมันขึ้นไปด้านบนของสแต็กสภาพแวดล้อมการดำเนินการและรับสิทธิ์ในการดำเนินการ ในเวลานี้มีสองสภาพแวดล้อมการดำเนินการในสแต็กสภาพแวดล้อมการดำเนินการคือสภาพแวดล้อมการดำเนินการทั่วโลกและฟังก์ชั่นสภาพแวดล้อมการดำเนินการ สภาพแวดล้อมการดำเนินการของ A อยู่ที่ด้านบนของสแต็กและสภาพแวดล้อมการดำเนินการทั่วโลกอยู่ที่ด้านล่างของสแต็ก จากนั้นสร้างขอบเขตห่วงโซ่ของฟังก์ชั่น A. ใน JavaScript แต่ละสภาพแวดล้อมการดำเนินการมีห่วงโซ่ขอบเขตของตัวเองสำหรับการแก้ไขตัวระบุ เมื่อสภาพแวดล้อมการดำเนินการถูกสร้างขึ้นโซ่ขอบเขตของมันจะเริ่มต้นเป็นวัตถุที่มีอยู่ในขอบเขตของฟังก์ชันที่ทำงานอยู่ในปัจจุบัน
ถัดไปเอ็นจิ้น JS จะสร้างวัตถุที่ใช้งานอยู่ (วัตถุเปิดใช้งาน) AO ของฟังก์ชั่นปัจจุบัน วัตถุที่ใช้งานอยู่ที่นี่มีบทบาทของวัตถุตัวแปร แต่เรียกว่าแตกต่างกันในฟังก์ชัน (คุณสามารถคิดได้ว่าวัตถุตัวแปรเป็นแนวคิดทั่วไปและวัตถุที่ใช้งานอยู่เป็นสาขาของมัน) AO มีพารามิเตอร์อย่างเป็นทางการของฟังก์ชันวัตถุอาร์กิวเมนต์วัตถุนี้รวมถึงคำจำกัดความของตัวแปรท้องถิ่นและฟังก์ชั่นภายในจากนั้น AO จะถูกผลักไปที่ด้านบนของห่วงโซ่ขอบเขต ควรสังเกตว่าเมื่อกำหนดฟังก์ชั่น B เครื่องยนต์ JS จะเพิ่มแอตทริบิวต์ขอบเขตให้กับขอบเขต B และจุดไปยังสภาพแวดล้อมที่กำหนดฟังก์ชั่น B สภาพแวดล้อมที่มีการกำหนดฟังก์ชั่น B คือ Active Object AO และ AO ตั้งอยู่ที่ส่วนหน้าของรายการที่เชื่อมโยง เนื่องจากรายการที่เชื่อมโยงมีลักษณะของการเชื่อมต่อสิ้นสุดขอบเขตของฟังก์ชั่น B ชี้ไปที่ห่วงโซ่ขอบเขตทั้งหมดของ A. มาดูโครงสร้าง ecstack ในเวลานี้:
ecstack = [// สภาพแวดล้อมการดำเนินการ stack ec (a) = {// สภาพแวดล้อมการดำเนินการของ [ขอบเขต]: vo (g), // vo เป็นวัตถุตัวแปรส่วนกลาง ao (a): {// สร้างวัตถุที่ใช้งานอยู่ y: 1, x: 2, // define ตัวแปรท้องถิ่น x b: {... } // สิ่งนี้หมายถึง AO เองและ AO อยู่ที่ด้านบนของ Scopechain ดังนั้น B [[ขอบเขต]] ชี้ไปที่ข้อโต้แย้งโซ่ขอบเขตทั้งหมด: [], // ข้อโต้แย้งที่เราเข้าถึงในฟังก์ชั่นเป็นข้อโต้แย้งใน AO นี้: หน้าต่างนี้ชี้ไปที่ LINK/SCOPE] จากนั้น AO จะถูกเพิ่มไปที่ด้านบนของห่วงโซ่ขอบเขต ในเวลานี้ห่วงโซ่ขอบเขตของ A: AO (a)-> vo (g)}, ec (g) = {// สภาพแวดล้อมการดำเนินการทั่วโลก vo (g): {// สร้างวัตถุตัวแปรส่วนกลาง ... // มีคุณสมบัติดั้งเดิมของวัตถุทั่วโลก x = 1; // กำหนดตัวแปร x a = function () {... }; // กำหนดฟังก์ชั่น aa [[ขอบเขต]] = สิ่งนี้; // กำหนดขอบเขตของ a, a [[ขอบเขต]] == vo (g)}}];3. เรียกใช้ฟังก์ชัน B
หลังจากฟังก์ชั่น A ดำเนินการแล้วการอ้างอิงถึง B จะถูกส่งคืนและกำหนดให้กับตัวแปร C. การดำเนินการของ C (1) นั้นเทียบเท่ากับการดำเนินการ B (1) เครื่องยนต์ JS จำเป็นต้องทำงานต่อไปนี้ให้เสร็จ:
อันดับแรกเช่นข้างต้นให้สร้างสภาพแวดล้อมการดำเนินการ EC ของฟังก์ชั่น B จากนั้นผลักดัน EC ไปยังด้านบนสุดของสภาพแวดล้อมการดำเนินการสแต็กและรับสิทธิ์ในการดำเนินการ ในเวลานี้มีสองสภาพแวดล้อมการดำเนินการในสแต็กสภาพแวดล้อมการดำเนินการคือสภาพแวดล้อมการดำเนินการทั่วโลกและสภาพแวดล้อมการดำเนินการของฟังก์ชั่น B. สภาพแวดล้อมการดำเนินการของ B อยู่ที่ด้านบนของสแต็กและสภาพแวดล้อมการดำเนินการทั่วโลกอยู่ที่ด้านล่างของสแต็ก (หมายเหตุ: เมื่อฟังก์ชั่นการส่งคืนสภาพแวดล้อมการดำเนินการของ A จะถูกลบออกจากสแต็กออกจากสภาพแวดล้อมการดำเนินการทั่วโลกเท่านั้น) จากนั้นสร้างขอบเขตห่วงโซ่ของฟังก์ชัน B และเริ่มต้นมันลงในวัตถุที่มีอยู่ในขอบเขตของฟังก์ชัน B นั่นคือขอบเขตของ A. ในเวลานี้ ecstack จะกลายเป็นเช่นนี้:
ecstack = [// สภาพแวดล้อมการดำเนินการ stack ec (b) = {// สร้างสภาพแวดล้อมการดำเนินการของ B และอยู่ที่ด้านบนของโซ่ขอบเขต [ขอบเขต]: AO (a), // ชี้ไปที่ห่วงโซ่ขอบเขตของฟังก์ชัน a, ao (a)-> vo (g) var ao (b) = {// Scopechain: <ao (b), b [[scope]]> // รายการที่เชื่อมโยงจะเริ่มต้นเป็น b [[ขอบเขต]] จากนั้น AO (b) จะถูกเพิ่มลงในส่วนหัวของรายการที่เชื่อมโยง ในเวลานี้ห่วงโซ่ขอบเขตของ B: AO (B)-> AO (A) -VO (G)}, EC (A), // สภาพแวดล้อมการดำเนินการของ A // A ถูกลบออกจากด้านบนของสแต็ก, EC (G) = {// สภาพแวดล้อมการดำเนินการทั่วโลก // กำหนดตัวแปร x a = function () {... }; // กำหนดฟังก์ชั่น aa [[ขอบเขต]] = สิ่งนี้; // กำหนดขอบเขตของ a, a [[ขอบเขต]] == vo (g)}}];เมื่อฟังก์ชั่น B ดำเนินการ "x+y+z" จำเป็นต้องแยกวิเคราะห์ตัวระบุสามตัว x, y และ z ทีละหนึ่ง กระบวนการแยกวิเคราะห์เป็นไปตามกฎการค้นหาตัวแปร: ก่อนอื่นให้ค้นหาว่าแอตทริบิวต์มีอยู่ในวัตถุที่ใช้งานของคุณหรือไม่ หากมีอยู่ให้หยุดค้นหาและส่งคืน; หากไม่มีอยู่ให้ค้นหาต่อจากด้านบนตามโซ่ขอบเขตจนกว่าจะพบ หากไม่พบตัวแปรในห่วงโซ่ขอบเขตทั้งหมดให้กลับ "ไม่ได้กำหนด" จากการวิเคราะห์ข้างต้นเราจะเห็นว่าห่วงโซ่ขอบเขตของฟังก์ชั่น B มีดังนี้:
ao (b)-> ao (a)-> vo (g)
ดังนั้นตัวแปร X จะพบได้ใน AO (A) และจะไม่มองหา X ใน VO (G) ตัวแปร Y จะพบได้ใน AO (A) และตัวแปร Z จะพบได้ใน AO ของตัวเอง (B) ดังนั้นผลการดำเนินการ: 2+1+1 = 4
สรุปง่ายๆ
หลังจากทำความเข้าใจกลไกการทำงานของเครื่องยนต์ JS เราไม่สามารถอยู่ในระดับของการทำความเข้าใจแนวคิด แต่ใช้เป็นเครื่องมือพื้นฐานในการเพิ่มประสิทธิภาพและปรับปรุงรหัสของเราในการทำงานจริงปรับปรุงประสิทธิภาพการดำเนินการและสร้างมูลค่าที่แท้จริง ใช้กลไกการค้นหาตัวแปรเป็นตัวอย่าง หากรหัสของคุณซ้อนกันอย่างลึกซึ้งและทุกครั้งที่คุณอ้างถึงตัวแปรระดับโลกเครื่องยนต์ JS จะมองหาห่วงโซ่ขอบเขตทั้งหมด ตัวอย่างเช่นมีปัญหานี้กับหน้าต่างและวัตถุเอกสารที่ด้านล่างของห่วงโซ่ขอบเขต ดังนั้นเราสามารถทำการเพิ่มประสิทธิภาพประสิทธิภาพจำนวนมากในการแก้ไขปัญหานี้และแน่นอนว่ามีการเพิ่มประสิทธิภาพด้านอื่น ๆ ฉันจะไม่เข้าไปดูรายละเอียดที่นี่ บทความนี้ถือได้ว่าเป็นคำแนะนำ!
โดย @一竞 2015
ข้างต้นเป็นเนื้อหาทั้งหมดของบทความนี้ ฉันหวังว่ามันจะเป็นประโยชน์ต่อการเรียนรู้ของทุกคนและฉันหวังว่าทุกคนจะสนับสนุน wulin.com มากขึ้น