نوع رقمي واحد يجمع كل شيء (تقريبًا)
أغلب لغات البرمجة تُفرِّق بين الأعداد الصحيحة والأعداد العشرية، وتمنحك نوعًا مستقلًا لكل منهما. أما جافا سكريبت فقد اعتمدت تاريخيًا على نوع واحد فقط هو Number. سواء كتبت 42 أو 3.14 أو -0.001، فالنتيجة واحدة: قيمة أولية من نوع عدد عشري مزدوج الدقة بحجم 64 بت وفق معيار IEEE 754.
يبدو الأمر مريحاً للوهلة الأولى — لا حاجة للتحويل بين int و float، ولا مشكلة تجاوز عند 2^31. لكن تمثيل الأرقام كأعداد عشرية بصيغة الفاصلة العائمة له ثمن، وكثيراً ما يقع المبتدئون في فخّه. لهذا أُضيف نوع رقمي ثانٍ اسمه BigInt سنة 2020، ليسدّ الثغرات التي يعجز Number عن تغطيتها.
مفاجأة الفاصلة العائمة في جافا سكريبت
جرّب تنفيذ هذا الكود:
السطر الأول يطبع 0.30000000000000004، والثاني يطبع false. وهذه ليست غرابة في جافا سكريبت — فلغات بايثون وجافا وسي وأي لغة تعتمد على معيار IEEE 754 للفواصل العائمة تتصرف بنفس الطريقة.
لماذا 0.1 + 0.2 لا يساوي 0.3؟ السبب ببساطة أن القيمتين 0.1 و 0.2 لا يمكن تمثيلهما بدقة في النظام الثنائي، تماماً كما لا يمكن كتابة 1/3 بدقة في النظام العشري. فيُخزَّن أقرب تقريب ثنائي ممكن، ثم تتراكم أخطاء طفيفة مع العمليات. الطريقة الصحيحة للتفكير في الأمر: اعتبر قيم Number التي تحوي كسوراً عشرية تقريبات قريبة جداً مما كتبته، لا أكثر.
أما في التعامل مع المبالغ المالية، فلا تُخزِّن $19.99 بالشكل 19.99، بل خزِّن القيمة بالسنتات كعدد صحيح — أي 1999 — ونسِّقها عند العرض فقط. هذه أفضل عادة منفردة تحميك من أخطاء الفاصلة العائمة.
كيفية مقارنة الأعداد العشرية بأمان
بما أن المقارنة المباشرة بالمساواة غير موثوقة، قارن باستخدام هامش تسامح (tolerance) عند الحاجة:
Number.EPSILON هو أصغر فرق بين العدد 1 والعدد التالي الذي يمكن تمثيله — يعتبر قيمة افتراضية معقولة للتسامح مع الأخطاء عند التعامل مع أرقام قريبة من 1. أما مع الأرقام ذات الأحجام الكبيرة جداً أو الصغيرة جداً، فستحتاج إلى هامش تسامح يتناسب مع حجم المدخلات.
نطاق الأعداد الصحيحة الآمنة في جافا سكريبت
الأعداد الصحيحة حتى حجم معين تُمثَّل بدقة تامة داخل عدد عشري بتنسيق 64-bit. لكن بمجرد تجاوز هذا الحد، تبدأ بفقدان الدقة بتاً بعد بت:
الرقم 2^53 - 1 هو آخر عدد صحيح يمكن تمثيله بدقة مع جميع الأعداد الأصغر منه. بعد هذا الحد، ببساطة لا يوجد بعض الأعداد الصحيحة ضمن النوع Number — بل يتم تقريبها إلى أقرب عدد مجاور. وهذه مشكلة خطيرة قد تُفسد بياناتك بصمت، خاصةً إذا كنت تقرأ معرّفات (IDs) بحجم 64 بت من قاعدة بيانات على هيئة أرقام JSON عادية.
ما هو BigInt في جافا سكريبت؟
النوع BigInt هو نوع أولي (primitive) منفصل مُخصَّص للأعداد الصحيحة بدقة لا نهائية. لإنشاء قيمة من هذا النوع، أضف الحرف n في نهاية العدد الصحيح، أو استخدم الدالة BigInt(...):
ليس لدى BigInt حدّ أعلى سوى حجم الذاكرة المتاحة، وهذا ما يجعله الخيار الأمثل في حالات مثل:
- معرّفات قواعد البيانات أو معرّفات snowflake الخاصة بـ Twitter/X التي تتجاوز
2^53. - العمليات الحسابية في التشفير.
- أي عمليات حسابية على أعداد صحيحة تكون فيها دقّة النتيجة أهمّ من السرعة.
في المقابل، BigInt ليس الخيار المناسب للعدّادات العادية، ولا لفهارس المصفوفات، ولا حتى لتمثيل المبالغ المالية بالقروش؛ فالنوع Number التقليدي أسرع ويتكامل مع جميع واجهات اللغة دون أي متاعب.
العمليات الحسابية على BigInt
تعمل جميع العوامل المعتادة بشكل طبيعي، شريطة أن يكون كلا المعاملَين من نوع BigInt:
القسمة تُقرِّب نحو الصفر — لا وجود للكسور في BigInt. إذا احتجت إلى جزء عشري فأنت مضطر للعودة إلى Number (أو استعمال مكتبة مخصّصة للأرقام العشرية).
ممنوع خلط الأنواع
القاعدة التي يتعثّر فيها الكثيرون: لا يمكنك خلط Number مع BigInt في التعبير نفسه.
المقارنة هي الاستثناء الوحيد — عوامل < و > و == تقوم بالتحويل بين النوعين تلقائيًا:
إذًا، == يعتبرهما متساويين، بينما === لا يرى ذلك. إن كنت قد اعتمدت === في كل مكان (وهذا ما يجب فعله)، فاعتبر أي مقارنة رقمية بين النوعين علامة على خلل في التصميم — اختر جهة واحدة وحوّل إليها.
التحويل بين Number و BigInt
تحويلان اثنان، ولكل منهما فخّه:
التحويل من Number إلى BigInt صارم: الكسور و NaN سترمي خطأ. أما التحويل من BigInt إلى Number فهو متساهل لكنه يفقد الدقة — أي رقم يتجاوز MAX_SAFE_INTEGER سيتم تقريبه. إذا كنت تُحوِّل قيمة BigInt استقبلتها من الخادم، اسأل نفسك أولاً: هل تحتاج فعلاً إلى هذا التحويل؟
قيم رقمية خاصة في جافا سكريبت
بما أننا نتحدث عن الأرقام في جافا سكريبت، هناك ثلاث قيم ينتمين إلى النوع Number لكنها ليست أرقاماً بالمعنى الرياضي:
Infinity و-Infinity يظهران عند القسمة على صفر أو عند تجاوز النطاق الذي يستطيع النوع العائم تمثيله. أما NaN (اختصار لـ "not a number") فيظهر حين تنتج عملية حسابية قيمة لا معنى لها.
الغريب في NaN أنه لا يساوي نفسه — وهذا ليس خللاً في جافا سكريبت، بل جزء من مواصفة IEEE 754. للتحقق منه استخدم Number.isNaN(x). أما الدالة العامة القديمة isNaN فتحوّل وسيطها أولاً قبل الفحص، ما يؤدي إلى نتائج خاطئة (مثلاً isNaN("hello") تُرجع true). لذلك التزم دائماً بـ Number.isNaN.
تحويل النصوص إلى أرقام في جافا سكريبت
كثيراً ما تصلك مدخلات المستخدم أو أرقام JSON على هيئة نصوص. إليك ثلاث طرق للتحويل:
Number() صارمة جدًا — أي قيمة غير رقمية تُرجع NaN، باستثناء النص الفارغ والمسافات البيضاء فهي تُرجع 0. أما parseInt وparseFloat فهما أكثر تساهلًا — يقرآن أكبر قدر ممكن ثم يتوقفان عند أول حرف غير صالح. اختر ما يناسب قصدك، وتأكد من النتيجة بفحص NaN قبل استخدامها.
ولتحويل نصٍّ إلى BigInt، استخدم BigInt("123") — فهي صارمة وترمي خطأً عند أي إدخال غير صالح.
قواعد سريعة تُغنيك عن الحيرة
- للعدّادات والعمليات الحسابية والإحداثيات ومعظم الأرقام اليومية: استخدم
Number. - للأموال: حوّلها إلى قيمة صحيحة بالقروش/السنتات واستخدم
Number، أو استعن بمكتبة خاصة بالأرقام العشرية. - للأعداد الصحيحة الأكبر من
2^53(معرّفات قواعد البيانات، التشفير، التوافيق): استخدمBigIntمع اللاحقةn. - قارن الأرقام العشرية ضمن هامش تسامح، لا باستخدام
===. - افحص النتائج غير الصالحة باستخدام
Number.isNaNوNumber.isFinite، لا بالدوال العامة. - لا تخلط بين
NumberوBigIntفي التعبير نفسه — حوّل بينهما صراحةً.
التالي: الفرق بين null و undefined
في جافا سكريبت طريقتان للتعبير عن "لا توجد قيمة" — null وundefined — وهما ليسا مترادفين. في الدرس القادم سنرى معنى كلٍّ منهما، والفرق بينهما، ومتى تستخدم هذا ومتى ذاك.
الأسئلة الشائعة
لماذا لا يساوي 0.1 + 0.2 القيمة 0.3 في جافا سكريبت؟
لأن نوع Number في جافا سكريبت هو رقم عشري بصيغة IEEE 754 بحجم 64 بت، والقيمتان 0.1 و0.2 لا يمكن تمثيلهما بدقة في النظام الثنائي، فتكون النتيجة 0.30000000000000004. وهذه ليست علة في جافا سكريبت تحديداً، بل تحدث في Python وJava وأي لغة تستخدم نفس صيغة الفاصلة العائمة. إذا كنت تتعامل مع أموال، فالأفضل أن تحوّل القيم إلى أعداد صحيحة (مثل القروش) أو تستخدم مكتبة عشرية متخصصة.
ما هو BigInt في جافا سكريبت ومتى يجب استخدامه؟
BigInt هو نوع رقمي بدائي منفصل مخصص للأعداد الصحيحة التي تتجاوز Number.MAX_SAFE_INTEGER (أي 2^53 - 1). يمكنك إنشاؤه بإضافة الحرف n في النهاية مثل 9007199254740993n، أو عبر استدعاء BigInt(value). استخدمه لمعرّفات قواعد البيانات بحجم 64 بت، أو في التشفير، أو في أي عملية حسابية تهمّك فيها الدقة أكثر من السرعة.
هل يمكن خلط Number مع BigInt في جافا سكريبت؟
لا، التعبير 1n + 1 يرمي خطأ TypeError: Cannot mix BigInt and other types. عليك التحويل بشكل صريح عبر BigInt(n) أو Number(b). أما عوامل المقارنة مثل < و== فتعمل بين النوعين، لكن === سيعيد false لأن النوعين مختلفان.