LexicalEnvironment والإغلاق في EcmaScript

مرحبا يا هبر!

لم أكتب أي شيء منذ فترة طويلة ، والكثير من العمل في المشروع خلال الأسابيع القليلة الماضية ، ولكن الآن لدي وقت فراغ ، لذلك قررت أن أقدم لكم مقالة جديدة.

سنستمر اليوم في تحليل مفاهيم EcmaScript الرئيسية والتحدث عن البيئة المعجمية والإغلاق. فهم مفهوم بيئة Lexical مهم جدًا لفهم الإغلاق ، والإغلاق هو أساس الكثير من التقنيات والتقنيات الجيدة في عالم JS (والتي تستند إلى مواصفات EcmaScript).

لذلك دعونا نبدأ.

البيئة المعجمية (LexicalEnvironment، LO، LE)


تحدد المواصفات ES6 الرسمية هذا المصطلح بأنه:
بيئة Lexical هي نوع من المواصفات المستخدمة لحل أسماء المعرفات عند البحث عن متغيرات ووظائف محددة بناءً على البنية المعجمية لتداخل كود ECMAScript. تتكون البيئة المعجمية من سجل للبيئة ، وربما إشارة فارغة إلى البيئة المعجمية الخارجية.
سوف نفهم بمزيد من التفاصيل.

سوف أتخيل البيئة المعجمية كنوع من الهياكل التي تخزن اتصال معرفات السياق بمعناها. هذا هو نوع من مستودع المتغيرات والوظائف والفئات المعلنة في نطاق هذا السياق.

من الناحية الفنية ، LO هو كائن ذو خاصيتين:

  • سجل البيئة (هذا هو المكان الذي يتم فيه تخزين جميع الإعلانات)
  • تصل إلى LO السياق العام.

من خلال ارتباط إلى السياق الأصل للسياق الحالي ، يمكننا ، إذا لزم الأمر ، الحصول على رابط إلى "سياق الجد" للسياق الرئيسي ، وهكذا ، إلى السياق العام ، ستكون الإشارة إلى الأصل منها باطلة. من هذا التعريف ، يترتب على ذلك أن بيئة Lexical هي صلة الكيان بالسياقات الأصلية. نوع من ScopeChain في وظائف هو التناظرية للبيئة Lexical. تحدثنا عن ScopeChain بالتفصيل في هذه المقالة .

let x = 10; let y = 20; const foo = z => { let x = 100; return x + y + z; } foo(30);// 150.   foo    {record: {z: 30, x: 100}, parent: __parent}; // __parent      {record: {x: 10, y: 20}, parent: null} 

من الناحية الفنية ، ستحدث عملية تحليل أسماء المعرّفات كما هو الحال في ScopeChain ، أي سيحدث الاستقصاء المتسلسل للكائنات في حلقة LO حتى يتم العثور على المعرف المطلوب. إذا لم يتم العثور على المعرف ، فراجع ReferenceError.

يتم إنشاء البيئة المعجمية وملؤها في مرحلة إنشاء السياق. عندما ينتهي السياق الحالي من التنفيذ ، تتم إزالته من مكدس الاستدعاءات ، لكن يمكن أن تستمر بيئة Lexical في العيش طالما يوجد رابط واحد على الأقل. هذه هي واحدة من مزايا النهج الحديث لتصميم لغات البرمجة. أعتقد أنه يستحق الحديث عنه!

كومة منظمة مقابل الذاكرة المشتركة بشكل حيوي


في لغات المكدس ، يتم تخزين المتغيرات المحلية على المكدس ، والذي يتم تجديده عندما يتم تنشيط الوظيفة ، وعندما تنتهي الوظيفة ، تتم إزالة المتغيرات المحلية من المكدس.

مع مؤسسة مكدسة ، لن يكون من الممكن إرجاع دالة محلية من دالة ، أو استدعاء دالة إلى متغير حر.

