9.4 KiB
title | localeTitle |
---|---|
Scopes | نطاقات |
إذا كنت تقوم بالبرمجة في JavaScript لفترة من الوقت ، فستواجه بدون شك مفهومًا يعرف باسم scope
. ما هو scope
؟ لماذا يجب أن تأخذ الوقت الكافي لتعلمها؟
في intermer speak ، scope
هو سياق التنفيذ الحالي . مشوش؟ لنلق نظرة على جزء الشفرة التالي:
`var foo = 'Hi, I am foo!';
var baz = function () { var bar = 'Hi, I am bar too!'; console.log(foo); }
baz(); // Hi, I am foo! console.log(bar); // ReferenceError... `
هذا مثال بسيط ، لكنه يقوم بعمل جيد لتوضيح ما يعرف باسم نطاق Lexical . جافا سكريبت ، وتقريبا كل لغة برمجة أخرى لديها نطاق Lexical . هناك نوع آخر من النطاق يعرف بالنطاق الديناميكي ، لكننا لن نناقش ذلك.
الآن ، يبدو مصطلح نطاق Lexical يتوهم ، ولكن كما سترى ، فإن الأمر بسيط للغاية من حيث المبدأ. في نطاق المعجم ، هناك نوعان من النطاقات: النطاق العالمي والنطاق المحلي .
قبل كتابة السطر الأول من التعليمة البرمجية في البرنامج ، يتم إنشاء نطاق عالمي من أجلك. يحتوي هذا على كافة المتغيرات التي تقوم بتعريفها في البرنامج خارج أي وظائف .
في المثال أعلاه ، يكون foo
المتغير في النطاق العالمي للبرنامج ، بينما يتم الإعلان عن bar
المتغير داخل دالة وبالتالي يكون في النطاق المحلي لتلك الوظيفة .
يتيح تقسيم المثال عن طريق سطر. في حين قد تكون مرتبكًا في هذه المرحلة ، فأعدك أن يكون لديك فهم أفضل بكثير عند الانتهاء من قراءة هذا.
على خط 1 نحن نعلن متغير foo
. لا شيء يتوهم جدا هنا. دعنا نطلق على هذا إشارة إلى حجم اليسار (LHS) إلى foo
، لأننا نقوم بتعيين قيمة foo
وهي تقع على الجانب الأيسر من علامة equal
.
في السطر 3 ، نعلن عن وظيفة ونقوم بتعيينها إلى baz
متغير. هذه إشارة LHS أخرى إلى baz
. نحن نقوم بتعيين قيمة لها (تذكر ، الدوال هي قيم أيضًا!). ثم يتم استدعاء هذه الدالة على السطر 8. هذا هو RHS ، أو إشارة الجانب الأيمن إلى baz
. نحن baz
قيمة baz
، والتي في هذه الحالة دالة ثم تستدعيها. سيكون مرجع RHS آخر إلى baz
إذا قمنا بتعيين قيمته إلى متغير آخر ، على سبيل المثال foo = baz
. سيكون هذا إشارة LHS إلى foo
وإشارة RHS إلى baz
.
قد تبدو المراجع LHS و RHS مربكة ، ولكنها مهمة لمناقشة النطاق. فكر في الأمر بهذه الطريقة: يقوم مرجع LHS بتعيين قيمة للمتغير ، بينما يقوم مرجع RHS باسترداد قيمة المتغير. إنها طريقة أقصر وأكثر ملاءمةً للقول "استرداد القيمة" و "تعيين قيمة".
دعونا الآن نقوم بتعطل ما يحدث داخل الوظيفة نفسها.
عندما يجمع المحول البرمجي الرمز داخل دالة ، فإنه يدخل النطاق المحلي للوظيفة.
على الخط 4 ، يتم التصريح عن bar
المتغير. هذه إشارة LHS إلى bar
. في السطر التالي ، لدينا مرجع RHS foo
داخل console.log()
. تذكر أننا foo
قيمة foo
ثم نمررها كحجة إلى console.log()
.
عندما يكون لدينا مرجع RHS إلى foo
، يبحث المترجم عن إعلان المتغير foo
. لا يجدها المحول البرمجي في الوظيفة نفسها ، أو النطاق المحلي للوظيفة ، حيث يرتفع مستوى واحد: إلى النطاق العالمي .
في هذه المرحلة ، ربما تفكر في أن النطاق له علاقة بالمتغيرات. هذا صحيح. يمكن اعتبار النطاق بمثابة حاوية للمتغيرات. لا يمكن الوصول إلى جميع المتغيرات التي يتم إنشاؤها داخل نطاق محلي إلا في النطاق المحلي. ومع ذلك ، يمكن لكافة النطاقات المحلية الوصول إلى النطاق العالمي. (أعلم أنك على الأرجح أكثر إرباكًا الآن ، لكن فقط تحمل معي لفقرات قليلة أخرى).
وبالتالي فإن المترجم ترتفع إلى نطاق عالمي لإيجاد مرجعية LHS إلى متغير foo
. يجد واحدًا على السطر 1 ، بحيث يسترد القيمة من مرجع LHS ، وهو سلسلة: 'Hi, I am foo!'
. يتم إرسال هذه السلسلة إلى الأسلوب console.log()
و outputted إلى وحدة التحكم.
انتهى المحول البرمجي من تنفيذ الشفرة داخل الدالة ، لذا نعود إلى السطر 9. في السطر 9 ، لدينا مرجع RHS bar
المتغير.
الآن ، أعلن bar
في النطاق المحلي من baz
، ولكن هناك إشارة RHS bar
في النطاق العالمي. نظرًا لعدم وجود مرجع LHS bar
في النطاق العالمي ، فلن يتمكن المحول البرمجي من العثور على قيمة bar
ويلقي ReferenceError.
ولكن ، قد تسأل ، إذا كانت الوظيفة يمكن أن تبدو خارج نفسها للمتغيرات ، أو نطاق محلي يمكن أن ينظر إلى النطاق العالمي للعثور على مراجع LHS ، فلماذا لا يستطيع النطاق العالمي النظر إلى نطاق محلي؟ حسنا هذا هو كيف يعمل المدى معجمي!
... // global scope var baz = function() { ... // baz's scope } ... /// global scope
هذا هو نفس رمز من أعلاه الذي يوضح النطاق. يشكل هذا نوعًا من التسلسل الهرمي الذي يرتقي إلى النطاق العالمي:
baz -> global
.
لذا ، إذا كان هناك مرجع RHS لمتغير داخل نطاق baz
، فيمكن تحقيقه بواسطة مرجع LHS لهذا المتغير في النطاق العالمي. لكن العكس ليس صحيحًا .
ماذا لو كان لدينا وظيفة أخرى داخل baz
؟
`... // global scope var baz = function() { ... // baz's scope
var bar = function() { ... // bar's scope. }
} ... /// global scope `
في هذه الحالة ، سيبدو التسلسل الهرمي أو سلسلة النطاق كما يلي:
bar -> baz -> global
أي إشارات RHS داخل bar
'نطاق محلي الصورة يمكن fullfilled بالإشارة LHS في نطاق عالمي أو baz
"نطاق الصورة، ولكن إشارة RHS في baz
" نطاق الصورة لا يمكن fullfilled بالإشارة LHS في bar
"نطاق الصورة.
يمكنك فقط عبور سلسلة نطاق ، وليس لأعلى.
هناك شيئين مهمين آخرين يجب معرفتهما حول نطاقات جافا سكريبت.
- يتم الإعلان عن النطاقات بواسطة الدالات ، وليس بواسطة الكتل.
- وظائف يمكن الرجوع إلى الأمام ، المتغيرات لا يمكن.
لاحظ (كل تعليق يصف النطاق الموجود في السطر المكتوب عليه):
`` ` // outer () في النطاق هنا لأن الدالات يمكن أن تكون مرجعية للأمام
`function outer() {
// only inner() is in scope here
// because only functions are forward-referenced
var a = 1;
//now 'a' and inner() are in scope
function inner() {
var b = 2
if (a == 1) {
var c = 3;
}
// 'c' is still in scope because JavaScript doesn't care
// about the end of the 'if' block, only function inner()
}
// now b and c are out of scope
// a and inner() are still in scope
}
// here, only outer() is in scope `
`` `
المراجع
- نطاقات وإغلاق بواسطة كايل سيمبسون. يذهب إلى مزيد من التفاصيل حول كيفية عمل آلية النطاق ، ولديه أيضًا وصفًا سطحيًا لكيفية عمل مترجم جافا سكريبت ، لذلك إذا كنت مهتمًا بذلك ، فقم بالتأكيد بإعطائه قراءة! إنه مجاني على GitHub ويمكن شراؤه من O'Reilly.
- أسرار جافا سكريبت النينجا من قبل جون Resig والدب Bibeault. دليل رائع لفهم أكثر عمقًا لجافا سكريبت.