المعاملات هي الطريقة التي تُنجز بها التعابير عملها
أي سطر جافا سكريبت غير تافه مبني من معاملات جافا سكريبت (operators) — رموز تأخذ قيمة أو قيمتين وتُنتج قيمة جديدة. + للجمع، و=== للمقارنة، و&& لدمج القيم المنطقية، و? : للاختيار بين قيمتين. معظمها مألوف من لغات أخرى، لكن هناك حفنة منها تحمل خصائص مميزة في جافا سكريبت يستحق أن تعرفها منذ البداية.
سنمر عليها مجموعةً مجموعة، ونختم بتلك التي نادراً ما ستحتاجها لكنها تصنع الفارق حين تحتاجها فعلاً.
المعاملات الحسابية
الأساسيات تتصرف كما تتوقع تماماً:
هناك بعض النقاط التي تستحق الإشارة إليها:
- العامل
/ينفّذ دائمًا قسمة عشرية (floating-point). فـ7 / 2يساوي3.5وليس3. إذا أردت نتيجة صحيحة، استخدمMath.floor(7 / 2)أوMath.trunc(7 / 2). - العامل
%يُرجع باقي القسمة، لا القيمة الرياضية الحقيقية للـ modulo. فهو يحتفظ بإشارة المعامل الأيسر، ولهذا فإن-7 % 3يساوي-1وليس2. - العامل
+مُحمَّل بأكثر من وظيفة. إذا كان أحد الطرفين نصًا (string)، فإنه يقوم بالدمج بدلًا من الجمع:"3" + 1نتيجته"31". سنعود لهذه النقطة بعد قليل.
الزيادة والنقصان (Increment و Decrement)
الفرق بين count++ و ++count ما بيفرق إلا لما تستخدم قيمة التعبير في نفس السطر. أما لو كان سطر لحاله، فالاثنين بيعملوا نفس الشيء. ومعظم أدلة كتابة الكود بتفضّل count += 1 لأنها أوضح.
معامل + له وجهان مختلفان
هذه النقطة بتوقع الكل في الفخ مرة على الأقل:
إذا كان أحد الطرفين نصًا (string)، فإن العامل + يتحول إلى عامل دمج للنصوص ويُحوّل الطرف الآخر إلى نص أيضًا. أما بقية المعاملات الحسابية فتسلك المسار المعاكس تمامًا — إذ تُحوّل النصوص إلى أرقام:
الخلاصة: لما تبني نصوصًا، استخدم القوالب النصية (`price: ${5}`). ولما تشتغل على عمليات حسابية، تأكّد إنّ المدخلات فعلاً أرقام — الدالة Number(x) أو parseInt(x, 10) تحوّلها صراحةً.
معاملات المقارنة في جافا سكريبت
معاملات المقارنة دائمًا ترجع قيمة منطقية (true أو false). وفي جافا سكريبت لها نوعان: المقارنة الصارمة والمقارنة المرنة.
المعاملان === و !== هما المعاملان الصارمان — يتطلبان تطابق القيمة والنوع معًا. أما == و != فيقومان بتحويل الأنواع قبل المقارنة، وهذا ما يؤدي إلى مفاجآت مثل أن نتيجة null == undefined تكون true، أو أن [] == false تساوي true أيضًا. القاعدة الذهبية: اعتمد === و !== افتراضيًا. الاستثناء الوحيد الشائع هو x == null، فهو اختصار مفيد للسؤال: "هل x قيمته null أو undefined؟"
أما معاملات الترتيب فتعمل كما تتوقع مع الأرقام، ومع النصوص تقارن أبجديًا:
مقارنة السلاسل النصية تعتمد على ترميز الحروف (character codes)، لذلك فهي حساسة لحالة الأحرف. إذا كنت تحتاج ترتيباً يتماشى مع المنطق البشري، استخدم String.prototype.localeCompare.
المعاملات المنطقية في جافا سكريبت
تُستخدم && (و)، و|| (أو)، و! (النفي) لدمج القيم المنطقية — لكنها في الواقع أكثر إثارة للاهتمام مما قد توحي به جبر بوول البحت.
المفاجأة هنا: المعاملان && و || لا يُرجعان true أو false، بل يُرجعان أحد المعاملات نفسها. المعامل && يُرجع أول قيمة زائفة (falsy) يصادفها، أو آخر قيمة إذا كانت جميع القيم صادقة. أما || فيُرجع أول قيمة صادقة (truthy)، أو آخر قيمة إذا كانت كلها زائفة.
لهذا السبب سترى أنماطًا مثل const displayName = user.name || "Guest" — أي اختر أول قيمة ليست فارغة. الأسلوب مختصر، لكن انتبه: المعامل || يعتبر 0 و"" وfalse قيمًا تستدعي البديل. إذا كانت هذه القيم مقبولة لديك فعلًا، استخدم ?? بدلًا منه (كما سنرى بعد قليل).
كلا المعاملين يعملان أيضًا بأسلوب التقييم القصير (short-circuit): إذا حسم الطرف الأول النتيجة، فلن يُنفَّذ الطرف الآخر إطلاقًا.
معامل الدمج الصفري ??
المعامل ?? يشبه ||، لكنه لا يتفاعل إلا مع القيم null أو undefined فقط — أي أنه لا يتعامل مع 0 أو "" أو false على أنها قيم فارغة.
استخدم ?? عندما تكون القيمة المشروعة قد تكون falsy — مثل العدّادات أو السلاسل الفارغة أو القيمة false الصريحة. أما || فاستخدمه حين ترغب في أن تُعامَل أيّ قيمة falsy باعتبارها "غير موجودة". في الكود الحديث، يُعدّ ?? الخيار الأكثر أمانًا للقيم الاختيارية ذات القيم الافتراضية غير المعدومة.
معاملات الإسناد
المعامل = يقوم بالإسناد، بينما تجمع الصيغ المركّبة بين الإسناد ومعامل آخر:
توجد أيضًا معاملات إسناد منطقية — وهي ||= و &&= و ??= — تقوم بالإسناد فقط إذا كانت القيمة الحالية تستوفي شرطًا معينًا:
هذه المعاملات مفيدة جدًا لتعيين القيم الافتراضية دون الحاجة إلى كتابة جمل if طويلة ومكررة.
العامل الثلاثي في جافا سكريبت
الصيغة condition ? a : b هي النسخة التعبيرية من if/else، حيث تُرجع القيمة a إذا تحقق الشرط (truthy)، وإلا فإنها تُرجع b:
العامل الثلاثي (? :) مثالي عندما تحتاج إلى اختيار قيمة بشكل مختصر، لكنه يتحوّل إلى كابوس بمجرد أن تبدأ بتداخله. إذا وجدت نفسك تكتب شيئًا مثل a ? b : c ? d : e، فالأفضل أن تلجأ إلى if/else أو إلى كائن بحث (lookup object).
المعاملان typeof و instanceof
يُعيد typeof سلسلة نصية تصف نوع المُعامل الذي يُطبَّق عليه:
هناك مَطبّان يستحقان الحفظ: typeof null يُعيد "object" (خطأ من عام 1995 أصبح دائماً الآن)، والمصفوفات أيضاً تُرجِع "object". استخدم Array.isArray(x) للتحقق من المصفوفات و x === null للتحقق من null.
أما instanceof فيتحقق مما إذا كان الكائن قد أُنشئ من مُنشئ (constructor) مُعيّن:
الـ Spread والـ Rest يستخدمان نفس الرمز ...
سيظهر لك الرمز ... في موضعين مختلفين. عند استخدامه كـ spread، فإنه يقوم بتوسيع العنصر القابل للتكرار (iterable) إلى عناصر مفردة:
بوصفه معامل الباقي (rest)، فهو يجمع عدة قيم داخل مصفوفة واحدة، وغالبًا ما نستخدمه في معاملات الدوال أو في التفكيك (destructuring):
نفس الصياغة، لكن وظيفتان متعاكستان: المعامل spread يقوم بالـ_فرد_، أما rest فيقوم بالـ_تجميع_. والذي يحدد أيّهما يعمل هو السياق: داخل استدعاء دالة أو قيمة حرفية (literal) فهو فرد، أما داخل قائمة معاملات الدالة أو نمط تفكيك (destructuring) فهو تجميع.
أولوية المعاملات في جافا سكريبت (عند الشك، استخدم الأقواس)
لكل معامل أولوية تحدد أيّها يُنفَّذ أولاً عند خلط أكثر من معامل في التعبير الواحد. فالضرب أسبق من الجمع، والمقارنة أسبق من المعاملات المنطقية، وهكذا:
الجدول الكامل طويل، وما حد بيحفظه كله. في عادة بسيطة بتغطي 99% من الحالات: لما تخلط بين أكثر من معامل وما تكون متأكد من الترتيب، ضيف أقواس. الكود بيصير أوضح للقراءة وما بيعتمد على ذاكرة اللي بيقرأه.
المعاملات على مستوى البت (نادراً ما تحتاجها)
للإحاطة فقط: المعاملات & و | و ^ و ~ و << و >> و >>> تشتغل على التمثيل الثنائي للأعداد الصحيحة. بتصادفها في أكواد الرسوميات، والبروتوكولات منخفضة المستوى، وبعض واجهات الـ API اللي تعتمد على أقنعة البِتات (flag-bitmask).
حيلة شائعة لكن مشكوك فيها: n | 0 يقتطع الرقم ليصبح عددًا صحيحًا بـ 32 بت، وكان البعض يستغلها كبديل أسرع لـ Math.trunc. تجنّب ذلك — فـ Math.trunc أوضح ويعمل حتى مع الأرقام خارج نطاق الـ 32 بت.
التالي: if/else
المعاملات تُنتج قيمًا، وif/else يستعمل هذه القيم ليختار أيّ فرع من الشيفرة سيُنفَّذ. معظم ما ستفعله بمعاملات المقارنة والمعاملات المنطقية التي رأيناها أعلاه هو تمرير نتائجها إلى جُمل شرطية — وهذا بالضبط موضوع الصفحة التالية.
الأسئلة الشائعة
ما هي أهم المعاملات في جافا سكريبت؟
تنقسم معاملات جافا سكريبت إلى: حسابية (+, -, *, /, %, **)، مقارنة (===, !==, <, >)، منطقية (&&, ||, !)، وإسناد (=, +=, -=)، إضافة إلى معاملات خاصة مثل العامل الثلاثي ? : وtypeof ومعامل الدمج الصفري ??. هذه المعاملات هي اللبنات الأساسية لبناء التعبيرات والتحكم في تدفق الكود.
ما الفرق بين == و === في جافا سكريبت؟
=== يتحقق من تطابق القيمة والنوع معًا، أما == فيقوم بتحويل الأنواع قبل المقارنة، ولذلك تجد أن 0 == "0" يعطي true بينما 0 === "0" يعطي false. القاعدة: استخدم === دائمًا كخيار افتراضي، لأن قواعد التحويل في == دقيقة بما يكفي لتسبّب أخطاءً يصعب ملاحظتها حتى في مراجعة الكود.
ما وظيفة العامل الثلاثي في جافا سكريبت؟
condition ? a : b هو اختصار لسطر واحد يؤدي عمل if/else لكنه يُرجع قيمة. إذا كان condition صحيحًا (truthy) يرجع a، وإلا يرجع b. مفيد جدًا في التعبيرات الشرطية القصيرة مثل const label = count === 1 ? 'item' : 'items'، لكن تجنّب تداخل أكثر من عامل ثلاثي لأنه يصبح صعب القراءة بسرعة.
متى أستخدم ?? بدلًا من ||؟
|| يرجع إلى القيمة البديلة مع أي قيمة falsy، بما في ذلك 0 و"" وfalse. أما ?? فيرجع للبديل فقط عندما تكون القيمة null أو undefined. فإذا كنت تريد أن يحافظ count ?? 10 على القيمة 0 كقيمة مشروعة، فاستخدم ??. أما إذا كنت تريد فعلًا أن تؤدي أي قيمة falsy إلى تفعيل البديل، فـ || هو الخيار المناسب.