المتغير الحر هو متغير تستخدمه دالة ، لكنه ليس معلمة رسمية ولا متغير محلي لهذه الوظيفة.

 function testFn() { var locaVar = 10; //     innerFn function innerFn(p) { alert(p + localVar); } return innerFn; //  } var test = testFn();//  innerFn   test();//       

مع تنظيم المكدس ، لن يكون بالإمكان البحث عن locaVar في LexicalEnvironment الخارجية أو إرجاع دالة innerFn ، لأن innerFn هو أيضا إعلان محلي ل testFn. عند الانتهاء من اختبار testFn ، ستتم إزالة كل المتغيرات المحلية من المكدس.

لذلك ، تم اقتراح مفهوم آخر - مفهوم الذاكرة المخصصة ديناميكيًا (كومة الذاكرة المؤقتة ، heep) + أداة تجميع مجمعي البيانات المهملة + مرجع الحساب. جوهر هذا المفهوم بسيط: طالما هناك مرجع واحد على الأقل لكائن ما ، فلا يتم حذفه من الذاكرة. مزيد من التفاصيل يمكن العثور عليها هنا .

إغلاق (الإغلاق)


الإغلاق عبارة عن مزيج من مجموعة التعليمات البرمجية وبيانات السياق الذي يتم فيه إنشاء هذه الكتلة ، أي إنها علاقة الكيان بسياقات التوليد من خلال سلسلة من LO أو SopeChain.

اسمحوا لي أن أقتبس مقالة جيدة للغاية حول هذا الموضوع:

 function person() { let name = 'Peter'; return function displayName() { console.log(name); }; } let peter = person(); peter(); // prints 'Peter' 

عند تنفيذ وظيفة الشخص ، يقوم JavaScript بإنشاء سياق تنفيذ جديد والبيئة المعجمية لهذه الوظيفة. بعد اكتمال هذه الوظيفة ، ستقوم بإرجاع وظيفة displayName وسيتم تعيينها إلى المتغير بيتر.

وهكذا ، فإن البيئة المعجمية لها ستبدو كما يلي:

 personLexicalEnvironment = { environmentRecord: { name : 'Peter', displayName: < displayName function reference> } outer: <globalLexicalEnvironment> } 

عند اكتمال وظيفة الشخص ، يظهر سياق التنفيذ الخاص به خارج الحزمة. ولكن البيئة المعجمية الخاصة بها ستظل في الذاكرة ، حيث تشير البيئة المعجمية لدالة displayName الداخلية إليها. وبالتالي ، ستبقى متغيراته متوفرة في الذاكرة.

عندما يتم تنفيذ وظيفة بيتر (والتي هي في الواقع إشارة إلى وظيفة displayName) ، يقوم JavaScript بإنشاء سياق تنفيذ جديد وبيئة لغوية لهذه الوظيفة.

لذلك سوف تبدو بيئته المعجمية كما يلي:

 displayNameLexicalEnvironment = { environmentRecord: { } outer: <personLexicalEnvironment> } 

لا يوجد أي متغير في وظيفة displayName ؛ سيكون سجل البيئة فارغًا. أثناء تنفيذ هذه الوظيفة ، سيحاول JavaScript العثور على متغير الاسم في البيئة المعجمية لهذه الوظيفة.

نظرًا لعدم وجود متغيرات في البيئة المعجمية لدالة displayName ، فسوف تبحث في البيئة المعجمية الخارجية ، أي البيئة المعجمية لوظيفة الشخص ، والتي لا تزال في الذاكرة. سيجد JavaScript هذا المتغير ويتم طباعة الاسم على وحدة التحكم.
من أهم خصائص الإغلاق في ES أنه يستخدم نطاقًا ثابتًا (في عدد من اللغات الأخرى التي تستخدم الإغلاق ، يكون الموقف مختلفًا).

مثال:

 var a = 5; function testFn() { alert(a); } (function(funArg) { var a = 20; funArg();//  5 ..  ScopeChain/LexicalEnvironment testFn   ,    = 5 })(testFn) 

خاصية إغلاق مهمة أخرى هي الموقف التالي:

 var first; var second; function testFn() { var a = 10; first = function() { return ++a; } second = function() { return --a; } a = 2; first();//3 } testFn(); first();//4 second();//3 

أي نرى أن المتغير الحر الموجود في إغلاق العديد من الوظائف يتم تغييره بالرجوع إليه.

استنتاج


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

Source: https://habr.com/ru/post/ar474852/


All Articles