جافاسكريبت - لماذا تصبح بعض المتغيرات المعلنة باستخدام let داخل دالة متاحة في دالة أخرى، بينما يؤدي البعض الآخر إلى خطأ مرجعي؟
لا أستطيع أن أفهم لماذا تتصرف المتغيرات بشكل غريب عندما يتم إعلانها داخل دالة.
في الدالة
firstأعلن باستخدامletالمتغيراتbوcبالقيمة 10:b = c = 10;في الدالة
الثانيةأُظهر:b + ", " + cوهذا يُظهر:
10, 10أيضًا في دالة
firstأُعلن عنaبالقيمة 10:let a = b = c = 10;ولكن في الدالة
الثانيةيظهر خطأ:لا يمكن العثور على المتغير:
aالآن في الدالة
firstأُعلن عنdبالقيمة 20:var d = 20;ولكن في الدالة
الثانيةيظهر نفس الخطأ كما من قبل، ولكن مع المتغيرd:لا يمكن العثور على المتغير:
d
مثال :
function first() {
let a = b = c = 10;
var d = 20;
second();
}
function second() {
console.log(b + ", " + c); //shows "10, 10"
try{ console.log(a); } // Rreference error
catch(e){ console.error(e.message) }
try{ console.log(d); } // Reference error
catch(e){ console.error(e.message) }
}
first()الإجابة رقم 1
ذلك لأنك في الواقع تقول:
c = 10;
b = c;
let a = b;
وليس ما تعتقد أنك تقوله، وهو:
let a = 10;
let b = 10;
let c = 10;
ستلاحظ أنه بغض النظر عن عدد المتغيرات التي تضيفها إلى سلسلتك، فإن المتغير الأول فقط (أ) هو الذي يسبب الخطأ.
هذا لأن "let" تجعل نطاق متغيرك محصوراً في الكتلة (أو، "محلياً"، بمعنى أكثر أو أقل "في الأقواس") التي تعرّفه فيها.
إذا قمت بإعلان متغير دون استخدام "let"، فإن ذلك يجعل نطاق المتغير عالميًا.
إذاً، في الدالة حيث تقوم بتعيين متغيراتك، يحصل كل شيء على القيمة 10 (يمكنك رؤية هذا في المتتبع إذا وضعت نقطة توقف). إذا وضعت سجل وحدة التحكم للمتغيرات a و b و c في تلك الدالة الأولى، فكل شيء على ما يرام.
ولكن بمجرد أن تخرج من تلك الدالة، فإن الأولى (أ) -- ومرة أخرى، ضع في اعتبارك، من الناحية التقنية في ترتيب التعيين، فهي الأخيرة -- "تختفي" (مرة أخرى، يمكنك أن ترى هذا في مصحح الأخطاء إذا قمت بتعيين نقطة توقف في الدالة الثانية)، بينما الأخرىان (أو مهما كان عدد الإضافات التي تقوم بها) لا تزال متاحة.
هذا لأن "let" تنطبق فقط على المتغير الأول (أي ينطبق فقط على النطاق المحلي) -- مرة أخرى، الذي يُعتبر من الناحية التقنية آخر متغير يتم الإعلان عنه وتعيين قيمة له -- في السلسلة. البقية من الناحية التقنية ليس أمامهم "let". لذلك يتم الإعلان عن تلك المتغيرات من الناحية التقنية على مستوى عالمي (أي على الكائن العالمي)، ولهذا السبب تظهر في دالتك الثانية.
جربها: أزل كلمة المرور "let". جميع متغيراتك ستكون متاحة الآن.
"var" له تأثير مشابه على نطاق المحلي، ولكنه يختلف في الطريقة التي يتم بها "رفع" المتغير، وهو شيء يجب أن تفهمه بالتأكيد، لكنه غير مرتبط مباشرة بسؤالك.
(على فكرة، هذا السؤال قد يربك عدد كافٍ من مطوري جافا سكريبت المحترفين ليجعله سؤالًا جيدًا).
أقترح بشدة أن تقضي وقتًا في التعرف على الاختلافات في كيفية إعلان المتغيرات في جافاسكريبت: بدون كلمة مفتاحية، باستخدام "let"، وباستخدام "var".
الإجابة رقم 2
في الدالة first()، يتم إنشاء المتغيرات b و c بشكل مباشر، بدون استخدام var أو let.
let a = b = c = 10; // b and c are created on the fly
مختلف عن
let a = 10, b = 10, c = 10; // b and c are created using let (note the ,)
تصبح ضمنية على مستوى العالم. لهذا السبب هي متاحة في second()
من الوثائق
تعيين قيمة لمتغير غير مُعلن يُنشئه ضمنيًا كمتغير عام (يصبح خاصية للكائن العام) عندما يتم تنفيذ التعيين.
لتجنب ذلك، يمكنك استخدام "use strict" الذي سيعرض أخطاء عند استخدام متغير غير مُعلن عنه
"use strict"; // <-------------- check this
function first() {
/*
* With "use strict" c is not defined.
* (Neither is b, but since the line will be executed from right to left,
* the variable c will cause the error and the script will stop)
* Without, b and c become globals, and then are accessible in other functions
*/
let a = b = c = 10;
var d = 20;
second();
}
function second() {
console.log(b + ", " + c); //reference error
console.log(a); //reference error
console.log(d); //reference error
}
first();الإجابة رقم 3
قبل أن نطلق على الأمور غريبة، لنبدأ بمعرفة بعض الأساسيات أولاً:
var و let كلاهما يُستخدم لإعلان المتغيرات في جافاسكريبت. على سبيل المثال،
var one = 1;
let two = 2;
يمكن أيضًا إعلان المتغيرات دون استخدام var أو let. على سبيل المثال،
three = 3;
الآن الفرق بين الأساليب المذكورة أعلاه هو:
var له نطاق دالة
و
let له نطاق الكتلة.
بينما يصبح نطاق المتغيرات المعلنة دون استخدام الكلمة المفتاحية
var/letعالميًا بغض النظر عن مكان إعلانها.يمكن الوصول إلى المتغيرات العامة من أي مكان في صفحة الويب (غير مستحسن لأنها يمكن أن تتغير عن طريق الخطأ).
الآن وفقًا لهذه المفاهيم، دعونا نلقي نظرة على الكود المعني:
function first() {
let a = b = c = 10;
/* The above line means:
let a=10; // Block scope
b=10; // Global scope
c=10; // Global scope
*/
var d = 20; // Function scope
second();
}
function second() {
alert(b + ", " + c); // Shows "10, 10" //accessible because of global scope
alert(a); // Error not accessible because block scope has ended
alert(d); // Error not accessible because function scope has ended
}
