من التعريف إلى التنفيذ ، يقوم محرك JS بالكثير من أعمال التهيئة في طبقة التنفيذ. لذلك ، قبل تعلم آلية العمل لمحرك JS ، نحتاج إلى تقديم العديد من المفاهيم ذات الصلة: مكدس بيئة التنفيذ ، والكائنات العالمية ، وبيئة التنفيذ ، والكائنات المتغيرة ، والكائنات النشطة ، وسلاسل النطاق وسلاسل النطاق ، وما إلى ذلك. هذه المفاهيم هي المكونات الأساسية لعمل محرك JS. الغرض من هذه المقالة ليس شرح كل مفهوم لك في عزلة ، ولكن لتحليله من خلال عرض تجريبي بسيط ، وشرح تفاصيل محرك JS من التعريف إلى التنفيذ ، والدور الذي تلعبه هذه المفاهيم فيه.
var x = 1 ؛ // تحديد متغير عالمي Xfunction a (y) {var x = 2 ؛ // تحديد متغير محلي X الدالة B (z) {// تحديد وظيفة داخلية B console.log (x+y+z) ؛ } العودة ب ؛ // إرجاع مرجع إلى الدالة b} var c = a (1) ؛ // تنفيذ a ، return bc (1) ؛ // تنفيذ الدالة بهذا العرض التوضيحي عبارة عن إغلاق ، ونتيجة التنفيذ هي 4. سنقوم أدناه بتحليل آلية العمل لمحرك JS في ثلاث مراحل: التهيئة العالمية ، وظيفة التنفيذ A ، ودالة التنفيذ ب :
1. التهيئة العالمية
عندما يدخل محرك JS إلى جزء قابل للتنفيذ ، يحتاج إلى إكمال مهام التهيئة الثلاثة التالية:
أولاً ، قم بإنشاء كائن عالمي (كائن عالمي). لا يوجد سوى نسخة عالمية واحدة من هذا الكائن ، ويمكن الوصول إلى خصائصه في أي مكان ، وسيصاحب وجودها دورة حياة التطبيق بأكملها. عند إنشاء كائن عالمي ، يتم استخدام كائنات JS شائعة الاستخدام مثل الرياضيات والسلسلة والتاريخ والوثيقة كخصائصها. نظرًا لأنه لا يمكن الوصول إلى هذا الكائن العالمي مباشرة بالاسم ، فهناك نافذة خاصية أخرى ، ويشير إلى النافذة إلى نفسها ، بحيث يمكن الوصول إلى الكائن العالمي من خلال النافذة. الهيكل العام لاستخدام الرمز الزائف لمحاكاة الكائنات العالمية هو كما يلي:
// إنشاء كائن عالمي var globaloBject = {math: {} ، السلسلة: {} ، التاريخ: {} ، المستند: {} ، // dom stare ... window: this // let the window statribute تشير إلى نفسها}ثم ، يحتاج محرك JS إلى إنشاء مكدس سياق تنفيذ. في الوقت نفسه ، يحتاج أيضًا إلى إنشاء سياق تنفيذ عالمي ودفع بيئة التنفيذ العالمية هذه إلى مكدس بيئة التنفيذ. تتمثل وظيفة مكدس بيئة التنفيذ في التأكد من تنفيذ البرنامج بالترتيب الصحيح. في JavaScript ، كل وظيفة لها بيئة التنفيذ الخاصة بها. عند تنفيذ وظيفة ، سيتم دفع بيئة تنفيذ الوظيفة إلى أعلى مكدس بيئة التنفيذ والحصول على حقوق التنفيذ. عند تنفيذ هذه الوظيفة ، يتم حذف بيئة التنفيذ الخاصة بها من الجزء العلوي من المكدس ويتم إرجاع حق التنفيذ إلى بيئة التنفيذ السابقة. نستخدم الرمز الكاذب لمحاكاة العلاقة بين مكدس بيئة التنفيذ و EC:
var ecstack = [] ؛ // تحديد مكدس بيئة التنفيذ ، على غرار Array var ec = {} ؛ // إنشاء مساحة تنفيذ ، // ECMA-262 لا تحدد مواصفات بنية بيانات EC بوضوح ، يمكنك فهمها كقطعة من المساحة المخصصة في الذاكرة Ecstack.push (EC) ؛ // أدخل الوظيفة ودفع بيئة التنفيذ ecstack.pop (EC) ؛ // بعد عودة الوظيفة ، احذف بيئة التنفيذأخيرًا ، يقوم محرك JS أيضًا بإنشاء كائن متغير عالمي (كائن varibale) VO المرتبط بـ EC و Points Vo للكائن العالمي. لا يحتوي VO على الخصائص الأصلية للكائن العالمي فحسب ، بل يتضمن أيضًا المتغير X ووظيفة محددة عالميًا. في الوقت نفسه ، عند تحديد الوظيفة A ، تتم إضافة نطاق السمة الداخلية أيضًا إلى نطاق ونقاط VO. عندما يتم تعريف كل وظيفة ، سيتم إنشاء سمة النطاق المرتبطة بها ، ويشير النطاق دائمًا إلى البيئة التي يتم تحديد الوظيفة فيها. هيكل Ecstack في هذا الوقت كما يلي:
EcStack = [// Execution Environment 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 هي الكائن النشط لـ A ، ويقع AO في الطرف الأمامي من القائمة المرتبطة. نظرًا لأن القائمة المرتبطة بها خصائص الاتصال النهائي ، فإن نطاق الوظيفة B يشير إلى سلسلة النطاق بأكملها من A. لنلقي نظرة على بنية Ecstack في هذا الوقت:
EcStack = [// Execution Environment Stack EC (a) = {// a evelope envormation [scope]: vo (g) ، // vo هو الكائن المتغير العالمي AO (a): {// إنشاء الكائن النشط y: 1 ، x: 2 ، // تحديد المتغير المحلي x b: function () {...} ، // define the function bb [ // هذا يشير إلى AO نفسه ، و AO في الجزء العلوي من scopechain ، لذلك B [[النطاق]] يشير إلى حجج سلسلة النطاق بأكملها: [] ، // الوسيطات التي نصل إليها في الوظيفة هي وسيطات في AO هذا: window // هذا في نقاط الوظيفة إلى كائن نافذة المتصل} ، scopechain: <a) ثم يضاف AO إلى أعلى سلسلة النطاق. في هذا الوقت ، سلسلة نطاق A: AO (A)-> VO (G)} ، EC (G) = {// بيئة التنفيذ العالمية VO (G): {// إنشاء كائن متغير عالمي ... // يحتوي على السمات الأصلية للكائن العالمي x = 1 ؛ // تحديد المتغير x a = function () {...} ؛ // تحديد الوظيفة aa [[scope]] = هذا ؛ // تحديد نطاق a ، a [[scope]] == vo (g)}}] ؛3. تنفيذ الوظيفة ب
بعد تنفيذ الوظيفة A ، يتم إرجاع الإشارة إلى B وتعيينها إلى المتغير C. تنفيذ C (1) يعادل تنفيذ B (1). يحتاج محرك JS إلى إكمال المهام التالية:
أولاً ، مثل أعلاه ، قم بإنشاء بيئة التنفيذ EC للدالة B ، ثم ادفع EC إلى أعلى مكدس بيئة التنفيذ والحصول على حقوق التنفيذ. في هذا الوقت ، هناك بيئتان للتنفيذ في مكدس بيئة التنفيذ ، وهما بيئة التنفيذ العالمية وبيئة تنفيذ الوظيفة B. بيئة تنفيذ B في الجزء العلوي من المكدس ، وبيئة التنفيذ العالمية في أسفل المكدس. (ملاحظة: عندما يتم إرجاع الوظيفة A ، سيتم حذف بيئة تنفيذ A من المكدس ، تاركًا فقط بيئة التنفيذ العالمية) ثم قم بإنشاء سلسلة نطاق الوظيفة B وتهيئتها إلى الكائن الوارد في نطاق الوظيفة B ، أي سلسلة نطاق A. أخيرًا ، قم بإنشاء كائن نشط للوظيفة B ، واستخدام المعلمات z ، كائن الحجج والكائن B كما Properies AO. في هذا الوقت ، سيصبح Ecstack هكذا:
EcStack = [// Execution Environment stack eC (b) = {// إنشاء بيئة تنفيذ B و Us في الجزء العلوي من سلسلة النطاق [Scope]: ao (a) Scopechain: <ao (b) ، b [[scope]]> // تتم تهيئة القائمة المرتبطة إلى B [[Scope]] ، ثم يضاف Ao (B) إلى رأس القائمة المرتبطة. في هذا الوقت ، تم حذف سلسلة نطاق B's: AO (B)-> AO (A) -VO (G)} ، EC (A) ، // A Environment Environment من الجزء العلوي من المكدس ، EC (G) = {// بيئة التنفيذ العالمية VOO: // تحديد المتغير x a = function () {...} ؛ // تعريف الدالة aa [[النطاق]] = هذا ؛ // تحديد نطاق a ، a [[scope]] == 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 أكثر.