Menu
العربية

التحويل بين الأنواع في JavaScript: الضمني والصريح

كيف تتعامل JavaScript مع تحويل القيم بين الأنواع المختلفة: القواعد الضمنية التي توقع المبرمجين في الأخطاء، والتحويلات الصريحة التي يُفضَّل استخدامها، ومتى يعمل كل نوع منها.

نوعان من التحويل في جافا سكريبت

جافا سكريبت لغة متساهلة مع الأنواع. حين يستقبل أحد المعاملات قيمة من نوع "غير مناسب"، لا ترمي اللغة خطأً، بل تقوم بتحويل القيمة. هذا التحويل يُسمّى type coercion، وينقسم إلى نوعين:

  • التحويل الصريح (Explicit) — أنت من يطلبه بنفسه: Number("42"), String(99), Boolean(value).
  • التحويل الضمني (Implicit) — يحدث تلقائيًا بفعل أحد المعاملات دون أن تكتب أي دالة تحويل: "5" - 2, "" == 0, if (value).

النوعان في النهاية يستدعيان نفس قواعد التحويل الداخلية، والفارق الوحيد هو: هل أنت من قرّر التحويل، أم أن اللغة قرّرته نيابةً عنك؟ ومعظم النتائج المُربِكة في جافا سكريبت — مثل "5" + 1 === "51" و [] == false و null == undefined — مصدرها التحويل الضمني الذي يباغتك من حيث لا تحتسب.

index.js
Output
Click Run to see the output here.

النموذج الذهني: لمّا تكتب Number(...) أو String(...)، أنت تعرف بالضبط شو طالب من المحرّك. أما لمّا ما تكتبها، فكل معامل (operator) إله رأيه الخاص بكيفية التحويل، وهون بالضبط بتسكن معظم الباغات.

الأنواع الثلاثة المستهدفة في التحويل

تحويل الأنواع في جافا سكريبت بيصبّ دايمًا باتجاه واحد من ثلاثة أنواع بدائية: string، أو number، أو boolean. (في نوع رابع وهو bigint، بس ما بيصير التحويل إليه تلقائيًا من أنواع تانية.) وباقي قواعد التحويل في javascript كلها بتتفرّع من سؤال واحد: لأي نوع من هدول الثلاثة بيحاول المعامل يوصل؟

index.js
Output
Click Run to see the output here.

التحويل إلى نص في جافا سكريبت متسامح جدًا — فكل قيمة لها تمثيل نصي. لكن انتبه أن الكائنات (objects) تتحول إلى النص عديم الفائدة "[object Object]"، ولهذا السبب عندما تطبع كائنًا مدموجًا مع نص ("user: " + user) نادرًا ما ترى ما كنت تتوقعه. استخدم JSON.stringify أو القوالب النصية (template literals) مع تحديد الحقول التي تريدها بدلًا من ذلك.

التحويل إلى رقم في جافا سكريبت

التحويل إلى رقم أكثر صرامة. لا بد أن يكون النص على هيئة رقم فعلًا، وإلا ستحصل على NaN:

index.js
Output
Click Run to see the output here.

المفاجآت الموضوعة بين قوسين هي التي تستحق أن تحفظها جيدًا:

  • السلسلة الفارغة والسلاسل التي تحتوي على مسافات فقط تتحول إلى 0، وليس إلى NaN.
  • القيمة null تتحول إلى 0، أما undefined فتتحول إلى NaN.
  • المصفوفة الفارغة تتحول إلى 0، والمصفوفة ذات العنصر الواحد تتحول إلى قيمة ذلك العنصر نفسه، بينما المصفوفة التي تحتوي على أكثر من عنصر تتحول إلى NaN.

وإذا أردت استخراج رقم من نص يحتوي على رموز إضافية، فاستخدم parseInt أو parseFloat بدلًا من Number:

index.js
Output
Click Run to see the output here.

مرر دائمًا الأساس (10) إلى parseInt. لأنك لو أهملته، فالسلاسل التي تبدأ بـ "0x" ستُفسَّر كأرقام ست عشرية، وهذا نادرًا ما يكون مقصدك.

التحويل إلى Boolean

