جافاسكريبت - لماذا تصبح بعض المتغيرات المعلنة باستخدام let داخل دالة متاحة في دالة أخرى، بينما يؤدي البعض الآخر إلى خطأ مرجعي؟


لا أستطيع أن أفهم لماذا تتصرف المتغيرات بشكل غريب عندما يتم إعلانها داخل دالة.

  1. في الدالة first أعلن باستخدام let المتغيرات b و c بالقيمة 10:

    b = c = 10;
    

    في الدالة الثانية أُظهر:

    b + ", " + c
    

    وهذا يُظهر:

    10, 10
    
  2. أيضًا في دالة first أُعلن عن a بالقيمة 10:

    let a = b = c = 10;
    

    ولكن في الدالة الثانية يظهر خطأ:

    لا يمكن العثور على المتغير: a

  3. الآن في الدالة 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
}