{"id":"l-js-1-3","courseId":"javascript-fundamentals","lang":"ar","kind":"lesson","summary":"تُجري العمليات الحسابية على أرقام JavaScript، وتتجنّب مفاجآت الأعداد العشرية بتخزين المال سنتاتٍ صحيحة، وتقارن القيم بأمان باستخدام === بدلًا من ==.","html":"<p>اطلب من JavaScript أن تجمع ثمن قهوة وغداء وتذكرة حافلة، وستعطيك جوابًا خاطئًا بفارق ضئيل:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"js\" data-run=\"js\" data-index=\"0\"><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=\"0\" 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:#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:#0550AE;--shiki-dark:#79C0FF\">4.1</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> +</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> 12.2</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> +</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> 2.7</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">);</span></span></code></pre><div class=\"run-output\" data-run-output=\"0\" hidden></div></div>\n<p>توقّعت 19 فحصلت على <code dir=\"ltr\">18.999999999999996</code>. هذا ليس خطأً في JavaScript؛ فمعظم لغات البرمجة تفعل الشيء نفسه. وحين تفهم السبب، تستطيع أن تصمّم Pocket بحيث لا يلدغك هذا أبدًا.</p>\n<h2 id=\"sec-1\" tabindex=\"-1\">العمليات الحسابية بالطريقة المعتادة</h2>\n<p>تعمل العوامل الحسابية كما تتمنّى، مع أولوية العمليات المعتادة: <code dir=\"ltr\">*</code> و<code dir=\"ltr\">/</code> و<code dir=\"ltr\">%</code> تُنفَّذ قبل <code dir=\"ltr\">+</code> و<code dir=\"ltr\">-</code>، والأقواس تتقدّم على كل شيء.</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:#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:#0550AE;--shiki-dark:#79C0FF\">2</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> +</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> 3</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> *</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> 4</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">);    </span><span style=\"color:#6E7781;--shiki-dark:#8B949E\">// multiplication first</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:#0550AE;--shiki-dark:#79C0FF\">2</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\">*</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> 4</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">);  </span><span style=\"color:#6E7781;--shiki-dark:#8B949E\">// parentheses first</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:#0550AE;--shiki-dark:#79C0FF\">17</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> %</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> 5</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">);       </span><span style=\"color:#6E7781;--shiki-dark:#8B949E\">// remainder after dividing 17 by 5</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:#0550AE;--shiki-dark:#79C0FF\">2</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:#6E7781;--shiki-dark:#8B949E\">// 2 to the power of 10</span></span></code></pre><div class=\"run-output\" data-run-output=\"1\" hidden></div></div>\n<p><code dir=\"ltr\">%</code> (باقي القسمة) أنفع مما تبدو: <code dir=\"ltr\">n % 2 === 0</code> تخبرك إن كان الرقم زوجيًا، و<code dir=\"ltr\">minutes % 60</code> تعطيك الدقائق المتبقية بعد الساعات الكاملة.</p>\n<p>حين تحدّث متغيّرًا انطلاقًا من قيمته نفسها، توجد اختصارات. <code dir=\"ltr\">total += 450</code> تعني <code dir=\"ltr\">total = total + 450</code>، والنمط نفسه يعمل مع <code dir=\"ltr\">-=</code> و<code dir=\"ltr\">*=</code> و<code dir=\"ltr\">/=</code>. أما <code dir=\"ltr\">count++</code> فتضيف واحدًا، وستراها في حلقات التكرار.</p>\n<h2 id=\"sec-2\" tabindex=\"-1\">لماذا لا تساوي 0.1 + 0.2 القيمة 0.3</h2>\n<p>لدى JavaScript نوع واحد للأرقام، يُستخدم للأعداد الصحيحة والعشرية معًا. وفي الخفاء يُخزَّن كل رقم بالنظام الثنائي في 64 بتًا، وفق معيار اسمه IEEE 754. الأعداد الصحيحة تتّسع بلا مشكلة، أما كثير من الأعداد العشرية فلا: فالعدد 0.1 بالنظام الثنائي كسر يتكرّر إلى ما لا نهاية، مثل 1/3 بالنظام العشري (0.3333…). يضطر الحاسوب إلى قطعه في مكان ما، فيخزّن أقرب قيمة يستطيعها. اجمع بضعًا من هذه القيم “شبه الصحيحة” فيصبح الخطأ مرئيًا.</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:#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:#0550AE;--shiki-dark:#79C0FF\">0.1</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> +</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> 0.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:#0550AE;--shiki-dark:#79C0FF\">0.1</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> +</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> 0.2</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> ===</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> 0.3</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:#0550AE;--shiki-dark:#79C0FF\">4.1</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></code></pre><div class=\"run-output\" data-run-output=\"2\" hidden></div></div>\n<p>الخطأ ضئيل جدًا، نحو جزء واحد من عشرة كوادريليون، لذلك لا يهم أبدًا في قياس المسافات أو الرسم على الشاشة. لكنه يهم في المال. فالمجموع الذي يُفترض أن يكون 19 لكنه مخزّن على أنه 18.999999999999996 يفشل في فحص المساواة، ويُقرَّب في الاتجاه الخاطئ على الإيصال، وينحرف أكثر مع كل مصروف تضيفه. وتقريب الرقم عند عرضه يُخفي العَرَض بينما يبقى الرقم المخزّن خاطئًا.</p>\n<p>مع المال، الحل بسيط، وهو ما تفعله شركات الدفع: <strong>خزّن المبالغ سنتاتٍ صحيحة</strong>. الأعداد الصحيحة دقيقة حتى <code dir=\"ltr\">Number.MAX_SAFE_INTEGER</code>، أي نحو 9 كوادريليون، وهذا أكثر من كافٍ لمتتبّع مصاريف.</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\">const</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> coffee</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> 410</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">;   </span><span style=\"color:#6E7781;--shiki-dark:#8B949E\">// $4.10 in cents</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">const</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> lunch</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:#CF222E;--shiki-dark:#FF7B72\">const</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> bus</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> 270</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\"> total</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> coffee </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">+</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> lunch </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">+</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> bus;</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 style=\"color:#6E7781;--shiki-dark:#8B949E\">// exact</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 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:#6E7781;--shiki-dark:#8B949E\">// convert to dollars only when you display it</span></span></code></pre><div class=\"run-output\" data-run-output=\"3\" hidden></div></div>\n<p>من هذا الدرس فصاعدًا، يُخزَّن كل مبلغ في Pocket بالسنتات. وحين يصل مبلغ بالدولار، مثل <code dir=\"ltr\">4.1</code>، حوّله مرة واحدة باستخدام <code dir=\"ltr\">Math.round(4.1 * 100)</code>. لـ <code dir=\"ltr\">Math.round</code> أهمية هنا، لأن <code dir=\"ltr\">4.1 * 100</code> تساوي <code dir=\"ltr\">409.99999999999994</code>، والتقريب يُرجعها إلى <code dir=\"ltr\">410</code>.</p>\n<h2 id=\"sec-3\" tabindex=\"-1\">حين تلصق + بدلًا من أن تجمع</h2>\n<p>القيم التي تُكتب في صفحة ويب تصل دائمًا نصوصًا، حتى لو بدت أرقامًا. ولـ <code dir=\"ltr\">+</code> وظيفتان: جمع الأرقام ولصق النصوص. فإن كان أحد الطرفين نصًا، فإنها تلصق.</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\">const</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> typed</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> \"5\"</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\">(typed </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:#6E7781;--shiki-dark:#8B949E\">// joins: \"53\"</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\">(typed </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:#6E7781;--shiki-dark:#8B949E\">// only maths makes sense, so 2</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\">Number</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(typed) </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:#6E7781;--shiki-dark:#8B949E\">// convert first: 8</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\">Number</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"12abc\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">));    </span><span style=\"color:#6E7781;--shiki-dark:#8B949E\">// not a number: NaN</span></span></code></pre><div class=\"run-output\" data-run-output=\"4\" hidden></div></div>\n<p>أما العوامل الأخرى (<code dir=\"ltr\">-</code> و<code dir=\"ltr\">*</code> و<code dir=\"ltr\">/</code>) فلا تعرف إلا الحساب، لذلك تحوّل النصوص إلى أرقام بصمت، وهذا يجعل الخطأ أصعب اكتشافًا: الطرح يعمل والجمع لا يعمل. حوّل المدخلات صراحةً باستخدام <code dir=\"ltr\">Number()</code> بمجرّد أن تستلمها.</p>\n<p>حين يفشل التحويل تحصل على <code dir=\"ltr\">NaN</code>، أي “ليس رقمًا” (not a number)، والمربك أن نوعها number. و<code dir=\"ltr\">NaN</code> معدية: أي عملية حسابية معها تعطي <code dir=\"ltr\">NaN</code>. افحصها باستخدام <code dir=\"ltr\">Number.isNaN(value)</code>، لأن <code dir=\"ltr\">NaN === NaN</code> تساوي <code dir=\"ltr\">false</code>.</p>\n<p>وقريبتها هي <code dir=\"ltr\">Infinity</code>. القسمة على صفر لا تُوقف برنامج JavaScript كما تفعل في الآلة الحاسبة: <code dir=\"ltr\">5 / 0</code> تساوي <code dir=\"ltr\">Infinity</code>، و<code dir=\"ltr\">-5 / 0</code> تساوي <code dir=\"ltr\">-Infinity</code>. يبدو هذا غير مؤذٍ حتى يحسب Pocket “متوسط الإنفاق اليومي” في أول الشهر وعدد الأيام المسجّلة صفر. لا <code dir=\"ltr\">NaN</code> ولا <code dir=\"ltr\">Infinity</code> ترمي خطأً، فيستمر برنامجك في العمل ويعرض كلامًا بلا معنى. والوقاية واحدة لكليهما: افحص مدخلاتك قبل أن تحسب.</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>جمع المدخلات الخام</span></p>\n<p><code dir=\"ltr\">total = total + amountInput</code> تبدو صحيحة، لكنها تُنتج <code dir=\"ltr\">&quot;0450&quot;</code> في أول مرة يستخدم فيها أحدهم نموذجك. حوّل أولًا باستخدام <code dir=\"ltr\">Number(amountInput)</code>، ثم افحص الناتج بـ <code dir=\"ltr\">Number.isNaN</code> قبل استخدامه.</p>\n</aside>\n<h2 id=\"sec-4\" tabindex=\"-1\">مقارنة القيم</h2>\n<p>عوامل المقارنة تُنتج قيمة منطقية (boolean): <code dir=\"ltr\">&gt;</code> و<code dir=\"ltr\">&lt;</code> و<code dir=\"ltr\">&gt;=</code> و<code dir=\"ltr\">&lt;=</code>، ونوعا المساواة.</p>\n<p><code dir=\"ltr\">===</code> (المساواة الصارمة) تكون <code dir=\"ltr\">true</code> فقط حين تكون للقيمتين النوع نفسه والقيمة نفسها. أما <code dir=\"ltr\">==</code> (المساواة المتساهلة) فتحوّل الطرفين أولًا إلى نوع مشترك، وفق قواعد تفاجئ حتى المطوّرين ذوي الخبرة:</p>\n<div class=\"table-wrap\" role=\"region\" aria-label=\"جدول قابل للتمرير\" tabindex=\"0\"><table><thead>\n<tr>\n<th>التعبير</th>\n<th><code dir=\"ltr\">==</code></th>\n<th><code dir=\"ltr\">===</code></th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><code dir=\"ltr\">&quot;5&quot;</code> و<code dir=\"ltr\">5</code></td>\n<td>true</td>\n<td>false</td>\n</tr>\n<tr>\n<td><code dir=\"ltr\">&quot;&quot;</code> و<code dir=\"ltr\">0</code></td>\n<td>true</td>\n<td>false</td>\n</tr>\n<tr>\n<td><code dir=\"ltr\">null</code> و<code dir=\"ltr\">undefined</code></td>\n<td>true</td>\n<td>false</td>\n</tr>\n<tr>\n<td><code dir=\"ltr\">&quot;0&quot;</code> و<code dir=\"ltr\">false</code></td>\n<td>true</td>\n<td>false</td>\n</tr>\n</tbody>\n</table></div><p>استخدم <code dir=\"ltr\">===</code> و<code dir=\"ltr\">!==</code> في كل مكان. وإن كان النوعان قد يختلفان، فحوّل صراحةً أولًا ثم قارن. عندها يقول كودك ما يعنيه بالضبط، ولا يحتاج أحد إلى حفظ جدول تحويلات.</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"js\" data-run=\"js\" data-index=\"5\"><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=\"5\" 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\"> budgetCents</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> 2500</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\"> spentCents</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> 2200</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\"> isOverBudget</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> spentCents </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">></span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> budgetCents;</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\">(isOverBudget, </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">typeof</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> isOverBudget);</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\">(spentCents </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">===</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> 2200</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"2200\"</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> ===</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> 2200</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">);</span></span></code></pre><div class=\"run-output\" data-run-output=\"5\" hidden></div></div>\n<aside class=\"callout callout-tip\" 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=\"M15 14c.2-1 .7-1.7 1.5-2.5 1-.9 1.5-2.2 1.5-3.5A6 6 0 0 0 6 8c0 1 .2 2.2 1.5 3.5.7.7 1.3 1.5 1.5 2.5\"/><path d=\"M9 18h6\"/><path d=\"M10 22h4\"/></svg><span><span class=\"callout-kind\">نصيحة</span><span class=\"callout-sep\" aria-hidden=\"true\"> · </span>التقريب للبشر</span></p>\n<p><code dir=\"ltr\">Math.round</code> و<code dir=\"ltr\">Math.floor</code> (للأسفل) و<code dir=\"ltr\">Math.ceil</code> (للأعلى) تُرجع أعدادًا صحيحة. و<code dir=\"ltr\">Math.max(3, 9, 4)</code> و<code dir=\"ltr\">Math.min(...)</code> تختاران الأكبر أو الأصغر. ستستخدمها كلها في Pocket.</p>\n</aside>\n<p>بعد أن صارت المبالغ دقيقة والمقارنات آمنة، الخطوة التالية أن تعرضها بشكل جميل: النصوص، وكيف تبني نصًا مقروءًا مثل “Lunch: $12.20”.</p>\n","toc":[{"id":"sec-1","text":"العمليات الحسابية بالطريقة المعتادة","level":2},{"id":"sec-2","text":"لماذا لا تساوي 0.1 + 0.2 القيمة 0.3","level":2},{"id":"sec-3","text":"حين تلصق + بدلًا من أن تجمع","level":2},{"id":"sec-4","text":"مقارنة القيم","level":2}],"takeaways":["لدى JavaScript نوع واحد للأرقام الصحيحة والعشرية معًا، والأعداد العشرية تُخزَّن بالنظام الثنائي، لذلك لا تساوي <code dir=\"ltr\">0.1 + 0.2</code> القيمة <code dir=\"ltr\">0.3</code> بالضبط.","خزّن المال سنتاتٍ صحيحة (integers)، ولا تحوّله إلى دولارات إلا عند العرض؛ فالحساب بالأعداد الصحيحة دقيق تمامًا.","<code dir=\"ltr\">+</code> مع نص تُلصق النصوص بدلًا من أن تجمع، لذلك حوّل المدخلات باستخدام <code dir=\"ltr\">Number()</code> قبل أي عملية حسابية.","قارن دائمًا باستخدام <code dir=\"ltr\">===</code> و<code dir=\"ltr\">!==</code>؛ فـ <code dir=\"ltr\">==</code> تحوّل الأنواع أولًا وتعطي نتائج مثل أن <code dir=\"ltr\">&quot;&quot; == 0</code> صحيحة."],"further":[{"title":"Expressions and operators (MDN)","url":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_operators"},{"title":"Equality comparisons and sameness (MDN)","url":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Equality_comparisons_and_sameness"},{"title":"Number (MDN)","url":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number"}],"quiz":[{"q":"<p>يكتب المستخدم <code dir=\"ltr\">5</code> في حقل نصي، فيستلم كودك النص <code dir=\"ltr\">&quot;5&quot;</code>. ما ناتج <code dir=\"ltr\">&quot;5&quot; + 3</code>؟</p>","options":[{"text":"<code dir=\"ltr\">8</code>","why":"هذا ما أردته، لكن <code dir=\"ltr\">+</code> ترى نصًا في أحد الطرفين فتتحوّل إلى لصق النصوص."},{"text":"<code dir=\"ltr\">&quot;53&quot;</code>","why":"صحيح. حين يكون أحد طرفي <code dir=\"ltr\">+</code> نصًا، تحوّل JavaScript الطرف الآخر إلى نص وتلصقهما. حوّل أولًا باستخدام <code dir=\"ltr\">Number(&quot;5&quot;)</code>."},{"text":"<code dir=\"ltr\">NaN</code>","why":"تظهر <code dir=\"ltr\">NaN</code> حين تعجز عملية حسابية عن إنتاج رقم. أما هنا فـ <code dir=\"ltr\">+</code> تلصق النصوص بلا مشكلة، فلا تفشل أي عملية حسابية."},{"text":"خطأ TypeError، لأنه لا يمكنك جمع نص ورقم.","why":"لا ترمي JavaScript أي خطأ هنا؛ إنها تحوّل الأنواع بصمت، ولهذا بالضبط يسهل أن يفوتك هذا الخطأ."}],"answer":1},{"q":"<p>يخزّن Pocket القهوة بقيمة <code dir=\"ltr\">4.1</code> دولار والغداء بقيمة <code dir=\"ltr\">12.2</code>. لماذا يُعدّ تخزين <code dir=\"ltr\">410</code> و<code dir=\"ltr\">1220</code> سنتًا تصميمًا أفضل؟</p>","options":[{"text":"الحساب بالأعداد الصحيحة دقيق، أما الأعداد العشرية الثنائية مثل 4.1 فتحمل أخطاء تقريب صغيرة تتراكم في المجاميع.","why":"صحيح. الأعداد الصحيحة حتى نحو 9 كوادريليون تُمثَّل بدقة تامة، فلا تنحرف مجاميع السنتات أبدًا."},{"text":"الأعداد الصحيحة تستهلك ذاكرة أقل من العشرية في JavaScript.","why":"كلاهما من نوع الأرقام نفسه ويستهلكان الذاكرة نفسها. الفائدة في الدقة، لا في الحجم."},{"text":"لا تستطيع JavaScript جمع الأعداد العشرية أصلًا دون مكتبة.","why":"تستطيع جمعها؛ لكن الناتج أحيانًا يبتعد شعرة عن الصواب، مثل <code dir=\"ltr\">18.999999999999996</code> بدلًا من 19."}],"answer":0},{"q":"<p>أيّ مقارنة نتيجتها <code dir=\"ltr\">true</code>؟</p>","options":[{"text":"<code dir=\"ltr\">&quot;0&quot; === 0</code>","why":"<code dir=\"ltr\">===</code> لا تحوّل الأنواع أبدًا، والنص لا يساوي الرقم مساواةً صارمة في أي حال."},{"text":"<code dir=\"ltr\">NaN === NaN</code>","why":"NaN هي القيمة الوحيدة التي لا تساوي نفسها. استخدم <code dir=\"ltr\">Number.isNaN(value)</code> لفحصها."},{"text":"<code dir=\"ltr\">10 % 3 === 1</code>","why":"صحيح. <code dir=\"ltr\">%</code> تعطي باقي القسمة، وقسمة 10 على 3 تترك باقيًا قدره 1."},{"text":"<code dir=\"ltr\">2 + 3 * 2 === 10</code>","why":"الضرب يُنفَّذ قبل الجمع، فيصبح الطرف الأيسر <code dir=\"ltr\">2 + 6</code>، أي 8."}],"answer":2},{"q":"<p>ما القيمة التي يتركها <code dir=\"ltr\">let n = 4; n += 2; n *= 3;</code> في <code dir=\"ltr\">n</code>؟</p>","options":[{"text":"<code dir=\"ltr\">18</code>","why":"صحيح. <code dir=\"ltr\">n += 2</code> تجعلها 6، و<code dir=\"ltr\">n *= 3</code> تجعلها 18. كل اختصار يقرأ القيمة الحالية، ويُجري العملية، ثم يعيد الإسناد."},{"text":"<code dir=\"ltr\">10</code>","why":"هذا ناتج <code dir=\"ltr\">4 + 2 * 3</code>. لكن التعليمتين تُنفَّذان واحدة بعد الأخرى، فينتهي الجمع قبل أن يبدأ الضرب."},{"text":"<code dir=\"ltr\">14</code>","why":"هذا ناتج <code dir=\"ltr\">4 * 3 + 2</code>. التعليمات تُنفَّذ بترتيب كتابتها: الجمع أولًا، ثم الضرب."}],"answer":0}],"exercise":{"kind":"web","mode":"js","starter":{"js":"// Three expenses, still in dollars\nconst coffee = 4.1;\nconst lunch = 12.2;\nconst bus = 2.7;\n// A fourth amount, typed into a text box, so it arrived as a string\nconst typedAmount = \"3\";\n\nconst budgetCents = 2500;\n\n// Fix this: it should be the exact total in whole cents\nconst totalCents = coffee + lunch + bus + typedAmount;\n\n// Declare isOverBudget and leftCents below\n"},"solution":{"js":"const coffee = 4.1;\nconst lunch = 12.2;\nconst bus = 2.7;\nconst typedAmount = \"3\";\n\nconst budgetCents = 2500;\n\nconst totalCents =\n  Math.round(coffee * 100) +\n  Math.round(lunch * 100) +\n  Math.round(bus * 100) +\n  Math.round(Number(typedAmount) * 100);\n\nconst isOverBudget = totalCents > budgetCents;\nconst leftCents = budgetCents - totalCents;\nconsole.log(totalCents, isOverBudget, leftCents);\n"},"checks":[{"id":"total-number","test":"return typeof totalCents === 'number';"},{"id":"total-exact","test":"return totalCents === 2200;"},{"id":"over-budget","test":"return isOverBudget === false;"},{"id":"left","test":"return leftCents === 300;"}],"title":"المجموع بالسنتات، بدقة","prompt":"<p>يجمع كود البداية ثلاثة مبالغ بالدولار ونصًا مكتوبًا، فتكون النتيجة فوضى. أصلح <code dir=\"ltr\">totalCents</code> بحيث يكون <strong>المجموع الدقيق بالسنتات الصحيحة</strong> للمبالغ الأربعة كلها (حوّل كل مبلغ إلى سنتات باستخدام <code dir=\"ltr\">Math.round(amount * 100)</code>، وحوّل <code dir=\"ltr\">typedAmount</code> إلى رقم أولًا). ثم صرّح بـ <code dir=\"ltr\">isOverBudget</code>، وهي قيمة منطقية تساوي <code dir=\"ltr\">true</code> حين يكون <code dir=\"ltr\">totalCents</code> أكبر من <code dir=\"ltr\">budgetCents</code>، وبـ <code dir=\"ltr\">leftCents</code>، أي الميزانية ناقص المجموع.</p>\n","hints":["اطبع <code dir=\"ltr\">totalCents</code> في كود البداية لترى المشكلة: إنه نص، لأن <code dir=\"ltr\">+</code> صادفت <code dir=\"ltr\">&quot;3&quot;</code> فبدأت تلصق النصوص.","<code dir=\"ltr\">Number(typedAmount)</code> تحوّل <code dir=\"ltr\">&quot;3&quot;</code> إلى <code dir=\"ltr\">3</code>. وحوّل كل مبلغ بالدولار باستخدام <code dir=\"ltr\">Math.round(x * 100)</code>.","المقارنة قيمة منطقية بحدّ ذاتها: <code dir=\"ltr\">const isOverBudget = totalCents &gt; budgetCents;</code>."],"explanation":"<p>الضرب في 100 ينقل كل مبلغ إلى السنتات، لكن <code dir=\"ltr\">4.1 * 100</code> تساوي <code dir=\"ltr\">409.99999999999994</code>، لذلك تُرجعها <code dir=\"ltr\">Math.round</code> إلى العدد الصحيح الذي يجب أن تكونه. ويجب أن يمرّ <code dir=\"ltr\">typedAmount</code> عبر <code dir=\"ltr\">Number()</code> أولًا؛ وإلا فإن أول <code dir=\"ltr\">+</code> تصادفه ستلصق النصوص بدلًا من أن تجمع. وحين يصبح كل شيء أعدادًا صحيحة، يكون المجموع دقيقًا: 410 + 1220 + 270 + 300 = 2200. والتعبير <code dir=\"ltr\">totalCents &gt; budgetCents</code> ناتجه <code dir=\"ltr\">false</code>، فتخزّن هذه القيمة المنطقية مباشرة بدلًا من كتابة <code dir=\"ltr\">if</code>.</p>\n","checkLabels":{"total-number":"<code dir=\"ltr\">totalCents</code> رقم، وليس نصًا","total-exact":"<code dir=\"ltr\">totalCents</code> يساوي 2200 بالضبط","over-budget":"<code dir=\"ltr\">isOverBudget</code> هي القيمة المنطقية <code dir=\"ltr\">false</code>","left":"<code dir=\"ltr\">leftCents</code> يساوي 300"}},"words":668,"readMinutes":4}