هذا المقال لمستوى: مبتدئ. لو لسه بتتعلم JavaScript وحَيّرك إن نفس القيمتين بيطلّعوا نتيجتين مختلفتين حسب العلامة اللي بينهم، ده المقال اللي يفك اللخبطة دي من جذورها.
ليه "5" + 3 بتطلّع "53" بينما "5" - 3 بتطلّع 2؟
لو كتبت "5" + 3 في JavaScript وانتظرت 8، هتتصدم لما تلاقي الناتج "53". وبعدها تكتب "5" - 3 وتلاقيه 2 بالظبط. نفس الرقمين، علامة مختلفة، عالمين مختلفين. ده مش عشوائي، وده مش bug. اسمه Type Coercion، يعني التحويل الضمني للأنواع، وفهمه هيوفّر عليك ساعات من مطاردة بوجات مش بتظهر في رسالة خطأ.
المشكلة باختصار
JavaScript لغة فيها أنواع بيانات: نص (string)، رقم (number)، وغيرهم. لكنها مش بتوقفك لما تخلط بينهم. بدل ما ترمي خطأ، بتحاول تخمّن قصدك وتحوّل نوع لنوع تاني من ورا ضهرك. المشكلة إن التخمين ده مفيد أوقات، وبيكسر حساباتك أوقات تانية. وأخطر حاجة إنه بيحصل بصمت، من غير أي تحذير.
يعني إيه Type Coercion؟ المثال الأول
تخيّل كاشير في سوبر ماركت. لو إديته صنفين وقلتله "اجمعهم"، هيحط واحد ورا التاني في كيس واحد. لكن لو إديته فلوس وقلتله "اطرح الخصم"، هيحسبها أرقام عادي. الكاشير بيقرأ نية العملية من العلامة اللي قلتهاله، مش من نوع اللي في إيدك.
JavaScript بتعمل نفس الحاجة. علامة الجمع + ليها معنيين: تجميع نصوص (concatenation) أو جمع أرقام. لمّا تلاقي طرف واحد على الأقل نص، بتقرّر إنك قصدك تجمّع نصوص، فبتحوّل الرقم 3 لنص "3" وتلزقه. لكن علامة الطرح - ليها معنى واحد بس: عملية حسابية. مفيش حاجة اسمها "طرح نصوص"، فبتحوّل النص "5" لرقم 5 وتطرح عادي.
دلوقتي الشرح العلمي بدقة: المحرّك بيطبّق عملية مجرّدة اسمها ToPrimitive على كل طرف، وبعدها العلامة بتفرض اتجاه التحويل. علامة + لو أي طرف طلع نص، بتستدعي ToString على الطرف التاني. أي علامة حسابية تانية (-، *، /، %) بتستدعي ToNumber على الطرفين. القاعدة دي مكتوبة حرفيًا في مواصفة ECMAScript تحت عمليات ToNumber وToString.
الكود اللي يثبتلك الكلام
افتح أي console (المتصفح أو Node) وجرّب ده بنفسك. كل سطر معاه ناتجه:
console.log("5" + 3); // "53" نص، لأن + لقى نص
console.log("5" - 3); // 2 رقم، لأن - حسابية
console.log("5" * "2"); // 10 الاتنين اتحولوا لأرقام
console.log(typeof ("5" + 3)); // "string"
console.log(typeof ("5" - 3)); // "number"
console.log(true + 1); // 2 true بتتحول لـ 1
console.log("" == 0); // true == بيعمل coercion كمان