مرحبا يا هبر!
في
مقال سابق ، درسنا النظرية العامة ل OOP كما هو مطبق على EcmaScript والمغالطة الشعبية للمطورين المبتدئين فيما يتعلق بالاختلافات بين OOP في JS واللغات الكلاسيكية.
اليوم سوف نتحدث عن مفهومي EcmaScript آخرين مهمين ، وهما علاقة الكيان بسياق التنفيذ (
هذا هو الاتصال) وعلاقة الكيان بسياق
الإنشاء (
ScopeChain ).
لذلك دعونا نبدأ!
هذا
في المقابلات رداً على السؤال: "أخبرنا المزيد عن
هذا ". للمطورين المبتدئين ، كقاعدة عامة ، تقديم إجابات غامضة للغاية: "
هذا هو الكائن" قبل النقطة "الذي تم استخدامه لاستدعاء الطريقة ،" "
هذا هو السياق الذي تم استدعاء الوظيفة فيه ،" إلخ ...
في الواقع ، فإن الموقف مع هذا المفهوم ، وهو أمر أساسي في EcmaScript ، أكثر تعقيدًا إلى حد ما. دعونا معرفة ذلك بالترتيب.
دعنا نقول أن لدينا برنامج جافا سكريبت يحتوي على متغيرات تم الإعلان عنها عالميًا ؛ وظائف عالمية الوظائف المحلية (المعلنة داخل وظائف أخرى) ، وظائف عاد من وظائف.
const a = 10; const b = 20; const x = { a: 15, b: 25, } function foo(){ return this.a + this.b; } function bar () { const a = 30; return a + b; } function fooBaz(){ function test () { return this.a + this.b; } return test(); } function fooBar() { const a = 40; const b = 50; return function () { return a + b; } } fooBar()();
عند نقل التحكم إلى التعليمات البرمجية القابلة للتنفيذ ، يتم إدخال إدخال في سياق التنفيذ. رمز قابل للتنفيذ - هذا هو أي رمز ننفذه في وقت معين من الوقت ، يمكن أن يكون رمزًا عالميًا أو رمزًا لأي وظيفة.
سياق التنفيذ عبارة عن تجريد يقوم بكتابة وتعيين الكود. من وجهة نظر هذا التجريد ، يتم تقسيم الرمز إلى عالمي (أي نصوص متصلة ، نصوص مضمنة) ورمز وظيفة (رمز الوظائف المتداخلة لا ينتمي إلى سياق الوظائف الأصل).
هناك نوع ثالث - EvalCode. في هذه المقالة ، نحن نهملها.
من الناحية المنطقية ، فإن مجموعة سياقات التنفيذ هي
رصة تعمل على مبدأ Last-in-First-out (lifo). الجزء السفلي من المكدس هو دائمًا السياق العام ، والجزء العلوي هو الملف القابل للتنفيذ الحالي. في كل مرة يتم استدعاء وظيفة ، يتم إدخال إدخال في سياقها. عند اكتمال دالة ، ينتهي سياقها. تتم إزالة السياقات المستهلكة من المكدس بالتسلسل وفي ترتيب عكسي.
ألقِ نظرة على الكود أعلاه. لدينا مكالمة إلى وظيفة
fooBar في التعليمات البرمجية العالمية. في دالة
fooBar ، نرجع
وظيفة مجهولة المصدر نسميها على الفور. تحدث التغييرات التالية مع بنية تخزين العناصر: يدخل
سياق عام فيه - عندما
يتم استدعاء
fooBar ، وسياق سياقه -
يتم إنهاء
fooBar وإرجاع
وظيفة مجهولة المصدر وإزالتها من الحزمة - يتم استدعاء
وظيفة مجهولة ، وسياق سياقها -
تكامل وظيفة مجهولة ويتم حذف السياق الخاص به من المكدس - في نهاية البرنامج النصي ، يتم حذف
السياق العام من المكدس.
يمكن تمثيل سياق التنفيذ ككائن. ستكون إحدى خصائص هذا الكائن هي بيئة Lexical (LO).
البيئة المعجمية تحتوي على:
- كل التصريحات متغير السياق
- جميع إعلانات الوظائف
- جميع المعلمات الرسمية للوظيفة (إذا كنا نتحدث عن سياق الوظائف)
عند إدخال سياق التنفيذ ، يقوم المترجم بمسح السياق. كل التصريحات المتغيرة وإعلانات الوظائف ترتفع إلى بداية السياق. يتم إنشاء المتغيرات على قدم المساواة مع عدم تحديدها ، والوظائف جاهزة تمامًا للاستخدام.
هذا أيضًا خاصية خاصة بسياق التنفيذ ، ولكن ليس السياق نفسه ، كما يجيب بعض المقابلات المبتدئين!
يتم تعريف
هذا عند إدخال السياق ويبقى دون تغيير حتى نهاية عمر السياق (حتى تتم إزالة السياق من المكدس).
في سياق التنفيذ العام ، يتم تحديد ذلك من خلال
وضع صارم : عندما يكون وضع التقييد قيد إيقاف التشغيل ، فإن هذا يحتوي على كائن عام (في المتصفح يتم تقريبه إلى المستوى الأعلى في كائن النافذة) ، مع "استخدام صارم" هذا غير محدد.
هذا في سياق وظائف - السؤال هو أكثر إثارة للاهتمام بكثير!
يتم تحديد هذا في الوظائف بواسطة المتصل ويعتمد على بناء جملة المكالمة. على سبيل المثال ، كما نعلم ، هناك طرق تتيح لك تثبيت هذا بشدة عند الاتصال (
call ،
تطبيق ) وطريقة تسمح لك بإنشاء برنامج تغليف بـ "fix this" (
ربط ). في هذه الحالات ، نذكر هذا صراحة ولا يمكن أن يكون هناك شك في تعريفه.
مع استدعاء وظيفة عادية ، فإن الوضع أكثر تعقيدا بكثير!
سوف يساعدنا أحد أنواع EcmaScript المضمنة ،
ReferenceType ، في فهم كيفية تثبيت هذا في الوظائف. هذا هو أحد الأنواع الداخلية المتوفرة على مستوى التنفيذ. منطقيا ، هو كائن ذو خواص
أساسية (مرجع إلى كائن أساسي معين يتم إرجاع ReferenceType له) ،
propertyName (تمثيل سلسلة لمعرف الكائن الذي يتم إرجاع ReferenceType له).
يتم إرجاع
ReferenceType لجميع الإعلانات المتغيرة وإعلانات الوظائف ومراجع الممتلكات (هذه هي الحالة التي تهمنا من وجهة نظر فهم هذا).
قاعدة تعريف
هذا للوظائف التي يتم استدعاءها بالطريقة المعتادة:
إذا كان ReferenceType على يسار أقواس تنشيط الوظيفة ، فسيتم وضع أساس ReferenceType في this
الوظيفة. إذا كان هناك أي نوع آخر على يسار الأقواس ، this
إما كائن عالمي أو undefined
( null
الواقع ، ولكن بما أن null لا تحتوي على قيمة محددة من وجهة نظر ecmascript ، فسيتم إرسالها إلى كائن عمومي ، وهي إشارة يمكن أن تكون يساوي undefined
اعتمادا على الوضع الصارم).لنلقِ نظرة على مثال:
const x = 0; const obj = { x: 10, foo: function() { return this.x; } } obj.foo();
أعتقد أن طريقة التعريف موضحة بوضوح. الآن النظر في عدد قليل من الحالات أقل وضوحا.
تعبيرات وظيفية
دعنا نعود إلى مرجعنا لثانية واحدة. يحتوي هذا النوع على أسلوب
GetValue مضمن يقوم بإرجاع النوع الحقيقي للكائن الذي تم استلامه من خلال ReferenceType. في منطقة التعبير ، ينطلق GetValue دائمًا.
مثال:
(function (){ return this;
هذا يرجع إلى حقيقة أن GetValue يتم تشغيله دائمًا في منطقة التعبير. إرجاع GetValue نوع دالة ، وإلى يسار أقواس التنشيط ليس ReferenceType. استرجع حكمنا لتحديد
هذا :
إذا كان أي نوع آخر على يسار الأقواس ، فسيتم وضع كائن عمومي في this
أو undefined
( null
الواقع ، ولكن بما أن null لا تحتوي على قيمة معينة من وجهة نظر ecmascript ، فسيتم تحويلها إلى كائن عمومي ، الرابط الذي يمكن أن يكون مساويا لغير محدد حسب الوضع الصارم) .
مناطق التعبير هي: الواجب (=) ، المشغلات || أو عوامل تشغيل منطقية أخرى ، عامل تشغيل ثلاثي ، مُهيئ صفيف ، قائمة مفصولة بفواصل.
const x = 0; const obj = { x: 10, foo: function() { return this.x; } } obj.foo();
وضع مماثل في التعبيرات الوظيفية المسماة. حتى مع وجود مكالمة متكررة لهذا الكائن العمومي أو
undefined
هذه الوظائف المتداخلة تسمى في الأصل
أيضا موقف مهم!
const x = 0; function foo() { function bar(){ return this.x; } return bar(); } const obj = {x:10}; obj.test = foo; obj.test();
هذا لأن المكالمة إلى
bar()
تعادل المكالمة إلى
LE_foo.bar
،
LE_foo.bar
كائن البيئة المعجمية هذا التعريف.
وظائف البناء
كما كتبت أعلاه:
يتم تحديد هذا في الوظائف بواسطة المتصل ويعتمد على بناء جملة المكالمة.
نحن ندعو وظائف المنشئ باستخدام الكلمة الأساسية الجديدة. خصوصية هذه الطريقة في تنشيط الوظيفة هي أن طريقة الوظيفة الداخلية
[[build]] تسمى ، والتي تقوم بتنفيذ عمليات معينة (آلية إنشاء كيانات من قبل المصممين ستتم مناقشتها في المقالة الثانية أو الثالثة على OOP!) وتدعو الطريقة الداخلية
[[call]] ، التي تعطل في
هذا المثال الذي تم إنشاؤه لدالة المنشئ.
سلسلة النطاق
سلسلة النطاق هي أيضا خاصية لسياق التنفيذ مثل هذا. إنها قائمة بكائنات البيئات المعجمية للسياق الحالي وجميع السياقات المولدة. في هذه السلسلة ، يحدث البحث عن المتغيرات عند حل أسماء المعرفات.
ملاحظة: هذا يربط وظيفة مع سياق التنفيذ ، و ScopeChain مع السياقات التابعة.
تنص المواصفات على أن ScopeChain عبارة عن صفيف:
SC = [LO, LO1, LO2,..., LOglobal];
ومع ذلك ، في بعض التطبيقات ، مثل JS ، يتم تنفيذ سلسلة النطاق من خلال
قوائم مرتبطة .
من أجل فهم ScopeChain بشكل أفضل ، سنناقش دورة حياة الوظائف. وهي مقسمة إلى مرحلة الإنشاء ومرحلة التنفيذ.
عند إنشاء دالة ، يتم تخصيص الخاصية الداخلية
[[SCOPE]] .
في
[[SCOPE]] ، يتم تسجيل سلسلة هرمية من كائنات البيئات المعجمية في السياقات (المولدة) الأعلى. تبقى هذه الخاصية دون تغيير حتى يتم تدمير الوظيفة بواسطة أداة تجميع البيانات المهملة.
انتبه!
[[SCOPE]] ، على عكس ScopeChain ، هي خاصية للدالة نفسها ، وليس لسياقها.
عند استدعاء دالة ، تتم تهيئة سياق التنفيذ وتعبئته. يتم إرفاق السياق بـ ScopeChain = LO (من الدالة نفسها) + [[SCOPE]] (السلسلة الهرمية لـ LO التي تؤثر على السياقات).
تحليل أسماء المعرفات - استطلاع متسلسل لكائنات
LO في سلسلة
ScopeChain من اليسار إلى اليمين. الإخراج هو ReferenceType الذي تشير ملكيته الأساسية إلى كائن LO الذي تم العثور على المعرف فيه ، وسيكون PropertyName عبارة عن تمثيل سلسلة لاسم المعرف.
هذه هي الطريقة التي يتم بها ترتيب الإغلاق تحت غطاء محرك السيارة! الإغلاق هو في الأساس نتيجة البحث في ScopeChain عن جميع المتغيرات التي توجد معرفاتها في الوظيفة.
const x = 10; function foo () { return x; } (function (){ const x = 20; foo();
يوضح المثال التالي دورة الحياة
[[SCOPE]] .
function foo () { const x = 10; const y = 20; return function () { return [x,y]; } } const x = 30; const bar = foo();
استثناء مهم هو
وظيفة المنشئ . بالنسبة لهذا النوع من الوظائف ، يشير [[SCOPE]] دائمًا إلى كائن عمومي.
أيضًا ، لا تنس أنه إذا كان أحد الروابط في سلسلة ScopeChain يحتوي على نموذج أولي ، فسيتم إجراء البحث في النموذج الأولي أيضًا.
استنتاج
سنطرح الأفكار الرئيسية من الناحية النظرية:
- هذه هي علاقة الكيان بسياق التنفيذ
- ScopeChain هي علاقة الكيان بجميع سياقات التفريخ
- هذا و ScopeChain خصائص سياق التنفيذ
- يتم تحديد هذه الوظائف بواسطة المتصل وتعتمد على بناء جملة المكالمة
- ScopeChain هي البيئة المعجمية للسياق الحالي + [[Scope]]
- [[النطاق]] - هذه خاصية للوظيفة نفسها ، وتحتوي على سلسلة هرمية من البيئات المعجمية لتوليد السياقات
آمل أن المقالة كانت مفيدة. حتى المقالات في المستقبل ، والأصدقاء!