إذا كنت مطور JavaScript أو تريد أن تصبح واحدًا ، فهذا يعني أنك بحاجة إلى فهم الآليات الداخلية لتنفيذ تعليمات JS البرمجية. على وجه الخصوص ، يعد فهم ما هو سياق التنفيذ ومكدس الاستدعاء ضروريًا تمامًا لإتقان مفاهيم JavaScript الأخرى ، مثل زيادة المتغيرات والنطاق والإغلاق. المادة ، التي ننشر ترجمتها اليوم ، مخصصة لسياق التنفيذ وكومة الاستدعاء في JavaScript.

سياق التنفيذ
سياق التنفيذ ، بعبارات مبسطة ، هو مفهوم يصف البيئة التي يتم فيها تنفيذ تعليمات JavaScript البرمجية. يتم تنفيذ التعليمات البرمجية دائمًا داخل سياق.
▍ تشغيل أنواع السياق
تحتوي جافا سكريبت على ثلاثة أنواع من سياقات التنفيذ:
- سياق التنفيذ العالمي. هذا هو سياق التنفيذ الافتراضي الأساسي. إذا لم تكن بعض التعليمات البرمجية داخل أي وظيفة ، فهذا الرمز ينتمي إلى السياق العام. يتميز السياق العام بوجود كائن عام ، والذي ، في حالة المتصفح ، هو كائن
window
، وحقيقة أن this
تشير إلى هذا الكائن العام. يمكن أن يكون للبرنامج سياق عالمي واحد فقط. - سياق تنفيذ الوظيفة. في كل مرة يتم استدعاء دالة ، يتم إنشاء سياق جديد لها. كل وظيفة لها سياق التنفيذ الخاص بها. قد يكون للبرنامج في نفس الوقت العديد من السياقات لتنفيذ الوظائف. عند إنشاء سياق جديد لتنفيذ دالة ، فإنه يمر عبر سلسلة معينة من الخطوات ، التي سنناقشها أدناه.
- سياق التنفيذ لوظيفة
eval
. الشفرة التي يتم تنفيذها داخل دالة eval
لها أيضًا سياق التنفيذ الخاص بها. ومع ذلك ، نادرًا ما يتم استخدام دالة eval
، لذا لن نتحدث هنا عن سياق التنفيذ هذا.
كومة التنفيذ
مكدس التنفيذ ، الذي يسمى أيضًا مكدس الاستدعاء ، هو مكدس LIFO المستخدم لتخزين سياقات التنفيذ التي تم إنشاؤها أثناء تنفيذ التعليمات البرمجية.
عندما يبدأ محرك JS في معالجة البرنامج النصي ، يقوم المحرك بإنشاء سياق تنفيذ عام ووضعه على المكدس الحالي. عندما يتم الكشف عن أمر لاستدعاء وظيفة ، يقوم المحرك بإنشاء سياق تنفيذ جديد لهذه الوظيفة ويضعها في أعلى المكدس.
يقوم المحرك بتنفيذ وظيفة يكون سياق تنفيذها أعلى المكدس. عند اكتمال الوظيفة ، تتم إزالة سياقها من المكدس ويتم نقل عنصر التحكم إلى السياق الموجود في العنصر السابق من المكدس.
سنستكشف هذه الفكرة بالمثال التالي:
let a = 'Hello World!'; function first() { console.log('Inside first function'); second(); console.log('Again inside first function'); } function second() { console.log('Inside second function'); } first(); console.log('Inside Global Execution Context');
إليك كيفية تغيير مكدس المكالمة عند تنفيذ هذا الرمز.
استدعاء مكدس الحالةعندما يتم تحميل الكود أعلاه في المتصفح ، يقوم محرك JavaScript بإنشاء سياق تنفيذ عام ووضعه على مكدس الاستدعاء الحالي. عند إجراء مكالمة مع الوظيفة
first()
، يقوم المحرك بإنشاء سياق جديد لهذه الوظيفة ويضعها في أعلى المكدس.
عندما يتم استدعاء الوظيفة
second()
الوظيفة
first()
، يتم إنشاء سياق تنفيذ جديد لهذه الوظيفة ويتم دفعه أيضًا إلى المكدس. بعد أن تكمل الوظيفة
second()
عملها ، تتم إزالة سياقها من المكدس ويتم نقل عنصر التحكم إلى سياق التنفيذ الموجود على المكدس الموجود تحتها ، أي إلى سياق الدالة
first()
.
عند الخروج من الوظيفة
first()
، يظهر سياقها من المكدس ويتم نقل عنصر التحكم إلى السياق العام. بعد تنفيذ جميع التعليمات البرمجية ، يسترد المحرك سياق التنفيذ العام من المكدس الحالي.
حول إنشاء سياقات وشفرة تشغيل
حتى الآن ، تحدثنا عن كيفية إدارة محرك JS لسياقات التنفيذ. لنتحدث الآن عن كيفية إنشاء سياقات التنفيذ ، وماذا يحدث لهم بعد إنشائها. على وجه الخصوص ، نحن نتحدث عن مرحلة إنشاء سياق التنفيذ ومرحلة تنفيذ التعليمات البرمجية.
▍ مرحلة إنشاء سياق التنفيذ
قبل تنفيذ شفرة JavaScript ، يتم إنشاء سياق التنفيذ. في عملية إنشائه ، يتم تنفيذ ثلاثة إجراءات:
- يتم تحديد هذه القيمة وهذا ملزم (هذا الربط).
- يتم إنشاء مكون
LexicalEnvironment
. - يتم إنشاء مكون
VariableEnvironment
.
من الناحية النظرية ، يمكن تمثيل سياق التنفيذ على النحو التالي:
ExecutionContext = { ThisBinding = <this value>, LexicalEnvironment = { ... }, VariableEnvironment = { ... }, }
هذا ملزم
في سياق التنفيذ العام ، يحتوي
this
على مرجع إلى الكائن العام (كما ذكرنا سابقًا ، في المستعرض هو كائن
window
).
في سياق تنفيذ الوظيفة ، تعتمد قيمة
this
على كيفية استدعاء الوظيفة. إذا تم تسميته كطريقة لكائن ، فإن قيمة
this
مرتبطة بهذا الكائن. في حالات أخرى ،
this
مرتبط بكائن عمومي أو مضبوط على
undefined
(في وضع صارم). فكر في مثال:
let foo = { baz: function() { console.log(this); } } foo.baz(); // 'this' 'foo', 'baz' // 'foo' let bar = foo.baz; bar(); // 'this' window, //
البيئة المعجمية
وفقًا
لمواصفات ES6 ، تعد البيئة المعجمية مصطلحًا يُستخدم لتحديد العلاقة بين المعرّفات والمتغيرات والوظائف الفردية بناءً على بنية التعشيش المعجم لشفرة ECMAScript. تتكون البيئة المعجمية من سجل بيئي ومرجع إلى البيئة المعجمية الخارجية ، والتي يمكن أن تكون
null
.
ببساطة ، البيئة المعجمية هي بنية تقوم بتخزين المعلومات حول مراسلات المعرّفات والمتغيرات. هنا ، يُقصد بكلمة "المعرّف" اسم متغير أو دالة ، وبكلمة "متغير" هي إشارة إلى كائن محدد (بما في ذلك دالة) أو قيمة بدائية.
في البيئة المعجمية هناك مكونان:
- سجل البيئة. هذا هو المكان الذي يتم فيه تخزين إعلانات المتغيرات والوظائف.
- رابط للبيئة الخارجية. يشير وجود مثل هذا الارتباط إلى أن البيئة المعجمية يمكنها الوصول إلى البيئة المعجمية الأصلية (النطاق).
هناك نوعان من البيئات المعجمية:
- البيئة العالمية (أو سياق التنفيذ العالمي) هي بيئة معجمية لا تحتوي على بيئة خارجية. مرجع البيئة العالمية إلى البيئة الخارجية
null
. في البيئة العالمية (في سجل البيئة) ، تتوفر كيانات لغوية مضمنة (مثل Object
، Array
، وما إلى ذلك) مرتبطة بالكائن العمومي ، وهناك أيضًا متغيرات عامة يحددها المستخدم. تشير قيمة this
في هذه البيئة إلى كائن عمومي. - بيئة الوظيفة التي يتم فيها تخزين المتغيرات التي أعلن عنها المستخدم في سجل البيئة. يمكن أن يشير المرجع إلى البيئة الخارجية إلى كائن عام ودالة خارجية للدالة المعنية.
هناك نوعان من سجلات البيئة:
- سجل بيئي تعريفي يخزن المتغيرات والوظائف والمعلمات.
- سجل كائن بيئة يتم استخدامه لتخزين معلومات حول المتغيرات والدالات في سياق عام.
ونتيجة لذلك ، في بيئة عالمية ، يتم تمثيل سجل البيئة بسجل بيئة كائن ، وفي بيئة وظيفية ، بواسطة سجل بيئة تعريفية.
لاحظ أنه في بيئة الوظيفة ، يحتوي السجل التعريفي للبيئة أيضًا على كائن
arguments
، الذي يخزن المراسلات بين المؤشرات وقيم الوسائط التي تم تمريرها إلى الدالة ، ومعلومات حول عدد هذه الوسيطات.
يمكن تمثيل البيئة المعجمية على أنها الرمز الزائف التالي:
GlobalExectionContext = { LexicalEnvironment: { EnvironmentRecord: { Type: "Object", // } outer: <null> } } FunctionExectionContext = { LexicalEnvironment: { EnvironmentRecord: { Type: "Declarative", // } outer: < > } }
متغيرات البيئة
البيئة المتغيرة هي أيضًا بيئة معجمية يخزن سجل بيئتها الارتباطات التي تم إنشاؤها باستخدام أوامر
VariableStatement
في سياق التنفيذ الحالي.
بما أن بيئة المتغيرات هي أيضًا بيئة معجمية ، فإنها تمتلك جميع الخصائص الموصوفة أعلاه للبيئة المعجمية.
في ES6 ، هناك فرق واحد بين مكونات
LexicalEnvironment
و
VariableEnvironment
. وهو يتألف من حقيقة أن الأولى تُستخدم لتخزين إعلانات الوظائف والمتغيرات المُعلن عنها باستخدام الكلمات الرئيسية
let
and
const
، والأخيرة تُستخدم فقط لتخزين الارتباطات المتغيرة المُعلن عنها باستخدام الكلمة
var
.
فكر في أمثلة توضح ما ناقشناه للتو:
let a = 20; const b = 30; var c; function multiply(e, f) { var g = 20; return e * f * g; } c = multiply(20, 30);
سيبدو التمثيل التخطيطي لسياق التنفيذ لهذا الرمز كما يلي:
GlobalExectionContext = { ThisBinding: <Global Object>, LexicalEnvironment: { EnvironmentRecord: { Type: "Object", // a: < uninitialized >, b: < uninitialized >, multiply: < func > } outer: <null> }, VariableEnvironment: { EnvironmentRecord: { Type: "Object", // c: undefined, } outer: <null> } } FunctionExectionContext = { ThisBinding: <Global Object>, LexicalEnvironment: { EnvironmentRecord: { Type: "Declarative", // Arguments: {0: 20, 1: 30, length: 2}, }, outer: <GlobalLexicalEnvironment> }, VariableEnvironment: { EnvironmentRecord: { Type: "Declarative", // g: undefined }, outer: <GlobalLexicalEnvironment> } }
كما لاحظت على الأرجح ، فإن المتغيرات والثوابت المعلنة باستخدام الكلمات الرئيسية
let
and
const
لا تحتوي على قيم مرتبطة ، ويتم تعيين المتغيرات المعلنة باستخدام الكلمة
var
إلى
undefined
.
هذا لأنه أثناء إنشاء السياق ، يبحث الكود عن إعلانات المتغيرات والوظائف ، بينما يتم تخزين إعلانات الوظائف بالكامل في البيئة. يتم تعيين قيم المتغيرات ، عند استخدام
var
، إلى
undefined
، وعند استخدام
let
أو
const
تظل غير مهيأة.
هذا هو السبب في أنه يمكنك الوصول إلى المتغيرات المعلنة باستخدام
var
قبل الإعلان عنها (على الرغم من أنها
undefined
) ، ولكن عند محاولة الوصول إلى المتغيرات أو الثوابت التي تم الإعلان عنها باستخدام
let
و
const
قبل الإعلان عنها ، يحدث خطأ .
ما وصفناه للتو يسمى "متغيرات الرفع". الإعلانات المتغيرة "ترتفع" إلى أعلى نطاقها المعجمي قبل تنفيذ عمليات تعيين أي قيم لها.
▍ مرحلة تنفيذ الكود
ربما يكون هذا هو أبسط جزء من هذه المواد. في هذه المرحلة ، يتم تعيين القيم للمتغيرات ويتم تنفيذ التعليمات البرمجية.
يرجى ملاحظة أنه إذا لم يتمكن محرك JS ، أثناء تنفيذ الكود ، من العثور على قيمة المتغير المعلن عنه باستخدام الكلمة الأساسية
let
في مكان الإعلان ، فسيعين هذا المتغير قيمة
undefined
.
الملخص
ناقشنا للتو الآليات الداخلية لتنفيذ تعليمات JavaScript البرمجية. على الرغم من أن تكون مطورًا جيدًا لـ JS ، إلا أنه ليس من الضروري معرفة كل هذا ، إذا كان لديك بعض الفهم للمفاهيم المذكورة أعلاه ، فسوف تساعدك على فهم الآليات الأخرى للغة بشكل أفضل وأعمق ، مثل رفع المتغيرات والنطاق ، دوائر قصيرة.
أعزائي القراء! ما الذي تعتقد أنه بخلاف سياق التنفيذ ومكدس المكالمات مفيد لمطوري جافا سكريبت ليعرفوا؟
