{"id":"l-js-2-4","courseId":"javascript-fundamentals","lang":"ar","kind":"lesson","summary":"تتوقّع أين يمكن استخدام متغيّر ما (نطاق الكتلة ونطاق الدالة والنطاق العام)، وتتجنّب مزالق var القديمة، وتستخدم الـ closures لتمنح الدوال حالة خاصة تتذكّرها، مثل عدّاد للمعرّفات.","html":"<p>كل مصروف في Pocket يحتاج إلى معرّف (id) فريد كي تستطيع لاحقًا تعديله هو بالذات أو حذفه. والطريقة البديهية عدّاد في أعلى الملف:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"js\" data-index=\"0\"><div class=\"code-head\"><span class=\"code-title\">JavaScript</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"نسخ الكود\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>نسخ</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">let</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> nextId </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> 1</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">;</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">function</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\"> createId</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">() {</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">  return</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> `exp-${</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">nextId</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">++</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">}`</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">;</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">}</span></span></code></pre></div>\n<p>هذا يعمل، إلى أن يأتي جزء آخر من البرنامج، ربما ميزة “إعادة التعيين” كُتبت بعد أشهر، فيضبط <code dir=\"ltr\">nextId = 1</code> من جديد، فتحصل على مصروفين باسم <code dir=\"ltr\">exp-1</code>. العدّاد مرئي في كل مكان، لذلك يستطيع أي شيء أن يُفسده. ولكي تحميه، تحتاج إلى فهم <strong>النطاق</strong> (scope): قواعد المكان الذي يمكن أن يُرى فيه اسم ما.</p>\n<h2 id=\"sec-1\" tabindex=\"-1\">الكتل والدوال والنطاق العام</h2>\n<p>كل زوج من الأقواس المعقوفة يُنشئ <strong>كتلة</strong> (block)، وأسماء <code dir=\"ltr\">let</code> و<code dir=\"ltr\">const</code> تعيش في الكتلة التي صُرّح بها فيها. والدوال تُنشئ نطاقًا أيضًا. أما الأسماء المُصرَّح بها خارج كل الكتل والدوال فهي في <strong>النطاق العام</strong> (global scope)، ومرئية في كل مكان.</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"js\" data-run=\"js\" data-index=\"1\"><div class=\"code-head\"><span class=\"code-title\">JavaScript</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn code-run\" data-action=\"run\" data-lang=\"js\" data-index=\"1\" aria-label=\"شغّل هذا المثال\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><path d=\"M5 5a2 2 0 0 1 3.008-1.728l11.997 6.998a2 2 0 0 1 .003 3.458l-12 7A2 2 0 0 1 5 19z\"/></svg><span>تشغيل</span></button><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"نسخ الكود\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>نسخ</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">const</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> appName</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> \"Pocket\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">;           </span><span style=\"color:#6E7781;--shiki-dark:#8B949E\">// global</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">function</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\"> describe</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#953800;--shiki-dark:#FFA657\">amount</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">) {</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">  const</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> size</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> amount </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">></span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> 10000</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> ?</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> \"big\"</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> :</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> \"small\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">;   </span><span style=\"color:#6E7781;--shiki-dark:#8B949E\">// function scope</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">  if</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> (size </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">===</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> \"big\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">) {</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">    const</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> warning</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> \"Think twice\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">;  </span><span style=\"color:#6E7781;--shiki-dark:#8B949E\">// block scope</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">    console.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">log</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(appName, size, warning);</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  }</span></span>\n<span class=\"line\"><span style=\"color:#6E7781;--shiki-dark:#8B949E\">  // console.log(warning) here would throw a ReferenceError</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">}</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">describe</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">12000</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">);</span></span></code></pre><div class=\"run-output\" data-run-output=\"1\" hidden></div></div>\n<p>حين يصادف المحرّك اسمًا، يبحث عنه في النطاق الحالي أولًا. فإن لم يجده هناك، بحث في النطاق المحيط، ثم في الذي يحيط به، وصولًا إلى النطاق العام. وإن لم يجده بعد ذلك، تحصل على <code dir=\"ltr\">ReferenceError: … is not defined</code>. والبحث يتّجه <strong>إلى الخارج</strong> فقط: الكود داخل الدالة يستطيع قراءة <code dir=\"ltr\">appName</code> العام، لكن الكود خارج الدالة لا يستطيع أبدًا رؤية <code dir=\"ltr\">size</code>.</p>\n<p>ولهذا جُعل سطر <code dir=\"ltr\">warning</code> تعليقًا. فالكتلة التي صرّحت بـ <code dir=\"ltr\">warning</code> انتهت، فاختفى الاسم.</p>\n<figure class=\"diagram\"><svg class=\"diagram-svg\" focusable=\"false\" viewBox=\"0 0 640 250\" role=\"img\" aria-labelledby=\"l-js-2-4-t1\"><defs><marker id=\"arrow-l-js-2-4-1\" viewBox=\"0 0 10 10\" refX=\"9\" refY=\"5\" markerWidth=\"7\" markerHeight=\"7\" orient=\"auto-start-reverse\"><path d=\"M0 0 L10 5 L0 10 z\" class=\"d-arrowhead\"/></marker></defs>\n  <title id=\"l-js-2-4-t1\">ثلاثة صناديق متداخلة: النطاق العام يحمل appName، ونطاق الدالة describe يحمل amount وsize، وكتلة if تحمل warning. سهم من الكتلة الأعمق يشير إلى الخارج ليوضّح أن البحث يتّجه من النطاقات الداخلية إلى الخارجية.</title>\n  <rect class=\"d-box\" x=\"20\" y=\"16\" width=\"600\" height=\"220\" rx=\"14\"/>\n  <text class=\"d-label-strong\" x=\"40\" y=\"44\">النطاق العام</text>\n  <text class=\"d-code\" x=\"470\" y=\"44\">appName</text>\n  <rect class=\"d-box-accent\" x=\"50\" y=\"62\" width=\"540\" height=\"156\" rx=\"12\"/>\n  <text class=\"d-label-strong\" x=\"70\" y=\"90\">function describe</text>\n  <text class=\"d-code\" x=\"410\" y=\"90\">amount, size</text>\n  <rect class=\"d-box-primary\" x=\"80\" y=\"110\" width=\"300\" height=\"88\" rx=\"10\"/>\n  <text class=\"d-label-strong\" x=\"100\" y=\"138\">كتلة if</text>\n  <text class=\"d-code\" x=\"100\" y=\"172\">warning</text>\n  <path class=\"d-arrow\" d=\"M380 160 L460 160 L460 102\" marker-end=\"url(#arrow-l-js-2-4-1)\"/>\n  <path class=\"d-arrow\" d=\"M470 102 L520 56\" marker-end=\"url(#arrow-l-js-2-4-1)\"/>\n  <text class=\"d-label-muted\" x=\"520\" y=\"170\" text-anchor=\"middle\">ابحث إلى الخارج</text>\n</svg><figcaption>النطاقات تتداخل، والبحث يتّجه إلى الخارج</figcaption></figure>\n<p>إذا صرّح نطاق داخلي باسم موجود أصلًا في الخارج، فإن الاسم الداخلي <strong>يحجب</strong> (shadows) الخارجي: داخل الكتلة، يعني الاسم المتغيّر الداخلي. هذا مسموح، لكن وجود شيئين مختلفين باسم واحد يجعل الكود صعب القراءة، فاختر أسماء مختلفة.</p>\n<h2 id=\"sec-2\" tabindex=\"-1\">مشكلة var</h2>\n<p>قبل عام 2015، لم يكن في JavaScript إلا <code dir=\"ltr\">var</code>، و<code dir=\"ltr\">var</code> تتجاهل الكتل: إنها مرئية في الدالة التي هي فيها بالكامل. وهذا يُنتج خطأً شهيرًا. هنا تجدول الحلقة ثلاث دوال لتُنفَّذ بعد لحظة باستخدام <code dir=\"ltr\">setTimeout</code>:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"js\" data-run=\"js\" data-index=\"2\"><div class=\"code-head\"><span class=\"code-title\">JavaScript</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn code-run\" data-action=\"run\" data-lang=\"js\" data-index=\"2\" aria-label=\"شغّل هذا المثال\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><path d=\"M5 5a2 2 0 0 1 3.008-1.728l11.997 6.998a2 2 0 0 1 .003 3.458l-12 7A2 2 0 0 1 5 19z\"/></svg><span>تشغيل</span></button><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"نسخ الكود\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>نسخ</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">for</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> (</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">var</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> i </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> 0</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">; i </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">&#x3C;</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> 3</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">; i</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">++</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">) {</span></span>\n<span class=\"line\"><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">  setTimeout</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(() </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=></span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> console.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">log</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"var:\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, i), </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">0</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">);</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">}</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">for</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> (</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">let</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> j </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> 0</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">; j </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">&#x3C;</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> 3</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">; j</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">++</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">) {</span></span>\n<span class=\"line\"><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">  setTimeout</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(() </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=></span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> console.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">log</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"let:\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, j), </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">0</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">);</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">}</span></span></code></pre><div class=\"run-output\" data-run-output=\"2\" hidden></div></div>\n<p>حلقة <code dir=\"ltr\">var</code> تطبع <code dir=\"ltr\">3</code> ثلاث مرات، لأن هناك <code dir=\"ltr\">i</code> واحدًا فقط للحلقة كلها، وحين تُنفَّذ الدوال يكون قد بلغ 3. أما حلقة <code dir=\"ltr\">let</code> فتطبع 0 و1 و2، لأن <code dir=\"ltr\">let</code> تُنشئ <code dir=\"ltr\">j</code> جديدًا في كل دورة، وكل دالة تحتفظ بنسختها الخاصة. وتذكّر هذه الدوال للمتغيّرات هو بالضبط ما يُسمّى closure.</p>\n<aside class=\"callout callout-mistake\" role=\"note\"><p class=\"callout-title\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><path d=\"m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3\"/><path d=\"M12 9v4\"/><path d=\"M12 17h.01\"/></svg><span><span class=\"callout-kind\">خطأ شائع</span><span class=\"callout-sep\" aria-hidden=\"true\"> · </span>نسخ var من الشروحات القديمة</span></p>\n<p>كثير من كود JavaScript على الإنترنت أقدم من <code dir=\"ltr\">let</code> و<code dir=\"ltr\">const</code>. حين تنسخ مقتطفًا يستخدم <code dir=\"ltr\">var</code>، غيّره إلى <code dir=\"ltr\">const</code> (أو <code dir=\"ltr\">let</code> إن كان يُعاد إسناده)، وتأكّد أن لا شيء خارج الكتلة كان يعتمد على هذا التسرّب.</p>\n</aside>\n<h2 id=\"sec-3\" tabindex=\"-1\">الـ Closures: دوال تتذكّر</h2>\n<p>الدالة التي تُنشأ داخل دالة أخرى تستطيع استخدام متغيّرات الدالة الخارجية. والمفاجأة أنها تحتفظ بهذا الوصول <strong>بعد أن تنتهي الدالة الخارجية</strong>:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"js\" data-run=\"js\" data-index=\"3\"><div class=\"code-head\"><span class=\"code-title\">JavaScript</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn code-run\" data-action=\"run\" data-lang=\"js\" data-index=\"3\" aria-label=\"شغّل هذا المثال\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><path d=\"M5 5a2 2 0 0 1 3.008-1.728l11.997 6.998a2 2 0 0 1 .003 3.458l-12 7A2 2 0 0 1 5 19z\"/></svg><span>تشغيل</span></button><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"نسخ الكود\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>نسخ</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">function</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\"> createIdGenerator</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#953800;--shiki-dark:#FFA657\">prefix</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">) {</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">  let</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> count </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> 0</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">;</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">  return</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> () </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=></span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> {</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">    count</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">++</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">;</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">    return</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> `${</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">prefix</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">}-${</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">count</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">}`</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">;</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  };</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">}</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">const</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> nextExpenseId</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\"> createIdGenerator</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"exp\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">);</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">const</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> nextCategoryId</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\"> createIdGenerator</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"cat\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">);</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">console.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">log</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">nextExpenseId</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">());</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">console.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">log</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">nextExpenseId</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">());</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">console.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">log</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">nextCategoryId</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">());</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">console.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">log</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">nextExpenseId</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">());</span></span></code></pre><div class=\"run-output\" data-run-output=\"3\" hidden></div></div>\n<p>تتبّع المحرّك. استدعاء <code dir=\"ltr\">createIdGenerator(&quot;exp&quot;)</code> يُنشئ <code dir=\"ltr\">prefix</code> و<code dir=\"ltr\">count</code> جديدين، ويبني الدالة السهمية، ويُرجعها. في العادة تختفي متغيّرات الدالة حين تنتهي، لكن الدالة السهمية ما زالت تشير إلى <code dir=\"ltr\">count</code> و<code dir=\"ltr\">prefix</code>، فيبقيان حيّين، ملتصقين بها. وهذا الاقتران بين دالة والمتغيّرات التي وُلدت بجوارها هو ما يُسمّى <strong>closure</strong>.</p>\n<p>كل استدعاء لـ <code dir=\"ltr\">createIdGenerator</code> يُنشئ مجموعة جديدة منفصلة من المتغيّرات. ولهذا يبدأ <code dir=\"ltr\">nextCategoryId()</code> من 1، وتستمر معرّفات المصاريف من حيث توقّفت.</p>\n<p>والآن انظر إلى الأمان الذي كسبته. لا يوجد <code dir=\"ltr\">count</code> عام. لا يستطيع أي كود آخر قراءته، ولا تصفيره، ولا ضبطه على قيمة مكرّرة. والطريقة الوحيدة للتأثير فيه هي استدعاء الدالة التي أُعطيت لك، وهي لا تستطيع إلا أن تدفعه إلى الأمام. هذه هي الطريقة المعتمدة لمنح دالة <strong>حالة خاصة</strong> (private state) في JavaScript.</p>\n<aside class=\"callout callout-why\" role=\"note\"><p class=\"callout-title\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><circle cx=\"12\" cy=\"12\" r=\"10\"/><circle cx=\"12\" cy=\"12\" r=\"6\"/><circle cx=\"12\" cy=\"12\" r=\"2\"/></svg><span><span class=\"callout-kind\">لماذا يهمّ هذا</span><span class=\"callout-sep\" aria-hidden=\"true\"> · </span>لماذا للـ closures كل هذه الأهمية</span></p>\n<p>نادرًا ما ستكتب كلمة “closure” في الكود، لكنك ستستخدم واحدًا كل يوم. كل callback يقرأ متغيّرًا من الكود المحيط، وكل معالج نقرات في القسم 4 يحدّث قائمة، وكل دالة تتذكّر إعدادًا ما، هو closure. حين “يتذكّر” شيء ما قيمة، فهذه هي الآلية.</p>\n</aside>\n<p>يستطيع الـ closure أن يحمي أكثر من مجرّد عدّاد. هذه ميزانية لا يمكن أن تنقص إلا عبر الدالة التي تسلّمها:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"js\" data-run=\"js\" data-index=\"4\"><div class=\"code-head\"><span class=\"code-title\">JavaScript</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn code-run\" data-action=\"run\" data-lang=\"js\" data-index=\"4\" aria-label=\"شغّل هذا المثال\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><path d=\"M5 5a2 2 0 0 1 3.008-1.728l11.997 6.998a2 2 0 0 1 .003 3.458l-12 7A2 2 0 0 1 5 19z\"/></svg><span>تشغيل</span></button><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"نسخ الكود\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>نسخ</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">function</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\"> createBudget</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#953800;--shiki-dark:#FFA657\">limitCents</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">) {</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">  let</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> remaining </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> limitCents;</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">  return</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> (</span><span style=\"color:#953800;--shiki-dark:#FFA657\">spentCents</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">) </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=></span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> {</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">    remaining </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">-=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> spentCents;</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">    return</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> remaining;</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  };</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">}</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">const</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> spend</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\"> createBudget</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">2000</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">);</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">console.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">log</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">spend</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">450</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">));</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">console.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">log</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">spend</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">1220</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">));</span></span></code></pre><div class=\"run-output\" data-run-output=\"4\" hidden></div></div>\n<p>اكتمل القسم 2: تستطيع الآن اتخاذ القرارات، وتكرار العمل، وحزمه في دوال لها حالتها الخاصة. وينتقل القسم 3 إلى البيانات، بدءًا بالقائمة التي كنت تمرّ عليها: المصفوفة.</p>\n","toc":[{"id":"sec-1","text":"الكتل والدوال والنطاق العام","level":2},{"id":"sec-2","text":"مشكلة var","level":2},{"id":"sec-3","text":"الـ Closures: دوال تتذكّر","level":2}],"takeaways":["النطاق (scope) هو المكان الذي يمكن فيه استخدام اسم ما: <code dir=\"ltr\">let</code> و<code dir=\"ltr\">const</code> تعيشان في أقرب كتلة <code dir=\"ltr\">{ }</code>، والبحث عن الأسماء يتّجه إلى الخارج، لا إلى الداخل أبدًا.","الاسم الداخلي الذي يحمل اسم متغيّر خارجي “يحجبه” (shadowing) داخل النطاق الداخلي، وهذا مسموح لكن من السهل أن يُساء فهمه.","<code dir=\"ltr\">var</code> تتجاهل الكتل ويمتد نطاقها إلى الدالة كلها، وهذا يسبّب أخطاء كلاسيكية؛ استخدم <code dir=\"ltr\">let</code> و<code dir=\"ltr\">const</code>.","الـ closure دالة تحتفظ بإمكانية الوصول إلى متغيّرات النطاق الذي أُنشئت فيه، حتى بعد أن تكون الدالة الخارجية قد انتهت.","كل استدعاء للدالة الخارجية يُنشئ مجموعة جديدة من المتغيّرات، لذلك يملك كل closure تُرجعه حالته الخاصة."],"further":[{"title":"Closures (MDN)","url":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Closures"},{"title":"Scope (MDN glossary)","url":"https://developer.mozilla.org/en-US/docs/Glossary/Scope"},{"title":"var (MDN)","url":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/var"}],"quiz":[{"q":"<p>ماذا يحدث حين يُنفَّذ هذا الكود؟</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"js\" data-index=\"0\"><div class=\"code-head\"><span class=\"code-title\">JavaScript</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"نسخ الكود\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>نسخ</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">function</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\"> check</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#953800;--shiki-dark:#FFA657\">amount</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">) {</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">  if</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> (amount </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">></span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> 100</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">) {</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">    const</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> label</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> \"big\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">;</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  }</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">  return</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> label;</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">}</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">console.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">log</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">check</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">500</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">));</span></span></code></pre></div>\n","options":[{"text":"يطبع <code dir=\"ltr\">big</code>.","why":"صُرّح بـ <code dir=\"ltr\">label</code> باستخدام <code dir=\"ltr\">const</code> داخل كتلة <code dir=\"ltr\">if</code>، لذلك لا وجود له خارج هذين القوسين."},{"text":"يطبع <code dir=\"ltr\">undefined</code>.","why":"<code dir=\"ltr\">undefined</code> هي ما تعطيه <code dir=\"ltr\">var</code> حين تُتخطّى كتلة <code dir=\"ltr\">if</code>، كما في <code dir=\"ltr\">check(50)</code>؛ بل إن <code dir=\"ltr\">var</code> مع 500 كانت ستطبع <code dir=\"ltr\">big</code>. أما مع <code dir=\"ltr\">const</code> فالاسم لا وجود له خارج كتلته، لذلك استخدامه هناك خطأ."},{"text":"يرمي <code dir=\"ltr\">ReferenceError: label is not defined</code>.","why":"صحيح. نطاق الكتلة يعني أن <code dir=\"ltr\">label</code> يختفي عند القوس المعقوف الذي يُغلق <code dir=\"ltr\">if</code>، و<code dir=\"ltr\">return label</code> لا تستطيع رؤيته."}],"answer":2},{"q":"<p>ماذا يطبع هذا الكود؟</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"js\" data-index=\"1\"><div class=\"code-head\"><span class=\"code-title\">JavaScript</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"نسخ الكود\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>نسخ</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">function</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\"> makeCounter</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">() {</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">  let</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> count </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> 0</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">;</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">  return</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> () </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=></span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> {</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">    count</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">++</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">;</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">    return</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> count;</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  };</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">}</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">const</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> a</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\"> makeCounter</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">();</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">const</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> b</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\"> makeCounter</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">();</span></span>\n<span class=\"line\"><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">a</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(); </span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">a</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">();</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">console.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">log</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">a</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(), </span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">b</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">());</span></span></code></pre></div>\n","options":[{"text":"<code dir=\"ltr\">3 1</code>","why":"صحيح. كل استدعاء لـ <code dir=\"ltr\">makeCounter</code> يُنشئ <code dir=\"ltr\">count</code> خاصًا به. استُدعيت <code dir=\"ltr\">a</code> ثلاث مرات، و<code dir=\"ltr\">b</code> مرة واحدة."},{"text":"<code dir=\"ltr\">3 4</code>","why":"هذا يتطلّب <code dir=\"ltr\">count</code> واحدًا مشتركًا. لكن كل استدعاء لـ <code dir=\"ltr\">makeCounter</code> يُنشئ واحدًا منفصلًا."},{"text":"<code dir=\"ltr\">1 1</code>","why":"<code dir=\"ltr\">count</code> لا يُعاد تصفيره مع كل استدعاء للدالة الداخلية؛ فالـ closure يُبقي المتغيّر نفسه حيًا بين الاستدعاءات."},{"text":"<code dir=\"ltr\">0 0</code>","why":"كل استدعاء للدالة المُعادة ينفّذ <code dir=\"ltr\">count++</code> قبل الإرجاع، لذلك يُرجع الاستدعاء الأول 1 أصلًا."}],"answer":0},{"q":"<p>لماذا يُعدّ مولّد المعرّفات المبني على closure أكثر أمانًا من <code dir=\"ltr\">let nextId = 1</code> عام؟</p>","options":[{"text":"الـ closures تعمل أسرع من المتغيّرات العامة.","why":"السرعة ليست المقصود والفرق لا يُذكر. الفائدة في التحكّم بمن يستطيع تغيير القيمة."},{"text":"لا يستطيع أي كود خارج المولّد قراءة العدّاد أو تغييره، فلا يمكن تصفير المعرّفات أو تكرارها عن طريق الخطأ.","why":"صحيح. العدّاد يعيش داخل الـ closure فقط، والطريقة الوحيدة للتأثير فيه هي استدعاء الدالة التي أرجعتها."},{"text":"المتغيّرات العامة تُحذف بعد كل استدعاء دالة.","why":"المتغيّرات العامة تعيش ما دامت الصفحة موجودة. وهذه بالضبط المشكلة، لأن أي كود يستطيع تغييرها في أي وقت."}],"answer":1}],"exercise":{"kind":"web","mode":"js","starter":{"js":"// A global counter: any code can break it. Move it into a closure.\nlet count = 0;\n\nfunction createIdGenerator(prefix) {\n  return () => {\n    count++;\n    return `${prefix}-${count}`;\n  };\n}\n\n// Return a function spend(cents) that subtracts from a private\n// remaining amount and returns what is left\nfunction createBudget(limitCents) {\n}\n\nconst nextId = createIdGenerator(\"exp\");\nconsole.log(nextId(), nextId());\n"},"solution":{"js":"function createIdGenerator(prefix) {\n  let count = 0;\n  return () => {\n    count++;\n    return `${prefix}-${count}`;\n  };\n}\n\nfunction createBudget(limitCents) {\n  let remaining = limitCents;\n  return (spentCents) => {\n    remaining -= spentCents;\n    return remaining;\n  };\n}\n\nconst nextId = createIdGenerator(\"exp\");\nconsole.log(nextId(), nextId());\n"},"checks":[{"id":"ids-sequence","test":"const gen = createIdGenerator('x');\nreturn gen() === 'x-1' && gen() === 'x-2' && gen() === 'x-3';\n"},{"id":"ids-independent","test":"const a = createIdGenerator('a');\nconst b = createIdGenerator('b');\na(); a();\nreturn a() === 'a-3' && b() === 'b-1';\n"},{"id":"no-global-count","test":"return typeof count === 'undefined';\n"},{"id":"budget","test":"const spend = createBudget(2000);\nif (typeof spend !== 'function') return false;\nreturn spend(450) === 1550 && spend(1220) === 330 && spend(500) === -170;\n"},{"id":"budget-independent","test":"const s1 = createBudget(1000);\nconst s2 = createBudget(5000);\ns1(400);\nreturn s2(100) === 4900 && s1(100) === 500;\n"}],"title":"عدّادات خاصة باستخدام الـ closures","prompt":"<p>مولّد المعرّفات في كود البداية يتشارك <code dir=\"ltr\">count</code> عامًا واحدًا، فيتداخل مولّدان معًا ويستطيع أي كود تصفيره. أعِد كتابة <code dir=\"ltr\">createIdGenerator(prefix)</code> بحيث يملك كل مولّد <strong>عدّاده الخاص</strong>، ولا يبقى أي <code dir=\"ltr\">count</code> عام: أول معرّف من أي مولّد جديد هو دائمًا <code dir=\"ltr\">prefix-1</code>.</p>\n<p>ثم اكتب <code dir=\"ltr\">createBudget(limitCents)</code>، التي تُرجع دالة <code dir=\"ltr\">spend(cents)</code>. كل استدعاء يطرح <code dir=\"ltr\">cents</code> من مبلغ متبقٍّ خاص (يبدأ من <code dir=\"ltr\">limitCents</code>) ويُرجع ما تبقّى. ويمكن أن يصبح سالبًا.</p>\n","hints":["المتغيّرات المُصرَّح بها داخل <code dir=\"ltr\">createIdGenerator</code> تُنشأ من جديد مع كل استدعاء. انقل <code dir=\"ltr\">let count = 0;</code> إلى هناك واحذف المتغيّر العام.","لـ <code dir=\"ltr\">createBudget</code> الشكل نفسه: صرّح بـ <code dir=\"ltr\">let remaining = limitCents;</code>، ثم <code dir=\"ltr\">return (cents) =&gt; { … };</code>.","داخل الدالة المُعادة: <code dir=\"ltr\">remaining -= cents; return remaining;</code>."],"explanation":"<p>التصريح بـ <code dir=\"ltr\">count</code> داخل <code dir=\"ltr\">createIdGenerator</code> يعني أن كل استدعاء يُنشئ متغيّرًا جديدًا، وأن الدالة السهمية المُعادة من ذلك الاستدعاء تحتفظ بنسختها الخاصة منه. ولهذا لم يعد المولّدان يتداخلان، ولهذا يكون أول معرّف دائمًا 1. وبعد حذف المتغيّر العام، لا يستطيع أي شيء خارج الـ closure لمس العدّاد. وتستخدم <code dir=\"ltr\">createBudget</code> النمط نفسه: يبقى <code dir=\"ltr\">remaining</code> حيًا بين استدعاءات <code dir=\"ltr\">spend</code> لأن الدالة المُعادة ما زالت تشير إليه، ومع ذلك لا يستطيع أي كود آخر قراءته أو تغييره إلا عبر <code dir=\"ltr\">spend</code>.</p>\n","checkLabels":{"ids-sequence":"المولّد يُرجع prefix-1 ثم prefix-2 ثم prefix-3…","ids-independent":"مولّدان يحتفظ كلٌّ منهما بعدّه المنفصل","no-global-count":"لم يعد هناك متغيّر <code dir=\"ltr\">count</code> عام","budget":"<code dir=\"ltr\">spend</code> تطرح وتُرجع ما تبقّى","budget-independent":"ميزانيتان تحتفظ كلٌّ منهما بمبلغها المتبقّي المنفصل"}},"words":622,"readMinutes":4}