التحويل إلى Boolean هو الأبسط بين الأنواع الثلاثة. هناك قائمة قصيرة من القيم تتحول إلى false، وكل ما عداها يتحول إلى true.

القيم falsy هي:

  • false
  • 0, -0, 0n
  • "" (سلسلة فارغة)
  • null
  • undefined
  • NaN

هذه هي القائمة كاملة. أي قيمة أخرى — بما فيها "false" و "0" و [] و {} — تُعدّ truthy.

index.js
Output
Click Run to see the output here.

نتائج المصفوفة الفارغة والكائن الفارغ تُربك كثيرًا من القادمين من بايثون، حيث المجموعات الفارغة تُعتبر falsy. أما في جافا سكريبت فهي truthy — فإذا أردت أن تتحقق من "هل هذه المصفوفة فارغة؟"، افحص arr.length === 0 صراحةً.

التحويل إلى قيمة منطقية يحدث في كل مرة تظهر فيها قيمة في موضع يتوقع قيمة منطقية: if (...)، وwhile (...)، والعامل الثلاثي ? :، والعوامل المنطقية && و|| و!.

عامل + حالة خاصة

معظم العوامل الحسابية تُجبر معاملاتها على التحول إلى أرقام، لكن + مختلف — إذا كان أيّ طرف من طرفيه نصًا، فإن + يقوم بدمج النصوص (string concatenation)، وفي غير ذلك يُجري جمعًا عدديًا.

index.js
Output
Click Run to see the output here.

التقييم يتم من اليسار إلى اليمين. فالعبارة 1 + 2 + "3" تحسب أولًا 1 + 2 = 3، ثم 3 + "3" = "33". أما "1" + 2 + 3 فتبدأ كسلسلة نصية وتبقى كذلك: تصير "12"، ثم "123".

ولهذا السبب بالذات يكون بناء النصوص باستخدام + هشًّا وعرضة للأخطاء. القوالب النصية (Template literals) لا تعاني من هذه المشكلة:

index.js
Output
Click Run to see the output here.

يقوم القالب الحرفي (template literal) بتقييم count + 1 كتعبير رقمي مستقل، ثم يُدرج الناتج داخل النص. وبهذا نتخلّص من أي تحويل ضمني غير متوقّع.

التحويل الصريح أفضل من الضمني

عندما تحتاج فعلاً إلى تحويل نوع ما، فاكتب ذلك بوضوح. كلفة بضعة أحرف إضافية مقابل إزالة أي غموض عن القارئ التالي للكود:

index.js
Output
Click Run to see the output here.

نفس الكلام ينطبق على القيم المنطقية. الصيغة !!value شغّالة ومنتشرة بين المطورين، لكن Boolean(value) تعبّر بوضوح عن المقصود:

index.js
Output
Click Run to see the output here.

قاعدة منطقية: استخدم التحويل الصريح في منطق التطبيق، واحتفظ بالصيغ المختصرة (+x, !!x) للمواضع التي تكون فيها الإيجاز ضروريًا ويكون القصد واضحًا من السياق.

عامل == يعتمد على التحويل الضمني بشكل كبير

عوامل المساواة هي المصدر الأكبر للمفاجآت المتعلقة بالتحويل الضمني. فعامل == يقوم بتحويل القيم قبل المقارنة، بينما === لا يفعل ذلك — وهنا يكمن الفرق بين == و === في javascript.

index.js
Output
Click Run to see the output here.

كل true في الأمثلة السابقة هو نتيجة سلسلة تحويلات متعددة الخطوات، ونادراً ما يستطيع أي مطور استذكارها عن ظهر قلب. وهنا تكمن المشكلة: الكود الذي يعمل بالصدفة هو كود سينكسر لاحقاً. سنتناول القواعد الكاملة للمساواة في الصفحة التالية، لكن الخلاصة الآن: استخدم === افتراضياً، ولا تلجأ إلى == إلا في حالة واحدة محددة وهي التعبير x == null (الذي يلتقط كلاً من null و undefined معاً).

نجمع كل الخيوط معاً

إليك مثالاً عملياً يوضح متى يكون تحويل الأنواع مفيداً ومتى يتحول إلى كارثة:

index.js
Output
Click Run to see the output here.

