الوحدة 1 · الخطوات الأولى
الأرقام والعوامل والمقارنات
17 دقراءة 4 دبيئة تجريب الكود
الدرس في لمحة
تُجري العمليات الحسابية على أرقام JavaScript، وتتجنّب مفاجآت الأعداد العشرية بتخزين المال سنتاتٍ صحيحة، وتقارن القيم بأمان باستخدام === بدلًا من ==.
- 1العمليات الحسابية بالطريقة المعتادة
- 2لماذا لا تساوي 0.1 + 0.2 القيمة 0.3
- 3حين تلصق + بدلًا من أن تجمع
- 4مقارنة القيم
لا فيديو هنا، بل الشيء الحقيقي: درس مكتوب وأمثلة قابلة للتشغيل واختبار وتدريب.
اطلب من JavaScript أن تجمع ثمن قهوة وغداء وتذكرة حافلة، وستعطيك جوابًا خاطئًا بفارق ضئيل:
console.log(4.1 + 12.2 + 2.7);توقّعت 19 فحصلت على 18.999999999999996. هذا ليس خطأً في JavaScript؛ فمعظم لغات البرمجة تفعل الشيء نفسه. وحين تفهم السبب، تستطيع أن تصمّم Pocket بحيث لا يلدغك هذا أبدًا.
العمليات الحسابية بالطريقة المعتادة
تعمل العوامل الحسابية كما تتمنّى، مع أولوية العمليات المعتادة: * و/ و% تُنفَّذ قبل + و-، والأقواس تتقدّم على كل شيء.
console.log(2 + 3 * 4); // multiplication first
console.log((2 + 3) * 4); // parentheses first
console.log(17 % 5); // remainder after dividing 17 by 5
console.log(2 ** 10); // 2 to the power of 10% (باقي القسمة) أنفع مما تبدو: n % 2 === 0 تخبرك إن كان الرقم زوجيًا، وminutes % 60 تعطيك الدقائق المتبقية بعد الساعات الكاملة.
حين تحدّث متغيّرًا انطلاقًا من قيمته نفسها، توجد اختصارات. total += 450 تعني total = total + 450، والنمط نفسه يعمل مع -= و*= و/=. أما count++ فتضيف واحدًا، وستراها في حلقات التكرار.
لماذا لا تساوي 0.1 + 0.2 القيمة 0.3
لدى JavaScript نوع واحد للأرقام، يُستخدم للأعداد الصحيحة والعشرية معًا. وفي الخفاء يُخزَّن كل رقم بالنظام الثنائي في 64 بتًا، وفق معيار اسمه IEEE 754. الأعداد الصحيحة تتّسع بلا مشكلة، أما كثير من الأعداد العشرية فلا: فالعدد 0.1 بالنظام الثنائي كسر يتكرّر إلى ما لا نهاية، مثل 1/3 بالنظام العشري (0.3333…). يضطر الحاسوب إلى قطعه في مكان ما، فيخزّن أقرب قيمة يستطيعها. اجمع بضعًا من هذه القيم “شبه الصحيحة” فيصبح الخطأ مرئيًا.
console.log(0.1 + 0.2);
console.log(0.1 + 0.2 === 0.3);
console.log(4.1 * 100);الخطأ ضئيل جدًا، نحو جزء واحد من عشرة كوادريليون، لذلك لا يهم أبدًا في قياس المسافات أو الرسم على الشاشة. لكنه يهم في المال. فالمجموع الذي يُفترض أن يكون 19 لكنه مخزّن على أنه 18.999999999999996 يفشل في فحص المساواة، ويُقرَّب في الاتجاه الخاطئ على الإيصال، وينحرف أكثر مع كل مصروف تضيفه. وتقريب الرقم عند عرضه يُخفي العَرَض بينما يبقى الرقم المخزّن خاطئًا.
مع المال، الحل بسيط، وهو ما تفعله شركات الدفع: خزّن المبالغ سنتاتٍ صحيحة. الأعداد الصحيحة دقيقة حتى Number.MAX_SAFE_INTEGER، أي نحو 9 كوادريليون، وهذا أكثر من كافٍ لمتتبّع مصاريف.
const coffee = 410; // $4.10 in cents
const lunch = 1220;
const bus = 270;
const total = coffee + lunch + bus;
console.log(total); // exact
console.log(total / 100); // convert to dollars only when you display itمن هذا الدرس فصاعدًا، يُخزَّن كل مبلغ في Pocket بالسنتات. وحين يصل مبلغ بالدولار، مثل 4.1، حوّله مرة واحدة باستخدام Math.round(4.1 * 100). لـ Math.round أهمية هنا، لأن 4.1 * 100 تساوي 409.99999999999994، والتقريب يُرجعها إلى 410.
حين تلصق + بدلًا من أن تجمع
القيم التي تُكتب في صفحة ويب تصل دائمًا نصوصًا، حتى لو بدت أرقامًا. ولـ + وظيفتان: جمع الأرقام ولصق النصوص. فإن كان أحد الطرفين نصًا، فإنها تلصق.
const typed = "5";
console.log(typed + 3); // joins: "53"
console.log(typed - 3); // only maths makes sense, so 2
console.log(Number(typed) + 3); // convert first: 8
console.log(Number("12abc")); // not a number: NaNأما العوامل الأخرى (- و* و/) فلا تعرف إلا الحساب، لذلك تحوّل النصوص إلى أرقام بصمت، وهذا يجعل الخطأ أصعب اكتشافًا: الطرح يعمل والجمع لا يعمل. حوّل المدخلات صراحةً باستخدام Number() بمجرّد أن تستلمها.
حين يفشل التحويل تحصل على NaN، أي “ليس رقمًا” (not a number)، والمربك أن نوعها number. وNaN معدية: أي عملية حسابية معها تعطي NaN. افحصها باستخدام Number.isNaN(value)، لأن NaN === NaN تساوي false.
وقريبتها هي Infinity. القسمة على صفر لا تُوقف برنامج JavaScript كما تفعل في الآلة الحاسبة: 5 / 0 تساوي Infinity، و-5 / 0 تساوي -Infinity. يبدو هذا غير مؤذٍ حتى يحسب Pocket “متوسط الإنفاق اليومي” في أول الشهر وعدد الأيام المسجّلة صفر. لا NaN ولا Infinity ترمي خطأً، فيستمر برنامجك في العمل ويعرض كلامًا بلا معنى. والوقاية واحدة لكليهما: افحص مدخلاتك قبل أن تحسب.
مقارنة القيم
عوامل المقارنة تُنتج قيمة منطقية (boolean): > و< و>= و<=، ونوعا المساواة.
=== (المساواة الصارمة) تكون true فقط حين تكون للقيمتين النوع نفسه والقيمة نفسها. أما == (المساواة المتساهلة) فتحوّل الطرفين أولًا إلى نوع مشترك، وفق قواعد تفاجئ حتى المطوّرين ذوي الخبرة:
| التعبير | == |
=== |
|---|---|---|
"5" و5 |
true | false |
"" و0 |
true | false |
null وundefined |
true | false |
"0" وfalse |
true | false |
استخدم === و!== في كل مكان. وإن كان النوعان قد يختلفان، فحوّل صراحةً أولًا ثم قارن. عندها يقول كودك ما يعنيه بالضبط، ولا يحتاج أحد إلى حفظ جدول تحويلات.
const budgetCents = 2500;
const spentCents = 2200;
const isOverBudget = spentCents > budgetCents;
console.log(isOverBudget, typeof isOverBudget);
console.log(spentCents === 2200, "2200" === 2200);بعد أن صارت المبالغ دقيقة والمقارنات آمنة، الخطوة التالية أن تعرضها بشكل جميل: النصوص، وكيف تبني نصًا مقروءًا مثل “Lunch: $12.20”.
أهم النقاط
- لدى JavaScript نوع واحد للأرقام الصحيحة والعشرية معًا، والأعداد العشرية تُخزَّن بالنظام الثنائي، لذلك لا تساوي
0.1 + 0.2القيمة0.3بالضبط. - خزّن المال سنتاتٍ صحيحة (integers)، ولا تحوّله إلى دولارات إلا عند العرض؛ فالحساب بالأعداد الصحيحة دقيق تمامًا.
+مع نص تُلصق النصوص بدلًا من أن تجمع، لذلك حوّل المدخلات باستخدامNumber()قبل أي عملية حسابية.- قارن دائمًا باستخدام
===و!==؛ فـ==تحوّل الأنواع أولًا وتعطي نتائج مثل أن"" == 0صحيحة.