{"checkpoints":{"s-js-1":[{"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\">let</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> total </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> \"0\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">;</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">total </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> total </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">+</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\">total </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> total </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">+</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> 1220</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\">(total);</span></span></code></pre></div>\n","options":[{"text":"<code dir=\"ltr\">1670</code>","why":"هذا يحتاج إلى أرقام في الطرفين. يبدأ <code dir=\"ltr\">total</code> نصًا هو “0”، لذلك تلصق كل <code dir=\"ltr\">+</code> النصوص بدلًا من أن تجمع."},{"text":"<code dir=\"ltr\">&quot;1670&quot;</code>","why":"لا تحدث أي عملية جمع هنا. فـ <code dir=\"ltr\">+</code> مع نص في أحد طرفيها تلصق الأجزاء نصًا."},{"text":"<code dir=\"ltr\">NaN</code>","why":"تأتي <code dir=\"ltr\">NaN</code> من عملية حسابية تفشل. ولصق النصوص لا يفشل أبدًا، فلا تظهر <code dir=\"ltr\">NaN</code>."},{"text":"<code dir=\"ltr\">04501220</code>","why":"صحيح. “0” + 450 تساوي “0450”، و&quot;0450&quot; + 1220 تساوي “04501220”. ابدأ المجموع بالرقم 0."}],"answer":3},{"q":"<p><code dir=\"ltr\">const count = 0;</code> ثم لاحقًا <code dir=\"ltr\">count++;</code> يرمي <code dir=\"ltr\">TypeError: Assignment to constant variable.</code> ما الإصلاح الصحيح؟</p>","options":[{"text":"صرّح به بـ <code dir=\"ltr\">var</code> كي يعمل في كل مكان.","why":"<code dir=\"ltr\">var</code> ستعمل، لكنها تجلب معها قواعد نطاق مربكة. و<code dir=\"ltr\">let</code> هي الطريقة الحديثة للسماح بإعادة الإسناد."},{"text":"صرّح به بـ <code dir=\"ltr\">let count = 0;</code> لأن القيمة يجب أن تتغيّر.","why":"صحيح. <code dir=\"ltr\">let</code> موجودة للأسماء التي يُعاد إسنادها؛ و<code dir=\"ltr\">const</code> للأسماء التي لا يُعاد إسنادها أبدًا."},{"text":"استبدل <code dir=\"ltr\">count++</code> بـ <code dir=\"ltr\">count = count + 1</code>.","why":"هذه أيضًا إعادة إسناد، فترفضها <code dir=\"ltr\">const</code> بالطريقة نفسها تمامًا."},{"text":"ضع الزيادة بين قوسين: <code dir=\"ltr\">(count)++</code>.","why":"الأقواس لا تغيّر ما تفعله التعليمة. إنها ما زالت تحاول إعادة إسناد ثابت."}],"answer":1},{"q":"<p>تعرض صفحة الدفع “Total does not match” لأن <code dir=\"ltr\">subtotal === 0.3</code> تساوي false بعد جمع <code dir=\"ltr\">0.1</code> و<code dir=\"ltr\">0.2</code>. ما أفضل إصلاح لتطبيق مالي؟</p>","options":[{"text":"قارن باستخدام <code dir=\"ltr\">==</code> بدلًا من <code dir=\"ltr\">===</code>.","why":"<code dir=\"ltr\">==</code> لا تضيف إلا تحويل الأنواع. والطرفان رقمان أصلًا، وهما مختلفان فعلًا بفارق ضئيل."},{"text":"قارن <code dir=\"ltr\">subtotal.toFixed(2) === 0.3</code>.","why":"<code dir=\"ltr\">toFixed</code> تُرجع نصًا، والنص لا يساوي الرقم مساواةً صارمة أبدًا، لذلك تكون النتيجة false دائمًا."},{"text":"خزّن كل مبلغ سنتاتٍ صحيحة (integers)، فتصبح <code dir=\"ltr\">10 + 20 === 30</code> صحيحة بالضبط.","why":"صحيح. الحساب بالأعداد الصحيحة دقيق، فلا تنحرف المجاميع أبدًا. ولا تحوّل إلى الدولار إلا عند العرض."},{"text":"استدعِ <code dir=\"ltr\">parseFloat(subtotal)</code> قبل المقارنة.","why":"<code dir=\"ltr\">subtotal</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\">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\"> \"  Rent \"</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\"> clean</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> label.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">trim</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">().</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">toLowerCase</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\">(label.</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">length</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, clean);</span></span></code></pre></div>\n","options":[{"text":"<code dir=\"ltr\">7 rent</code>","why":"صحيح. دوال النصوص تُرجع نصوصًا جديدة، فيحتفظ <code dir=\"ltr\">label</code> بحروفه السبعة، بما فيها المسافات، وتكون <code dir=\"ltr\">clean</code> هي “rent”."},{"text":"<code dir=\"ltr\">4 rent</code>","why":"<code dir=\"ltr\">trim()</code> لم تغيّر <code dir=\"ltr\">label</code>؛ بل أرجعت نصًا جديدًا خُزّن في <code dir=\"ltr\">clean</code>. وما زال <code dir=\"ltr\">label</code> بمسافاته."},{"text":"<code dir=\"ltr\">7 Rent</code>","why":"<code dir=\"ltr\">clean</code> تأتي من <code dir=\"ltr\">toLowerCase()</code>، لذلك حرفها الأول صغير."},{"text":"<code dir=\"ltr\">4 Rent</code>","why":"لا جزء منهما صحيح: <code dir=\"ltr\">label</code> لم يُمسّ (7 حروف) و<code dir=\"ltr\">clean</code> حُوّلت إلى أحرف صغيرة."}],"answer":0},{"q":"<p>مع <code dir=\"ltr\">const cents = 1250;</code> و<code dir=\"ltr\">const item = &quot;lunch&quot;;</code>، أيّ تعبير يُنتج بالضبط <code dir=\"ltr\">Spent $12.50 on lunch</code>؟</p>","options":[{"text":"<code dir=\"ltr\">&quot;Spent $&quot; + cents / 100 + &quot; on &quot; + item</code>","why":"الأرقام تُسقط الأصفار في آخرها حين تتحوّل إلى نص، لذلك يعطي هذا “Spent $12.5 on lunch”."},{"text":"<code dir=\"ltr\">`Spent ${cents / 100} on ${item}`</code>","why":"هذا ينقصه رمز الدولار، ومثل الخيار الأول، يعرض 12.5 بدلًا من 12.50."},{"text":"<code dir=\"ltr\">'Spent $${(cents / 100).toFixed(2)} on ${item}'</code>","why":"العلامات المفردة تصنع نصًا عاديًا، فتُطبع أجزاء <code dir=\"ltr\">${...}</code> حرفيًا بدلًا من أن تُملأ."},{"text":"<code dir=\"ltr\">`Spent $${(cents / 100).toFixed(2)} on ${item}`</code>","why":"صحيح. علامات backtick تصنع template literal، و<code dir=\"ltr\">$</code> الأولى حرف عادي، و<code dir=\"ltr\">toFixed(2)</code> تحافظ على منزلتين عشريتين."}],"answer":3}],"s-js-2":[{"q":"<p>ماذا يطبع هذا الكود؟</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"js\" data-index=\"2\"><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\"> sizeLabel</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 style=\"color:#CF222E;--shiki-dark:#FF7B72\">return</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\">  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\"> 1000</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">) </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">return</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> \"huge\"</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\"> \"small\"</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\">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\">sizeLabel</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">5000</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">));</span></span></code></pre></div>\n","options":[{"text":"<code dir=\"ltr\">huge</code>","why":"أول <code dir=\"ltr\">if</code> تطابق 5000 أصلًا وتُرجع، فلا يُوصل أبدًا إلى فحص “huge”. ضع الفحص الأضيق أولًا."},{"text":"<code dir=\"ltr\">small</code>","why":"لا تُرجَع <code dir=\"ltr\">small</code> إلا حين لا يكون أيّ الشرطين صحيحًا، لكن 5000 أكبر من 100."},{"text":"<code dir=\"ltr\">big</code>","why":"صحيح، وهذا خطأ برمجي: <code dir=\"ltr\">return</code> تُنهي الدالة عند أول شرط صحيح، لذلك ترتيب الفحوص مهم."},{"text":"<code dir=\"ltr\">undefined</code>","why":"كل مسار في الدالة يُرجع نصًا، لذلك لا تكون النتيجة <code dir=\"ltr\">undefined</code> أبدًا."}],"answer":2},{"q":"<p>تعرض دالة “No note” حين يكون <code dir=\"ltr\">if (note)</code> خاطئًا. أيّ هذه الملاحظات تُعامَل على أنها مفقودة؟</p>","options":[{"text":"<code dir=\"ltr\">&quot;0&quot;</code>","why":"النص غير الفارغ truthy، حتى لو كان يحتوي على حرف الصفر."},{"text":"<code dir=\"ltr\">&quot; &quot;</code> (مسافة واحدة)","why":"المسافة حرف، لذلك النص ليس فارغًا ويُعدّ truthy."},{"text":"<code dir=\"ltr\">&quot;false&quot;</code>","why":"هذا نص من خمسة حروف، لا القيمة المنطقية <code dir=\"ltr\">false</code>، لذلك هو truthy."},{"text":"<code dir=\"ltr\">&quot;&quot;</code> (نص فارغ)","why":"صحيح. النص الفارغ إحدى القيم الثماني الـ falsy."}],"answer":3},{"q":"<p>ماذا يطبع هذا الكود؟</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"js\" data-index=\"3\"><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\"> 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\">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\"> 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\"> 5</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:#CF222E;--shiki-dark:#FF7B72\">  if</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\"> 3</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">) </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">break</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:#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\">(count);</span></span></code></pre></div>\n","options":[{"text":"<code dir=\"ltr\">3</code>","why":"صحيح. الدورات التي تكون فيها <code dir=\"ltr\">i</code> تساوي 0 و1 و2 تزيد <code dir=\"ltr\">count</code>؛ وحين تكون <code dir=\"ltr\">i</code> تساوي 3، تُنهي <code dir=\"ltr\">break</code> الحلقة قبل أن تُنفَّذ <code dir=\"ltr\">count++</code>."},{"text":"<code dir=\"ltr\">4</code>","why":"<code dir=\"ltr\">break</code> تُنفَّذ قبل <code dir=\"ltr\">count++</code> في الدورة التي تكون فيها <code dir=\"ltr\">i</code> تساوي 3، لذلك لا تُحتسب تلك الدورة."},{"text":"<code dir=\"ltr\">5</code>","why":"هذا صحيح لو لم توجد <code dir=\"ltr\">break</code>. لكن الحلقة تتوقّف مبكرًا."},{"text":"<code dir=\"ltr\">2</code>","why":"الدورة التي تكون فيها <code dir=\"ltr\">i</code> تساوي 0 تُحتسب أيضًا، لذلك تكتمل ثلاث دورات قبل <code dir=\"ltr\">break</code>."}],"answer":0},{"q":"<p>ماذا يطبع هذا الكود؟</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"js\" data-index=\"4\"><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\">const</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\"> add</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> (</span><span style=\"color:#953800;--shiki-dark:#FFA657\">a</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#953800;--shiki-dark:#FFA657\">b</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> 10</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">) </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=></span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> a </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">+</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> b;</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\">add</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">5</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">), </span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">add</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">5</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">0</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">), </span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">add</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">5</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">null</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">));</span></span></code></pre></div>\n","options":[{"text":"<code dir=\"ltr\">15 15 15</code>","why":"القيمة الافتراضية لا تُطبَّق إلا حين يكون الوسيط مفقودًا أو <code dir=\"ltr\">undefined</code>. أما 0 و<code dir=\"ltr\">null</code> فوسائط حقيقية."},{"text":"<code dir=\"ltr\">15 5 5</code>","why":"صحيح. الاستدعاء الأول وحده يستخدم القيمة الافتراضية. وفي الاستدعاء الأخير تُستخدم <code dir=\"ltr\">null</code>، وفي العمليات الحسابية تتحوّل إلى 0."},{"text":"<code dir=\"ltr\">15 5 15</code>","why":"تمرير <code dir=\"ltr\">null</code> لا يُفعّل القيمة الافتراضية؛ وحدها <code dir=\"ltr\">undefined</code> تفعل ذلك."},{"text":"<code dir=\"ltr\">15 15 5</code>","why":"تمرير 0 وسيط حقيقي، لذلك تكون <code dir=\"ltr\">b</code> تساوي 0، لا 10."}],"answer":1},{"q":"<p>ماذا يطبع هذا الكود؟</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"js\" data-index=\"5\"><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\"> makeLimiter</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#953800;--shiki-dark:#FFA657\">max</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\"> used </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:#CF222E;--shiki-dark:#FF7B72\">    if</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> (used </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">>=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> max) </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">return</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> false</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">;</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">    used</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:#0550AE;--shiki-dark:#79C0FF\"> true</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 style=\"color:#CF222E;--shiki-dark:#FF7B72\">const</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> canAdd</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\"> makeLimiter</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">2</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\">canAdd</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(), </span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">canAdd</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(), </span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">canAdd</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">());</span></span></code></pre></div>\n","options":[{"text":"<code dir=\"ltr\">true true true</code>","why":"الـ closure يتذكّر <code dir=\"ltr\">used</code> بين الاستدعاءات، لذلك يُبلغ الحدّ 2."},{"text":"<code dir=\"ltr\">false false false</code>","why":"أول استدعاءين يجدان <code dir=\"ltr\">used</code> أقل من <code dir=\"ltr\">max</code>، لذلك يُرجعان <code dir=\"ltr\">true</code>."},{"text":"<code dir=\"ltr\">true true false</code>","why":"صحيح. الـ closure يُبقي <code dir=\"ltr\">used</code> حيًا: تصبح 0 ثم 1 ثم 2، ويجد الاستدعاء الثالث أن الحدّ قد بُلغ."},{"text":"<code dir=\"ltr\">true false false</code>","why":"الحدّ 2، لذلك ينجح استدعاءان قبل أن تصبح <code dir=\"ltr\">used &gt;= max</code> صحيحة."}],"answer":2}],"s-js-3":[{"q":"<p><code dir=\"ltr\">const names = expenses.map((e) =&gt; { e.label.toUpperCase(); });</code> تعطي مصفوفة من <code dir=\"ltr\">undefined</code>. ما الإصلاح؟</p>","options":[{"text":"استخدم <code dir=\"ltr\">forEach</code> بدلًا من <code dir=\"ltr\">map</code>.","why":"<code dir=\"ltr\">forEach</code> نفسها تُرجع <code dir=\"ltr\">undefined</code>، فلن تحصل على أي مصفوفة أصلًا."},{"text":"احذف الأقواس المعقوفة، <code dir=\"ltr\">(e) =&gt; e.label.toUpperCase()</code>، أو أضف <code dir=\"ltr\">return</code> داخلها.","why":"صحيح. مع الأقواس المعقوفة يحتاج الـ callback إلى <code dir=\"ltr\">return</code> صريحة؛ وإلا فإنه يُرجع <code dir=\"ltr\">undefined</code> لكل عنصر."},{"text":"استخدم <code dir=\"ltr\">filter</code> بدلًا من <code dir=\"ltr\">map</code>.","why":"<code dir=\"ltr\">filter</code> تحتفظ بالعناصر أو تُسقطها؛ ولا تحوّلها. ومع هذا الـ callback ستُرجع مصفوفة فارغة."},{"text":"صرّح بـ <code dir=\"ltr\">names</code> باستخدام <code dir=\"ltr\">let</code> بدلًا من <code dir=\"ltr\">const</code>.","why":"طريقة تخزين النتيجة لا تغيّر ما تُرجعه <code dir=\"ltr\">map</code>."}],"answer":1},{"q":"<p>ماذا تُرجع <code dir=\"ltr\">[5, 25, 100].sort()</code>؟</p>","options":[{"text":"<code dir=\"ltr\">[5, 25, 100]</code>","why":"هذا يحتاج إلى دالة مقارنة مثل <code dir=\"ltr\">(a, b) =&gt; a - b</code>. الترتيب الافتراضي يقارن النصوص."},{"text":"<code dir=\"ltr\">[5, 100, 25]</code>","why":"بصفتها نصوصًا، تُرتَّب “1” قبل “2” و&quot;5&quot;، لذلك تأتي “100” أولًا."},{"text":"<code dir=\"ltr\">[25, 5, 100]</code>","why":"مقارنة النصوص تنظر إلى الحرف الأول أولًا: “1” &lt; “2” &lt; “5”."},{"text":"<code dir=\"ltr\">[100, 25, 5]</code>","why":"صحيح. دون دالة مقارنة، تُقارن الأرقام على أنها نصوص: “100” &lt; “25” &lt; “5”."}],"answer":3},{"q":"<p>ماذا يطبع هذا الكود؟</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"js\" data-index=\"6\"><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\">const</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> settings</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> { currency: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"USD\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, limits: { food: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">400</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\"> copy</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> { </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">...</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">settings };</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">copy.limits.food </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> 500</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">;</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">copy.currency </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> \"EUR\"</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\">(settings.currency, settings.limits.food);</span></span></code></pre></div>\n","options":[{"text":"<code dir=\"ltr\">USD 400</code>","why":"النشر سطحي: <code dir=\"ltr\">copy.limits</code> و<code dir=\"ltr\">settings.limits</code> هما الكائن الداخلي نفسه، لذلك يتغيّر حدّ الطعام لكليهما."},{"text":"<code dir=\"ltr\">EUR 500</code>","why":"<code dir=\"ltr\">currency</code> خاصية في المستوى الأعلى، نُسخت قيمتها إلى الكائن الجديد، لذلك تغيير <code dir=\"ltr\">copy.currency</code> يترك <code dir=\"ltr\">settings</code> كما هو."},{"text":"<code dir=\"ltr\">USD 500</code>","why":"صحيح. نُسخت خصائص المستوى الأعلى، لكن الكائن المتداخل <code dir=\"ltr\">limits</code> مشترك. استخدم <code dir=\"ltr\">structuredClone</code> لنسخة عميقة."},{"text":"<code dir=\"ltr\">EUR 400</code>","why":"الجزءان معكوسان: تغيير المستوى الأعلى يبقى في النسخة، والتغيير المتداخل مشترك."}],"answer":2},{"q":"<p>أيّ تعبير يُرجع خصمًا محفوظًا قيمته <code dir=\"ltr\">0</code> على أنه 0، ولا يستخدم 10 إلا حين لا يكون هناك خصم محفوظ؟</p>","options":[{"text":"<code dir=\"ltr\">user.discount ?? 10</code>","why":"صحيح. <code dir=\"ltr\">??</code> لا تلجأ إلى البديل إلا مع <code dir=\"ltr\">null</code> و<code dir=\"ltr\">undefined</code>، فيُحتفظ بالصفر الحقيقي."},{"text":"<code dir=\"ltr\">user.discount || 10</code>","why":"<code dir=\"ltr\">||</code> تلجأ إلى البديل مع كل قيمة falsy، و0 قيمة falsy، فيُستبدل الخصم المحفوظ بـ 10."},{"text":"<code dir=\"ltr\">user.discount ? user.discount : 10</code>","why":"العامل الثلاثي يفحص إن كانت القيمة truthy، تمامًا مثل <code dir=\"ltr\">||</code>، فيصبح 0 هو 10."},{"text":"<code dir=\"ltr\">user?.discount || 10</code>","why":"<code dir=\"ltr\">?.</code> تحمي فقط من غياب <code dir=\"ltr\">user</code>؛ أما <code dir=\"ltr\">||</code> فما زالت تتخلّص من الصفر."}],"answer":0},{"q":"<p>يعلّم Pocket المصاريف بأنها “reviewed” ويفحص <code dir=\"ltr\">isReviewed(id)</code> لكل عنصر في كل عرض. أيّ بنية أنسب لمعرّفات المصاريف المُراجَعة؟</p>","options":[{"text":"نص فيه المعرّفات مفصولة بفواصل.","why":"النصوص يجب البحث فيها حرفًا حرفًا، وقد تطابق أجزاءً من معرّفات أخرى، مثل “exp-1” داخل “exp-12”."},{"text":"Map من المعرّف إلى كائن المصروف الكامل.","why":"أنت تحتاج فقط إلى إجابة نعم/لا، لذلك تخزين المصاريف كاملة يكرّر بيانات موجودة أصلًا في القائمة."},{"text":"مصفوفة تضيف إليها كل معرّف بـ push.","why":"هذا يعمل، لكنه يسمح بالتكرار، و<code dir=\"ltr\">includes</code> تبحث في المصفوفة كلها مع كل فحص."},{"text":"Set من المعرّفات.","why":"صحيح. الـ Set تخزّن كل معرّف مرة واحدة، و<code dir=\"ltr\">has(id)</code> تجيب بسرعة دون بحث."}],"answer":3}],"s-js-4":[{"q":"<p>سكربت في <code dir=\"ltr\">&lt;head&gt;</code> ينفّذ <code dir=\"ltr\">document.querySelector(&quot;#total&quot;).textContent = &quot;$0.00&quot;;</code> ويرمي <code dir=\"ltr\">Cannot set properties of null</code>. والـ id مكتوب بشكل صحيح. ما الذي يُصلح ذلك؟</p>","options":[{"text":"استخدم <code dir=\"ltr\">getElementById(&quot;total&quot;)</code> بدلًا من ذلك.","why":"أي بحث يُرجع <code dir=\"ltr\">null</code> حين لا يكون العنصر موجودًا بعد. المشكلة في التوقيت، لا في الدالة."},{"text":"غلّف السطر بـ <code dir=\"ltr\">try</code>/<code dir=\"ltr\">catch</code>.","why":"هذا يُخفي الخطأ، ويظل المجموع لا يُضبط أبدًا."},{"text":"استخدم <code dir=\"ltr\">innerHTML</code> بدلًا من <code dir=\"ltr\">textContent</code>.","why":"الفشل يحدث قبل ضبط أيّ من الخاصيتين: العنصر نفسه <code dir=\"ltr\">null</code>."},{"text":"أضف <code dir=\"ltr\">defer</code> إلى وسم السكربت (أو حمّله بـ <code dir=\"ltr\">type=&quot;module&quot;</code>)، كي يُنفَّذ بعد تحليل الصفحة.","why":"صحيح. السكربت العادي في الـ head يُنفَّذ قبل وجود الـ body، فلا يكون العنصر موجودًا بعد."}],"answer":3},{"q":"<p>يضيف Pocket مستمع نقرات لكل <code dir=\"ltr\">&lt;li&gt;</code> عند تحميل الصفحة. والمصاريف المضافة لاحقًا لا تستجيب للنقرات. ما أفضل إصلاح؟</p>","options":[{"text":"ضع مستمعًا واحدًا على حاوية القائمة واستخدم <code dir=\"ltr\">event.target.closest(&quot;li&quot;)</code> لتجد العنصر المنقور.","why":"صحيح. النقرات على العناصر الجديدة تنتشر للأعلى إلى الحاوية نفسها، لذلك يغطّي التفويض العناصر المضافة في أي وقت."},{"text":"أضف المستمعين من جديد بعد ثانية باستخدام <code dir=\"ltr\">setTimeout</code>.","why":"العناصر المضافة بعد تلك الثانية ستفوتها أيضًا. التوقيت ليس المشكلة الحقيقية."},{"text":"ضع سمة <code dir=\"ltr\">onclick=&quot;...&quot;</code> في نص HTML لكل عنصر.","why":"هذا يخلط الكود بنصوص الوسوم، ويعرّضك لخطر الحقن، وما زال يتطلّب التعامل مع كل عنصر على حدة."},{"text":"أعِد إضافة المستمعين إلى كل عنصر كل ثانية باستخدام <code dir=\"ltr\">setInterval</code>.","why":"إنه يهدر العمل ويكدّس مستمعين مكرّرين على عناصر كان لها مستمع أصلًا."}],"answer":0},{"q":"<p>بعد الضغط على Enter في نموذج Pocket، يُعاد تحميل الصفحة ويظهر في الرابط <code dir=\"ltr\">?label=Tea</code>. ومعالج الحدث هو <code dir=\"ltr\">form.addEventListener(&quot;submit&quot;, () =&gt; { addExpense(); });</code>. ما السبب؟</p>","options":[{"text":"حدث <code dir=\"ltr\">submit</code> لا يُطلق إلا مع نقرات الفأرة، لا مع Enter.","why":"<code dir=\"ltr\">submit</code> يُطلق أيًّا كانت طريقة إرسال النموذج، ولهذا بالضبط تستمع إليه."},{"text":"يجب أن تكون <code dir=\"ltr\">addExpense</code> دالة <code dir=\"ltr\">async</code>.","why":"كونها غير متزامنة أو لا لا يؤثّر في إعادة التحميل. فالفعل الافتراضي للمتصفّح ما زال يحدث."},{"text":"معالج الحدث لا يستدعي <code dir=\"ltr\">event.preventDefault()</code> أبدًا، فيُجري المتصفّح الإرسال المعتاد.","why":"صحيح. الفعل الافتراضي يرسل النموذج ويحمّل صفحة جديدة. ألغِه في أول سطر من معالج الحدث."},{"text":"النموذج تنقصه السمة <code dir=\"ltr\">action</code>.","why":"دون <code dir=\"ltr\">action</code> يُرسَل النموذج إلى الصفحة الحالية، وهذه إعادة التحميل التي تراها. والإصلاح هو إلغاء الإرسال."}],"answer":2},{"q":"<p>بعد إعادة التحميل، تُرجع <code dir=\"ltr\">localStorage.getItem(&quot;pocket&quot;)</code> النص <code dir=\"ltr\">[object Object]</code>. ما الخطأ الذي وقع عند الحفظ؟</p>","options":[{"text":"كان التخزين ممتلئًا، فقُطعت البيانات.","why":"التخزين الممتلئ يرمي خطأ؛ ولا يستبدل البيانات بـ “[object Object]”."},{"text":"مُرّر الكائن إلى <code dir=\"ltr\">setItem</code> دون <code dir=\"ltr\">JSON.stringify</code>، فحُوّل بـ <code dir=\"ltr\">String()</code>.","why":"صحيح. التخزين لا يحتفظ إلا بالنصوص. حوّل إلى نص بـ <code dir=\"ltr\">JSON.stringify</code> واقرأ مجددًا بـ <code dir=\"ltr\">JSON.parse</code>."},{"text":"استُدعيت <code dir=\"ltr\">JSON.parse</code> مرتين عند التحميل.","why":"التحليل يحدث عند التحميل ولا يستطيع تغيير ما خُزّن. النص كان خاطئًا أصلًا عند الحفظ."},{"text":"كُتب المفتاح بشكل خاطئ عند التحميل.","why":"المفتاح المكتوب خطأً يُرجع <code dir=\"ltr\">null</code>، لا “[object Object]”."}],"answer":1},{"q":"<p>يجيب الخادم عن هذا الطلب برمز الحالة 500 وصفحة خطأ HTML. ماذا يحدث؟</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"js\" data-index=\"7\"><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\">async</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> function</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\"> load</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\"> res</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> await</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\"> fetch</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"/api/expenses\"</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\"> res.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">json</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","options":[{"text":"تُرفض <code dir=\"ltr\">fetch</code> بخطأ يقول “HTTP 500”.","why":"<code dir=\"ltr\">fetch</code> لا تُرفض إلا حين تفشل الشبكة. والـ 500 ما زالت استجابة HTTP كاملة."},{"text":"<code dir=\"ltr\">load()</code> تُنجَز بالقيمة <code dir=\"ltr\">undefined</code>.","why":"<code dir=\"ltr\">res.json()</code> لا تُرجع <code dir=\"ltr\">undefined</code> بهدوء؛ بل تحاول تحليل الجسم فتفشل."},{"text":"<code dir=\"ltr\">fetch</code> تُنجَز، ثم تُرفض <code dir=\"ltr\">res.json()</code> بخطأ SyntaxError لأن الجسم HTML لا JSON.","why":"صحيح. افحص <code dir=\"ltr\">res.ok</code> قبل التحليل وارمِ خطأً واضحًا من عندك."},{"text":"يعيد المتصفّح محاولة الطلب حتى ينجح.","why":"المتصفّحات لا تعيد محاولة استجابات HTTP الفاشلة تلقائيًا. كودك يستلم استجابة الـ 500."}],"answer":2}],"s-js-5":[{"q":"<p>يحتوي <code dir=\"ltr\">money.js</code> على <code dir=\"ltr\">export function formatMoney(cents) { … }</code>. ويحتوي <code dir=\"ltr\">main.js</code> على <code dir=\"ltr\">import formatMoney from &quot;./money.js&quot;;</code>. ماذا يحدث؟</p>","options":[{"text":"يفشل التحميل: ليس لـ <code dir=\"ltr\">money.js</code> تصدير افتراضي. اكتب <code dir=\"ltr\">import { formatMoney } from &quot;./money.js&quot;;</code>.","why":"صحيح. الأقواس المعقوفة تستورد التصديرات المسمّاة؛ ودونها تطلب تصديرًا افتراضيًا غير موجود."},{"text":"يعمل، لأن الأسماء متطابقة.","why":"الأسماء لا تهمّ إلا في الاستيراد المسمّى، الذي يحتاج إلى أقواس معقوفة. ودون الأقواس يطلب الاستيراد التصدير الافتراضي."},{"text":"<code dir=\"ltr\">formatMoney</code> تساوي <code dir=\"ltr\">undefined</code> حتى أول استدعاء لها.","why":"الاستيرادات تُربط قبل تنفيذ أي كود. والتصدير المفقود يمنع الوحدة من التحميل أصلًا."},{"text":"يعمل فقط إن غُيّر اسم الملف إلى <code dir=\"ltr\">money.mjs</code>.","why":"امتداد الملف لا يغيّر طريقة عمل التصديرات المسمّاة والافتراضية."}],"answer":0},{"q":"<p>ماذا يطبع هذا الكود؟</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"js\" data-index=\"8\"><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\">class</span><span style=\"color:#953800;--shiki-dark:#FFA657\"> Store</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> {</span></span>\n<span class=\"line\"><span style=\"color:#953800;--shiki-dark:#FFA657\">  #items</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\">  add</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#953800;--shiki-dark:#FFA657\">x</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">) { </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">this</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">.#items.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">push</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(x); }</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">  get</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\"> size</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">() { </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">return</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> this</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">.#items.</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">length</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\">const</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> s</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> new</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\"> Store</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">();</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">s.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">add</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</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:#1F2328;--shiki-dark:#E6EDF3\">console.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">log</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(s.size, s.items);</span></span></code></pre></div>\n","options":[{"text":"<code dir=\"ltr\">1 [1]</code>","why":"<code dir=\"ltr\">#items</code> و<code dir=\"ltr\">items</code> اسمان مختلفان. والحقل الخاص لا يُكشف على شكل خاصية عامة اسمها <code dir=\"ltr\">items</code>."},{"text":"<code dir=\"ltr\">undefined undefined</code>","why":"<code dir=\"ltr\">size</code> هو getter، لذلك قراءة <code dir=\"ltr\">s.size</code> تنفّذه فيُرجع 1."},{"text":"يرمي SyntaxError.","why":"وحدها كتابة <code dir=\"ltr\">s.#items</code> خارج الصنف خطأ في الصياغة. أما قراءة خاصية غير موجودة فمسموحة."},{"text":"<code dir=\"ltr\">1 undefined</code>","why":"صحيح. الـ getter يُرجع الطول، و<code dir=\"ltr\">s.items</code> مجرّد خاصية غير موجودة."}],"answer":3},{"q":"<p>يلتقط كود التحميل لديك كل الأخطاء في هذه الكتلة. فصار خطأ برمجي داخل <code dir=\"ltr\">render</code> يمرّ دون أن يُلاحَظ. ما البنية الأفضل؟</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"js\" data-index=\"9\"><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\">try</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\"> data</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> JSON</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">parse</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(text);</span></span>\n<span class=\"line\"><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">  render</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(data);</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">} </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">catch</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> {</span></span>\n<span class=\"line\"><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">  render</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","options":[{"text":"أبقِها كما هي، واترك كتلة catch فارغة بدلًا من ذلك.","why":"كتلة catch الفارغة تُخفي أكثر: الآن لا يُعرض شيء ولا يُبلَّغ عن شيء."},{"text":"غلّف <code dir=\"ltr\">JSON.parse</code> وحدها بـ <code dir=\"ltr\">try</code>/<code dir=\"ltr\">catch</code> مع قيمة بديلة، واستدعِ <code dir=\"ltr\">render</code> خارجها، كي تظهر أخطاؤها.","why":"صحيح. التقط فقط ما تتوقّعه وتستطيع التعامل معه، وهنا هو JSON التالف. والأخطاء غير المتوقّعة يجب أن تصل إلى الـ console."},{"text":"غلّف التطبيق كله بكتلة <code dir=\"ltr\">try</code>/<code dir=\"ltr\">catch</code> كبيرة واحدة في الأعلى.","why":"هذا يلتقط كل شيء في كل مكان، فيجعل الأخطاء البرمجية الحقيقية أصعب في العثور عليها."},{"text":"احذف <code dir=\"ltr\">try</code> كليًا.","why":"عندها ستُسقط قيمة محفوظة تالفة التطبيقَ عند البدء، وهذا فشل حقيقي ومتوقّع ينبغي أن تتعامل معه."}],"answer":1},{"q":"<p>يعرض Pocket <code dir=\"ltr\">Total: $NaN</code> بعد أن تضيف مصروفًا. ونقطة توقّف في <code dir=\"ltr\">totalCents</code> تُظهر أن المصروف الجديد فيه <code dir=\"ltr\">amount: undefined</code>. أين تبحث بعد ذلك؟</p>","options":[{"text":"في ملف الأنماط، تحسّبًا لأن يكون المجموع مخفيًا.","why":"القيمة نفسها خاطئة، فلا يمكن أن يكون التنسيق هو السبب."},{"text":"في استدعاء <code dir=\"ltr\">reduce</code> داخل <code dir=\"ltr\">totalCents</code>.","why":"<code dir=\"ltr\">reduce</code> تجمع بشكل صحيح؛ لكنها استلمت قيمة سيئة. الخطأ أبكر من ذلك، حيث صُنعت تلك القيمة."},{"text":"في كود معالج الإرسال الذي يبني كائن المصروف الجديد.","why":"صحيح. تتبّع القيمة السيئة إلى حيث أُنشئت، مثلًا اسم حقل مكتوب خطأً مثل <code dir=\"ltr\">data.get(&quot;amout&quot;)</code>."},{"text":"في حدّ حجم التخزين.","why":"التخزين الممتلئ يرمي خطأ عند الحفظ. ولا يغيّر مبلغًا في الذاكرة إلى <code dir=\"ltr\">undefined</code>."}],"answer":2},{"q":"<p>في Pocket، يستدعي معالج الحذف <code dir=\"ltr\">button.closest(&quot;li&quot;).remove()</code>. فتعود المصاريف المحذوفة حين يتغيّر الفلتر. لماذا؟</p>","options":[{"text":"<code dir=\"ltr\">remove()</code> غير متزامنة ولم تكن قد انتهت.","why":"<code dir=\"ltr\">remove()</code> تسري فورًا. أُزيل العنصر فعلًا؛ لكن شيئًا ما رسمه من جديد."},{"text":"يحتفظ المتصفّح بنسخة مؤقتة من القائمة بين عمليات العرض.","why":"لا توجد نسخة مؤقتة. <code dir=\"ltr\">render</code> تبني القائمة من جديد من الـ state في كل مرة."},{"text":"قائمة اختيار الفلتر تعيد تحميل الصفحة.","why":"حدث <code dir=\"ltr\">change</code> لا يعيد تحميل الصفحة. بل يستدعي <code dir=\"ltr\">update</code>، التي تعيد العرض."},{"text":"غيّر معالج الحدث الصفحة لكنه لم يغيّر <code dir=\"ltr\">state.expenses</code>، فيرسم العرض التالي المصروف من جديد.","why":"صحيح. غيّر الـ state عبر <code dir=\"ltr\">update</code>، ودع <code dir=\"ltr\">render</code> تُزيله من الصفحة."}],"answer":3}]},"final":[{"q":"<p>ماذا يطبع <code dir=\"ltr\">console.log(typeof (450 + &quot;50&quot;), 450 - &quot;50&quot;)</code>؟</p>","options":[{"text":"<code dir=\"ltr\">number 400</code>","why":"مع نص في أحد الطرفين، تلصق <code dir=\"ltr\">+</code> النصوص، لذلك النتيجة الأولى نص."},{"text":"<code dir=\"ltr\">string 45050</code>","why":"<code dir=\"ltr\">-</code> لا تفعل إلا الحساب، لذلك تتحوّل “50” إلى رقم وتكون النتيجة 400."},{"text":"<code dir=\"ltr\">string 400</code>","why":"صحيح. <code dir=\"ltr\">+</code> تلصق فتعطي “45050”، وهو نص؛ و<code dir=\"ltr\">-</code> تحوّل “50” إلى 50 وتطرح."},{"text":"<code dir=\"ltr\">number 500</code>","why":"لا تجمع أيّ من العمليتين 450 و50 بصفتهما رقمين هنا."}],"answer":2},{"q":"<p>ماذا تُرجع <code dir=\"ltr\">[3, 1, 2].map((n) =&gt; n * 2).filter((n) =&gt; n &gt; 2)</code>؟</p>","options":[{"text":"<code dir=\"ltr\">[6, 4]</code>","why":"صحيح. <code dir=\"ltr\">map</code> تعطي [6, 2, 4]، ثم تحتفظ <code dir=\"ltr\">filter</code> بالقيم الأكبر من 2، بترتيبها الأصلي."},{"text":"<code dir=\"ltr\">[4, 6]</code>","why":"لا <code dir=\"ltr\">map</code> ولا <code dir=\"ltr\">filter</code> ترتّبان. العناصر تحتفظ بترتيبها."},{"text":"<code dir=\"ltr\">[6, 2, 4]</code>","why":"هذه نتيجة <code dir=\"ltr\">map</code> وحدها؛ ثم تُزيل <code dir=\"ltr\">filter</code> الـ 2."},{"text":"<code dir=\"ltr\">[3, 2]</code>","why":"<code dir=\"ltr\">filter</code> تعمل على القيم المضاعفة، لا على المصفوفة الأصلية."}],"answer":0},{"q":"<p>مصروف ليست له خاصية <code dir=\"ltr\">receipt</code>. أيّ تعبير يرمي خطأ؟</p>","options":[{"text":"<code dir=\"ltr\">expense.receipt?.url</code>","why":"<code dir=\"ltr\">?.</code> تتوقّف عند الإيصال المفقود وتُرجع <code dir=\"ltr\">undefined</code>."},{"text":"<code dir=\"ltr\">expense?.receipt</code>","why":"قراءة خاصية مفقودة لا بأس بها؛ فهي تعطي <code dir=\"ltr\">undefined</code>."},{"text":"<code dir=\"ltr\">expense.receipt ?? &quot;none&quot;</code>","why":"هذا يقرأ خاصية مفقودة، وهو مسموح، ثم يلجأ إلى البديل “none”."},{"text":"<code dir=\"ltr\">expense.receipt.url</code>","why":"صحيح. <code dir=\"ltr\">expense.receipt</code> تساوي <code dir=\"ltr\">undefined</code>، وقراءة <code dir=\"ltr\">.url</code> من <code dir=\"ltr\">undefined</code> ترمي TypeError."}],"answer":3},{"q":"<p>بأي ترتيب تُطبع الحروف؟</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"js\" data-index=\"10\"><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:#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\">\"A\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">), </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 style=\"color:#CF222E;--shiki-dark:#FF7B72\">async</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\">  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\">\"B\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">);</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">  await</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> null</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:#0A3069;--shiki-dark:#A5D6FF\">\"C\"</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\">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\">\"D\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">);</span></span></code></pre></div>\n","options":[{"text":"A B C D","why":"callback المؤقّت مهمة (task)، تنتظر حتى ينتهي السكربت وكل المهام الدقيقة."},{"text":"B D C A","why":"صحيح. تُنفَّذ B بشكل متزامن، وتوقف <code dir=\"ltr\">await</code> الدالة مؤقتًا، وتُنهي D السكربت، وتستأنف C على شكل مهمة دقيقة، وتُنفَّذ A أخيرًا على شكل مهمة."},{"text":"B C D A","why":"<code dir=\"ltr\">await</code> توقف الدالة غير المتزامنة مؤقتًا حتى مع قيمة ليست promise، لذلك تُطبع D قبل C."},{"text":"D B C A","why":"الدالة غير المتزامنة تعمل بشكل متزامن حتى أول <code dir=\"ltr\">await</code>، لذلك تُطبع B قبل D."}],"answer":1},{"q":"<p><code dir=\"ltr\">totalsByCategory</code> تستخدم <code dir=\"ltr\">reduce</code> مع <code dir=\"ltr\">{}</code> قيمةً ابتدائية، وترمي <code dir=\"ltr\">Cannot read properties of undefined</code> عند المصروف الثاني. ما السبب الأرجح؟</p>","options":[{"text":"يجب أن تكون القيمة الابتدائية <code dir=\"ltr\">[]</code> لا <code dir=\"ltr\">{}</code>.","why":"الكائن هو الصحيح لمجاميع الفئات؛ والمصفوفة تحتاج إلى كود مختلف."},{"text":"يجب ترتيب المصاريف حسب الفئة أولًا.","why":"التجميع بـ <code dir=\"ltr\">reduce</code> يعمل بأي ترتيب."},{"text":"<code dir=\"ltr\">reduce</code> لا تستطيع بناء كائنات، بل أرقامًا فقط.","why":"يمكن أن يكون المُراكِم أي قيمة، بما في ذلك كائن."},{"text":"للـ callback أقواس معقوفة لكن ليس فيه <code dir=\"ltr\">return totals</code>، فيستلم الاستدعاء الثاني <code dir=\"ltr\">undefined</code>.","why":"صحيح. كل ما يُرجعه الـ callback يصبح المُراكِم التالي؛ ودون <code dir=\"ltr\">return</code> يكون ذلك <code dir=\"ltr\">undefined</code>."}],"answer":3},{"q":"<p>جزءان من Pocket يستدعي كلٌّ منهما <code dir=\"ltr\">createIdGenerator(&quot;exp&quot;)</code>، وأحيانًا يُنتجان المعرّف نفسه. ما الإصلاح الصحيح؟</p>","options":[{"text":"خزّن العدّاد في <code dir=\"ltr\">var</code> عام بدلًا من ذلك.","why":"العدّاد العام يمكن لأي كود أن يصفّره أو يغيّره، وهذه بالضبط المشكلة التي كانت الـ closures تحلّها."},{"text":"استدعِ <code dir=\"ltr\">createIdGenerator(&quot;exp&quot;)</code> داخل كل معالج نقرات.","why":"كل استدعاء يُنشئ عدّادًا جديدًا يبدأ من 1، وهذا يجعل التكرار أكثر احتمالًا."},{"text":"أنشئ مولّدًا واحدًا في وحدة واحدة واستورد الدالة نفسها أينما احتجت إلى معرّفات.","why":"صحيح. مولّد واحد يعني عدّادًا واحدًا، فيكون كل معرّف يعطيه فريدًا."},{"text":"استخدم <code dir=\"ltr\">Math.round(Math.random() * 10)</code> معرّفًا.","why":"مع 11 قيمة ممكنة فقط، التصادم مضمون. استخدم عدّادًا واحدًا أو <code dir=\"ltr\">crypto.randomUUID()</code>."}],"answer":2},{"q":"<p>تُعرض قائمة تسميات بـ <code dir=\"ltr\">li.innerHTML = `&lt;b&gt;${label}&lt;/b&gt;`;</code> ويحفظ مستخدم التسمية <code dir=\"ltr\">&lt;img src=x onerror=alert(1)&gt;</code>. ما الإصلاح الآمن؟</p>","options":[{"text":"ضع التسمية بين علامات تنصيص داخل القالب.","why":"علامات التنصيص مجرّد حروف بالنسبة لمحلّل HTML؛ فيظل الوسم يُنشأ ويظل كوده يُنفَّذ."},{"text":"أنشئ <code dir=\"ltr\">&lt;b&gt;</code> بـ <code dir=\"ltr\">createElement</code> واضبط <code dir=\"ltr\">textContent</code> الخاصة به على التسمية.","why":"صحيح. <code dir=\"ltr\">textContent</code> لا تحلّل HTML أبدًا، فتُعرض التسمية نصًا عاديًا."},{"text":"استخدم <code dir=\"ltr\">outerHTML</code> بدلًا من <code dir=\"ltr\">innerHTML</code>.","why":"<code dir=\"ltr\">outerHTML</code> تحلّل HTML أيضًا، فلها مشكلة الحقن نفسها."},{"text":"حدّد طول التسميات بـ 20 حرفًا.","why":"الحمولات القصيرة ما زالت قادرة على تشغيل الكود. والإصلاح ألّا تحلّل نص المستخدم على أنه HTML أبدًا."}],"answer":1},{"q":"<p><code dir=\"ltr\">getJson</code> تُرجع promise يُرفض. ماذا تفعل <code dir=\"ltr\">safeLoad()</code>؟</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"js\" data-index=\"11\"><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\">async</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> function</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\"> safeLoad</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">() {</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">  try</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:#8250DF;--shiki-dark:#D2A8FF\"> getJson</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"/api/rates\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">);</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  } </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">catch</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>\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></code></pre></div>\n","options":[{"text":"تُرجع promise يُرفض، لأنه دون <code dir=\"ltr\">await</code> يحدث الرفض بعد انتهاء كتلة <code dir=\"ltr\">try</code>.","why":"صحيح. اكتب <code dir=\"ltr\">return await getJson(...)</code> كي يُرمى الرفض داخل <code dir=\"ltr\">try</code> ويُلتقط."},{"text":"تُنجَز بالقيمة <code dir=\"ltr\">{}</code>.","why":"الـ <code dir=\"ltr\">catch</code> لا ترى إلا الأخطاء المرمية أثناء تنفيذ كتلة <code dir=\"ltr\">try</code>، ولا شيء يُرمى هناك دون <code dir=\"ltr\">await</code>."},{"text":"تُنجَز بالقيمة <code dir=\"ltr\">undefined</code>.","why":"الـ promise المُعاد يُتبنّى، فتتبع النتيجة <code dir=\"ltr\">getJson</code>: إنها تُرفض."},{"text":"ترمي خطأً بشكل متزامن عند استدعائها.","why":"الدالة غير المتزامنة لا ترمي بشكل متزامن أبدًا؛ إنها تُرجع promise دائمًا."}],"answer":0},{"q":"<p>تحتاج إلى <code dir=\"ltr\">amounts</code> مرتّبة من الأكبر إلى الأصغر لرسم بياني، بينما ما زالت لوحة أخرى تعرض <code dir=\"ltr\">amounts</code> بترتيبها الأصلي. أيّ سطر صحيح؟</p>","options":[{"text":"<code dir=\"ltr\">amounts.sort((a, b) =&gt; b - a)</code>","why":"دالة المقارنة صحيحة، لكن <code dir=\"ltr\">sort</code> تعيد ترتيب المصفوفة الأصلية، فتُفسد اللوحة الأخرى."},{"text":"<code dir=\"ltr\">amounts.toSorted()</code>","why":"إنها تُرجع نسخة، لكنها دون دالة مقارنة ترتّب كنصوص، تصاعديًا."},{"text":"<code dir=\"ltr\">[...amounts].sort()</code>","why":"النسخة تحمي الأصل، لكن الترتيب الافتراضي يقارن كنصوص ويرتّب تصاعديًا."},{"text":"<code dir=\"ltr\">amounts.toSorted((a, b) =&gt; b - a)</code>","why":"صحيح. نسخة مرتّبة، رقميًا وتنازليًا، تترك الأصل دون مساس."}],"answer":3},{"q":"<p><code dir=\"ltr\">items.forEach(store.add);</code> ترمي <code dir=\"ltr\">Cannot read properties of undefined (reading '#items')</code> داخل <code dir=\"ltr\">add</code>. أيّ تغيير يُصلح ذلك؟</p>","options":[{"text":"صرّح بـ <code dir=\"ltr\">store</code> باستخدام <code dir=\"ltr\">let</code> بدلًا من <code dir=\"ltr\">const</code>.","why":"طريقة التصريح بالمتغيّر لا تغيّر طريقة استدعاء <code dir=\"ltr\">add</code>."},{"text":"اكتب <code dir=\"ltr\">items.forEach(store.add());</code>.","why":"هذا يستدعي <code dir=\"ltr\">add</code> مرة واحدة فورًا دون أي عنصر، ويمرّر ما أرجعته إلى <code dir=\"ltr\">forEach</code>."},{"text":"اكتب <code dir=\"ltr\">items.forEach((item) =&gt; store.add(item));</code>.","why":"صحيح. الدالة السهمية تستدعي <code dir=\"ltr\">add</code> على <code dir=\"ltr\">store</code>، فتعود <code dir=\"ltr\">this</code> هي المخزن."},{"text":"أسند <code dir=\"ltr\">const add = store.add;</code> أولًا ومرّر <code dir=\"ltr\">add</code>.","why":"هذه الـ method المنفصلة نفسها باسم آخر، فما زالت <code dir=\"ltr\">this</code> ضائعة."}],"answer":2},{"q":"<p>الفحص <code dir=\"ltr\">if (amountInput.value == 0)</code> مقصود منه التقاط مبلغ قيمته صفر. لماذا يُنفَّذ أيضًا حين يكون الحقل فارغًا؟</p>","options":[{"text":"حقول الأرقام الفارغة تحتوي على النص “0”.","why":"قيمة الحقل الفارغ هي النص الفارغ “”، لا “0”."},{"text":"<code dir=\"ltr\">==</code> تحوّل <code dir=\"ltr\">&quot;&quot;</code> إلى الرقم 0 قبل المقارنة، لذلك <code dir=\"ltr\">&quot;&quot; == 0</code> صحيحة.","why":"صحيح. حوّل صراحةً، وافحص المدخلات الفارغة أولًا، وقارن بـ <code dir=\"ltr\">===</code>."},{"text":"<code dir=\"ltr\">value</code> تساوي <code dir=\"ltr\">null</code> حين يكون الحقل فارغًا.","why":"<code dir=\"ltr\">value</code> نص دائمًا؛ ومع الحقل الفارغ تكون “”."},{"text":"حقول الأرقام تُرجع <code dir=\"ltr\">NaN</code> حين تكون فارغة، و<code dir=\"ltr\">NaN == 0</code> صحيحة.","why":"<code dir=\"ltr\">value</code> تساوي “” لا <code dir=\"ltr\">NaN</code>، و<code dir=\"ltr\">NaN</code> لا تساوي أي شيء، ولا حتى نفسها."}],"answer":1},{"q":"<p>ماذا يحدث مع <code dir=\"ltr\">JSON.parse(JSON.stringify({ when: new Date() })).when.getFullYear()</code>؟</p>","options":[{"text":"يرمي TypeError، لأن <code dir=\"ltr\">when</code> تعود نصًا، والنص ليست له دالة <code dir=\"ltr\">getFullYear</code>.","why":"صحيح. يخزّن JSON التواريخ نصوصًا بصيغة ISO. حوّلها مجددًا بـ <code dir=\"ltr\">new Date(text)</code>، أو خزّن التواريخ نصوصًا من البداية."},{"text":"يُرجع السنة الحالية.","why":"هذا يتطلّب أن تعود <code dir=\"ltr\">when</code> كائن Date، لكن <code dir=\"ltr\">JSON.parse</code> لا تملك طريقة لمعرفة أن النص كان تاريخًا."},{"text":"يُرجع <code dir=\"ltr\">NaN</code>.","why":"لا يوجد كائن Date يُرجع <code dir=\"ltr\">NaN</code>؛ فالدالة غير موجودة على النص أصلًا."},{"text":"يُرجع <code dir=\"ltr\">undefined</code>.","why":"استدعاء شيء ليس دالة يرمي خطأ؛ ولا يُرجع <code dir=\"ltr\">undefined</code>."}],"answer":0}],"glossary":[{"id":"type","term":"نوع البيانات (Type)","definition":"طبيعة القيمة، وهي التي تحدّد ما يمكنك فعله بها. من الأنواع الأساسية في JavaScript: number وstring وboolean وundefined وnull؛ والعامل typeof يخبرك بنوع أي قيمة.","lesson":"l-js-1-2"},{"id":"variable","term":"المتغيّر (Variable)","definition":"اسم يشير إلى قيمة كي يستخدمها البرنامج لاحقًا. في JavaScript يكون النوع للقيمة لا للمتغيّر، لذلك يمكن لمتغيّر واحد أن يشير إلى أنواع مختلفة مع الوقت.","lesson":"l-js-1-2"},{"id":"let-const","term":"let وconst","definition":"الطريقتان الحديثتان للتصريح بمتغيّر. الاسم المُصرَّح به بـ const لا يمكن توجيهه إلى قيمة أخرى أبدًا؛ أما let فيمكن إعادة إسنادها. ابدأ بـ const ولا تستخدم let إلا عند الحاجة.","lesson":"l-js-1-2"},{"id":"floating-point","term":"العدد العشري الثنائي (Floating-point)","definition":"رقم مخزّن بالنظام الثنائي بدقة محدودة، كما هي كل أرقام JavaScript. كثير من الأعداد العشرية لا يمكن تخزينها بدقة، فتعطي 0.1 + 0.2 القيمة 0.30000000000000004؛ لذلك خزّن المال سنتاتٍ صحيحة.","lesson":"l-js-1-3"},{"id":"strict-equality","term":"المساواة الصارمة (===)","definition":"مقارنة لا تكون صحيحة إلا حين يكون للقيمتين النوع نفسه والقيمة نفسها. وعلى عكس المساواة المتساهلة (==)، لا تحوّل الأنواع أولًا أبدًا، لذلك “5” === 5 تساوي false.","lesson":"l-js-1-3"},{"id":"template-literal","term":"Template literal","definition":"نص يُكتب بين علامات backtick، يستطيع إدراج قيمة أي تعبير عبر ${ } ويمكن أن يمتد على عدّة أسطر، مثل <code dir=\"ltr\">Total: ${total}</code>.","lesson":"l-js-1-4"},{"id":"truthy-falsy","term":"Truthy وFalsy","definition":"طريقة معاملة JavaScript لأي قيمة تُستخدم شرطًا. ثماني قيم فقط falsy: false و0 و-0 و0n والنص الفارغ وnull وundefined وNaN. وكل قيمة أخرى truthy.","lesson":"l-js-2-1"},{"id":"function","term":"الدالة (Function)","definition":"كتلة كود لها اسم ويمكن إعادة استخدامها. تعلن عن معاملات، وتستلم وسائط حين تُستدعى، وتنفّذ جسمها وتُعيد نتيجة عبر return؛ ودون return تكون النتيجة undefined.","lesson":"l-js-2-3"},{"id":"arrow-function","term":"الدالة السهمية (Arrow function)","definition":"صياغة أقصر للدوال، مثل (cents) =&gt; cents / 100. إن جاء بعد السهم تعبير واحد، أُرجع تلقائيًا؛ أما مع الأقواس المعقوفة فتحتاج إلى return صريحة.","lesson":"l-js-2-3"},{"id":"callback","term":"Callback","definition":"دالة تُمرَّر إلى دالة أخرى، وتلك الأخرى هي التي تقرّر متى تستدعيها. دوال المصفوفات مثل map، ومستمعو الأحداث، والمؤقّتات، كلها تأخذ callbacks.","lesson":"l-js-2-3"},{"id":"scope","term":"النطاق (Scope)","definition":"منطقة الكود التي يمكن فيها استخدام اسم ما. نطاق let وconst هو أقرب كتلة، والبحث عن الأسماء يتّجه من النطاقات الداخلية إلى الخارجية، لا إلى الداخل أبدًا.","lesson":"l-js-2-4"},{"id":"closure","term":"Closure","definition":"دالة مقترنة بمتغيّرات النطاق الذي أُنشئت فيه. تحتفظ الدالة بإمكانية الوصول إليها حتى بعد انتهاء الدالة الخارجية، وهذا يمنحها حالة خاصة تتذكّرها.","lesson":"l-js-2-4"},{"id":"array","term":"المصفوفة (Array)","definition":"قائمة مرتّبة من القيم، تُكتب بين أقواس مربّعة وتبدأ فهارسها من 0. للمصفوفات دوال مثل map وfilter وfind وreduce لتحويل عناصرها والبحث فيها.","lesson":"l-js-3-1"},{"id":"object","term":"الكائن (Object)","definition":"قيمة تجمع خصائص مسمّاة، مثل { label: “Coffee”, amount: 450 }. تُقرأ الخصائص بصيغة النقطة، أو بالأقواس المربّعة حين يكون الاسم في متغيّر.","lesson":"l-js-3-2"},{"id":"reference","term":"المرجع (Reference)","definition":"ما يحمله المتغيّر حين تكون قيمته كائنًا أو مصفوفة: مؤشّر إليها، لا نسخة منها. وإسناده أو تمريره يتشارك الكائن نفسه، فيظهر التغيير عبر أي اسم من خلال الأسماء كلها.","lesson":"l-js-3-2"},{"id":"spread","term":"صيغة النشر (Spread)","definition":"ثلاث نقاط تنسخ عناصر مصفوفة أو خصائص كائن إلى مصفوفة أو كائن جديد، كما في […list, item] أو { …expense, amount: 500 }. والنسخة سطحية.","lesson":"l-js-3-2"},{"id":"destructuring","term":"التفكيك (Destructuring)","definition":"صياغة تفكّ خصائص كائن أو عناصر مصفوفة إلى متغيّرات في خطوة واحدة، مثل const { label, amount } = expense. وتدعم القيم الافتراضية وتعمل في معاملات الدوال.","lesson":"l-js-3-2"},{"id":"reduce","term":"reduce","definition":"دالة مصفوفات تمرّ على العناصر حاملةً مُراكِمًا (accumulator)، وتُرجع المُراكِم النهائي. تُستخدم للمجاميع والتجميع؛ ومرّر لها دائمًا قيمة ابتدائية وسيطًا ثانيًا.","lesson":"l-js-3-3"},{"id":"optional-chaining","term":"السلسلة الاختيارية (?.)","definition":"عامل يقرأ الخاصية فقط إن لم تكن القيمة على يساره null أو undefined، فيُرجع undefined بدلًا من رمي TypeError، كما في expense.receipt?.url.","lesson":"l-js-3-4"},{"id":"nullish-coalescing","term":"nullish coalescing (??)","definition":"عامل لا يُرجع طرفه الأيمن إلا حين يكون الطرف الأيسر null أو undefined. وعلى عكس ||، يحتفظ بالقيم الحقيقية مثل 0 والنص الفارغ.","lesson":"l-js-3-4"},{"id":"dom","term":"DOM","definition":"نموذج كائنات المستند (Document Object Model): شجرة الكائنات الحيّة في المتصفّح التي تمثّل الصفحة. وتغيير هذه الكائنات بـ JavaScript، مثل textContent لعنصر ما، يحدّث ما يراه المستخدم فورًا.","lesson":"l-js-4-1"},{"id":"event-delegation","term":"تفويض الأحداث (Event delegation)","definition":"التعامل مع أحداث عناصر أبناء كثيرة بمستمع واحد على الحاوية التي تضمّها. تنتشر الأحداث للأعلى إلى الحاوية، وتجد event.target.closest() الابن المعني، بما في ذلك الأبناء المضافون لاحقًا.","lesson":"l-js-4-2"},{"id":"json","term":"JSON","definition":"اختصار JavaScript Object Notation، وهو صيغة نصية للبيانات. تحوّل JSON.stringify القيم إلى نص JSON وتعيده JSON.parse قيمًا؛ ويحمل النصوص والأرقام والقيم المنطقية وnull والمصفوفات والكائنات البسيطة.","lesson":"l-js-4-4"},{"id":"promise","term":"Promise","definition":"كائن يمثّل نتيجة ليست جاهزة بعد. يبدأ معلّقًا (pending) ويستقرّ مرة واحدة، إمّا مُنجَزًا بقيمة أو مرفوضًا بخطأ؛ وتُلحق then وcatch ما يحدث بعد ذلك.","lesson":"l-js-4-5"},{"id":"async-await","term":"async/await","definition":"صياغة للعمل مع الـ promises. داخل دالة async، توقف await تلك الدالة مؤقتًا حتى يستقرّ الـ promise وتعطي قيمته، فيُقرأ الكود غير المتزامن من الأعلى إلى الأسفل وتُلتقط الأخطاء بـ try/catch.","lesson":"l-js-4-5"},{"id":"event-loop","term":"حلقة الأحداث (Event loop)","definition":"الآلية التي تتيح لـ JavaScript ذات الخيط الواحد أن تنتظر دون أن تتجمّد. حين يفرغ مكدّس الاستدعاءات، تنفّذ كل callbacks الـ promises المنتظرة (المهام الدقيقة)، ثم المهمة التالية مثل مؤقّت أو نقرة.","lesson":"l-js-4-5"},{"id":"module","term":"الوحدة (Module)","definition":"ملف JavaScript له نطاقه الخاص، ولا يتشارك القيم مع الملفات الأخرى إلا عبر export وimport. وفي المتصفّح، تُحمَّل وحدة الدخول بوسم script له type=“module”.","lesson":"l-js-5-1"},{"id":"class","term":"الصنف (Class)","definition":"مخطّط للكائنات يحزم البيانات مع الـ methods التي تعمل عليها. تنفّذ new الـ constructor وتُرجع instance جديدة؛ والحقول الخاصة الموسومة بـ # مخفية عن الكود الخارجي.","lesson":"l-js-5-2"},{"id":"stack-trace","term":"أثر المكدّس (Stack trace)","definition":"قائمة استدعاءات الدوال، مع أسماء الملفات وأرقام الأسطر، التي أدّت إلى خطأ ما، الأحدث أولًا. وهي تُظهر أين وقع الخطأ وكيف وصل البرنامج إلى هناك.","lesson":"l-js-5-3"},{"id":"breakpoint","term":"نقطة التوقّف (Breakpoint)","definition":"علامة في DevTools، أو تعليمة debugger في الكود، توقف التنفيذ مؤقتًا عند سطر ما كي تفحص كل متغيّر ومكدّس الاستدعاءات، وتتقدّم في الكود سطرًا سطرًا.","lesson":"l-js-5-3"}]}