لاحظ الاستدعاء الثاني. الدالة Number("") تُرجع 0 وليس NaN، وبالتالي فإن parsePrice("") تُرجع 0، وهذه على الأرجح ليست النتيجة التي يتوقعها من يستخدم هذه الدالة. إذا كنت تريد رفض المدخلات الفارغة، أضف تحققًا صريحًا لذلك:

index.js
Output
Click Run to see the output here.

معرفة أي القيم تتحوّل إلى 0 وأيّها إلى NaN هي بالضبط النوع من المعلومات اللي بيقيك من باغات غبية وصعبة التتبّع لاحقًا.

خلاصة الدرس

  • تحويل الأنواع في جافا سكريبت بيحوّل القيمة إلى نص أو رقم أو قيمة منطقية حسب العامل المستخدم.
  • عامل + مع أي نص بيتحوّل إلى دمج نصوص (concatenation)، أما باقي العوامل الحسابية فبتحوّل الطرفين إلى أرقام.
  • القيم الـ falsy عددها محدود ومعروف — احفظها. وأي حاجة تانية تُعتبر truthy، بما في ذلك [] و {}.
  • Number("") بيساوي 0، و Number([]) بيساوي 0، و Number(null) بيساوي 0 — لكن Number(undefined) بيساوي NaN. الفروق دي بتظهر في باغات حقيقية.
  • استخدم Number(x) و String(x) و Boolean(x) بدل الاعتماد على الحيل الضمنية الذكية. نفسك في المستقبل هتشكرك.

التالي: عوامل المساواة

كل ما يفعله التحويل الضمني داخل عمليات المقارنة موجود في ==. الصفحة الجاية هنقارن بين == و === و Object.is، ونشوف الحالة الوحيدة اللي لسه فيها == مفيد، وليه أدوات الـ linter بتحذّر من الصيغة المرنة افتراضيًا.

الأسئلة الشائعة

ما المقصود بتحويل الأنواع (Type Coercion) في JavaScript؟

تحويل الأنواع يعني أن JavaScript تقوم تلقائيًا بتحويل قيمة من نوع إلى آخر: رقم يتحول إلى نص، نص إلى رقم، أو أي قيمة إلى قيمة منطقية. يحدث هذا بشكل ضمني عندما تصطدم معاملات مثل + و == و if بقيمة ليست من النوع المتوقع، ويحدث بشكل صريح عندما تستدعي أنت بنفسك Number(x) أو String(x) أو Boolean(x).

ما الفرق بين التحويل الضمني والتحويل الصريح؟

التحويل الصريح هو أن تستدعي دالة التحويل بشكل مقصود، مثل Number("42") و String(99) و Boolean(value). أما التحويل الضمني فيحدث من وراء ظهرك بسبب أحد المعاملات: مثلًا "5" - 2 تعطيك 3، بينما "5" + 2 تعطيك "52". التحويل الصريح واضح ويمكن التنبؤ به، أما الضمني فهو السبب الرئيسي لمعظم الأخطاء من نوع «لماذا ظهرت لي NaN فجأة؟».

كيف أحوّل نصًا إلى رقم في JavaScript؟

استخدم Number("42") للتحويل الصارم (يُرجع NaN إذا لم يكن النص رقمًا صالحًا)، أو استخدم parseInt("42px", 10) و parseFloat("3.14em") إذا كنت تريد استخراج الرقم من بداية نص فوضوي. كما أن معامل + الأحادي (مثل +"42") يعمل بنفس طريقة Number()، لكنه يسهل تفويته عند مراجعة الكود بسرعة.

لماذا تُرجع [] + [] نصًا فارغًا؟

لأن معامل + ليس له معنى رقمي مع مصفوفتين، فتلجأ JavaScript إلى تحويل الطرفين إلى نصوص. والمصفوفة تُحوَّل إلى نص عن طريق ربط عناصرها بفواصل، والمصفوفة الفارغة تُنتج "". وبذلك تصبح العملية "" + "" والنتيجة "". حيلة طريفة، لكنها سبب وجيه لكي لا تعتمد أبدًا على + مع القيم غير البدائية.

تعلّم البرمجة مع Coddy

ابدأ الآن