مستوى المقال: مبتدئ. لو لسه في أول الطريق مع جافاسكريبت وبتتلخبط إمتى تكتب == وإمتى ===، المقال ده هيوريك اللي بيحصل تحت بالظبط، وهيوقّف عنك مفاجآت زي إن 0 == "" بترجّع true.
في جملة واحدة: == بيحوّل نوع القيم قبل ما يقارن، و=== بيقارن من غير أي تحويل. الفرق ده بيخبّي أخطاء بتعدّي من غير ما تاخد بالك، خصوصًا وانت بتتحقق من مدخلات المستخدم.
الفرق في سطر واحد قبل ما نغرق في التفاصيل
=== اسمه المساواة الصارمة (strict equality): لو النوع مختلف، النتيجة false على طول. == اسمه المساواة الفضفاضة (loose equality): لو النوع مختلف، بيحاول يوحّد النوع الأول وبعدين يقارن. المشكلة إن قواعد التوحيد دي مش بديهية، وده مصدر البق.
المشكلة باختصار
معظم المبتدئين بيكتبوا == بالعادة لأنها أقصر. بعدين بيتفاجئوا إن شرط زي if (input == false) بيتحقق مع قيم متوقعتهاش خالص: السلسلة الفاضية، الرقم صفر، وحتى مصفوفة فاضية. النتيجة: تحقق بيسمح بمدخل غلط، أو بيرفض مدخل صح.
الأول بمثال: مترجم على الحدود
تخيّل حدود بين بلدين. البلد الأولى بتتكلم أرقام، والتانية بتتكلم كلام. عايز تعرف هل الرسالة اللي جاية من هنا بتساوي الرسالة اللي جاية من هناك.
لو استخدمت ===، الحارس بيبصّ ويقول: "دي بتتكلم أرقام ودي بتتكلم كلام، مختلفين، خلاص مش متساويين". قرار سريع وواضح.
لو استخدمت ==، الحارس بيجيب مترجم. المترجم بياخد الرسالة الكلامية ويحوّلها لأرقام عشان يقدر يقارن. المشكلة إن المترجم ده بيتصرّف بقواعد غريبة: بيعتبر السلسلة الفاضية "" تساوي صفر، وبيعتبر الكلمة "0" برضه تساوي صفر. فيطلعلك إن حاجتين شكلهم مختلف تمامًا "متساويين".
المترجم هنا هو الـ Type Coercion: التحويل الضمني للنوع اللي جافاسكريبت بتعمله من ورا ظهرك.
دلوقتي بشكل علمي: خوارزمية المساواة المجرّدة
لما تكتب a == b، المواصفة (ECMAScript) بتمشي على خطوات محددة اسمها الـ Abstract Equality Comparison. المهم فيها:
- لو النوعين زي بعض، بتتصرّف زي
===بالظبط. - لو رقم مع سلسلة، السلسلة بتتحوّل لرقم.
- لو أي طرف
boolean، بيتحوّل لرقم الأول (trueتبقى 1 وfalseتبقى 0). - لو object مع رقم أو سلسلة، الـ object بيتحوّل لقيمة بدائية (نص أو رقم).
nullوundefinedبيساووا بعض بس، ومش بيساووا أي حاجة تانية.
الافتراض هنا إننا بنتكلم عن جافاسكريبت في المتصفح أو Node، وده السلوك القياسي المتوافق مع المواصفة، مش خاص بمكتبة معينة.
نمشي على [] == ![] خطوة بخطوة
ده أشهر مثال بيتلغبط فيه الناس، وبيطلع true. خلّينا نفكّه:
![]بتتحسب الأول. المصفوفة قيمتها المنطقيةtrue، فـ![]بتبقىfalse.- بقى عندنا
[] == false. الطرف الـ boolean بيتحوّل لرقم، يعني[] == 0. - المصفوفة
[]object، فبتتحوّل لقيمة بدائية. المصفوفة الفاضية بتبقى سلسلة فاضية"". بقى"" == 0. - السلسلة الفاضية بتتحوّل لرقم، و
""تساوي0. بقى0 == 0. - النتيجة
true.
خمس تحويلات ضمنية عشان توصل لنتيجة مبتتوقعهاش. ده بالظبط اللي بيخلي == صعب تتبّعه في الكود.
جرّبه بنفسك بكود شغّال
// افتح الكونسول في المتصفح أو node وجرّب
console.log(0 == ""); // true (تحويل غريب)
console.log(0 == "0"); // true
console.log("" == "0"); // false (النوعين نص، بلا تحويل)
console.log([] == ![]); // true (خمس تحويلات)
console.log(null == undefined); // true
console.log(null == 0); // false (null مبيساويش أرقام)
// نفس المقارنات بـ ===
console.log(0 === ""); // false
console.log(0 === "0"); // false
console.log(null === undefined); // false
لاحظ إن كل نتايج === منطقية ومتوقعة، وده السبب إن الفرق مش رفاهية.
سيناريو حقيقي بيوقعك
عندك فورم فيه خانة كمية، والقيمة اللي بتيجي من عنصر input دايمًا نص، حتى لو المستخدم كتب رقم. عايز ترفض لو الكمية صفر:
const qty = document.querySelector("#qty").value; // نص، مثلاً "0"
// الطريقة دي بتفشل
if (qty == 0) {
console.log("الكمية صفر");
}
المشكلة: الشرط ده بيتحقق مع "0" وبرضه مع السلسلة الفاضية "". يعني المستخدم اللي ساب الخانة فاضية بيتعامل زي اللي كتب صفر. البديل الصح: حوّل بنفسك وقارن صراحةً.
const qty = Number(document.querySelector("#qty").value);
if (Number.isNaN(qty)) {
console.log("مدخل مش رقم");
} else if (qty === 0) {
console.log("الكمية صفر فعلاً");
}
كده انت اللي بتتحكم في التحويل، مش المترجم الخفي.
الـ trade-off: تكسب إيه وتخسر إيه
لو مشيت على قاعدة "استخدم === دايمًا": بتكسب توقّع كامل للنتيجة وأخطاء أقل بكتير. بتخسر إنك محتاج تعمل التحويل بإيدك لما تحتاجه، وده سطر زيادة أحيانًا. المكسب أكبر من التكلفة في أغلب الحالات، عشان كده أدلة الأسلوب زي دليل Airbnb بتفرض قاعدة eqeqeq اللي بتمنع == أصلاً.
متى == مقبول فعلاً
فيه حالة واحدة بيستخدم فيها ناس محترفين == عن قصد: لما تعوز تشيك على null وundefined مع بعض في مقارنة واحدة. value == null بترجّع true لو القيمة null أو undefined، وبترجّع false لأي حاجة تانية. دي مختصرة ومقصودة. غير الحالة دي، ابعد عن ==.
الخطوة التالية
افتح أي ملف جافاسكريبت شغّال عندك، ودوّر على == و!=. غيّرهم لـ === و!== واختبر إن كل حاجة لسه شغالة. لو لقيت مكان اتكسر بعد التغيير، ده معناه إنه كان معتمد على تحويل ضمني خفي، وده بالظبط النوع من البق اللي بتتخلص منه. لو عايز تلزم نفسك بالقاعدة، فعّل قاعدة eqeqeq في ESLint.
المصادر
- MDN Web Docs — Equality comparisons and sameness: شرح الفرق بين
==و===وObject.is. - ECMA-262 (مواصفة ECMAScript) — قسم IsLooselyEqual (المساواة المجرّدة): الخطوات الرسمية اللي بتتبعها
==. - MDN Web Docs — Type coercion: تعريف التحويل الضمني للأنواع في جافاسكريبت.
- Airbnb JavaScript Style Guide — قاعدة Comparison Operators (eqeqeq): ليه بيفرضوا
===.