في آخر كل شهر تسأل نفسك نفس السؤال: أين ذهب الراتب؟ فتفتح تطبيق البنك وتمر على عشرات العمليات، أو تفتح جدول Excel وتجمع الأعمدة بيدك، ثم تقرر أنك سوف تكون أكثر انتباهاً الشهر القادم. ولنفرض أنك بدلاً من ذلك قررت أن تكتب برنامجاً صغيراً بنفسك: تعطيه قائمة مصاريفك، فيجمعها بحسب الفئة، ويخبرك كم صرفت على السكن والطعام والمواصلات، وهل تجاوزت الميزانية أم لا. وهذا البرنامج على بساطته يحتاج كل الأدوات الأساسية في أي لغة برمجة: مكاناً تحفظ فيه الأرقام، وطريقة تقرر بها، وطريقة تكرر بها نفس الخطوة على كل مصروف، وطريقة تجمع بها الخطوات تحت اسم واحد تستدعيه متى احتجت.
وهذه الأدوات هي موضوع هذا المقال: المتغيرات Variables، والأنواع Types، والشروط Conditions، والحلقات Loops، والدوال Functions. وسوف نبني برنامج المصاريف خطوة بخطوة، وفي كل خطوة نكتب الكود بلغتي Python وJavaScript جنباً إلى جنب ونشغله ونعرض المخرج الحقيقي، والسبب أن رؤية نفس الفكرة بلغتين تعلمك أن الفكرة هي المهمة وأن الكتابة تفاصيل، وتريك أيضاً أين تختلف اللغتان اختلافاً يسبب أخطاء حقيقية. وفي آخر المقال سوف نرتكب أشهر أخطاء المبتدئين عمداً ونقرأ رسائلها كما تظهر بالضبط. وقد جهزنا البيئة التي تشغل فيها هذه الأمثلة في مقال بيئة البرمجة للمبتدئين، وتعلمنا كيف نفكر في المسألة قبل كتابتها في مقال كيف تفكر كمبرمج.
وسوف نناقش في هذا المقال ما يلي:
- كيف تشغل الأمثلة، ولماذا Python وJavaScript معاً.
- المتغيرات Variables والأنواع Types: أين يحفظ البرنامج القيم، ولماذا يهم نوع كل قيمة.
- العمليات Operators، وكيف تختلف اللغتان عندما تخلط نصاً برقم.
- الشروط Conditions والقيم المنطقية Boolean، وأهمية حد الشرط.
- القوائم Lists والحلقات Loops بنوعيها، والحلقة التي لا تنتهي.
- الدوال Functions والمعاملات Parameters والقيمة المعادة Return، والفرق بين الطباعة والإعادة.
- البرنامج الكامل بلغتين، ثم أخطاء المبتدئين الحقيقية وكيف تقرأ رسالة كل خطأ.
- جدول مقارنة سريع بين اللغتين، وقائمة تحقق، وتدريب عملي.
كيف تشغل الأمثلة؟ Python و Node جنباً إلى جنب
كل مثال في هذا المقال ملفان: ملف .py تشغله بالأمر python3، وملف .js تشغله بالأمر node. وإذا كانت اللغتان مثبتتين على جهازك فهذا يكفي. أما نحن فشغلنا الأمثلة داخل Container واحد فيه Python 3.13 وNode 26 معاً، حتى تكون المخرجات متطابقة عند كل قارئ، وبنيناه بالملف التالي:
FROM node:26-slim AS node
FROM python:3.13-slim
# the official Node binary needs libatomic1, which python:3.13-slim does not ship
RUN apt-get update && apt-get install -y --no-install-recommends libatomic1 && rm -rf /var/lib/apt/lists/*
COPY --from=node /usr/local/bin/node /usr/local/bin/node
ENV PYTHONUNBUFFERED=1
WORKDIR /labولاحظ التعليق في السطر الثالث، فهو مشكلة حقيقية ظهرت لنا في أول تشغيل: نسخنا ملف node من الـ Image الرسمية لـ Node إلى Image الخاصة بـ Python، فرفض العمل برسالة error while loading shared libraries: libatomic.so.1: cannot open shared object file، لأن البرنامج يحتاج مكتبة نظام موجودة في Image الخاصة بـ Node وغير موجودة في Image الخاصة بـ Python. وهذا نفس نوع المشكلة الذي شرحناه في مقال Hello World عن المكتبات المشتركة. ثم شغلنا كل الأمثلة بسكربت واحد:
docker build -t first-steps .
docker run --rm -v "$PWD":/lab -w /lab first-steps sh run.sh > run.out 2>&1وأول سطر في المخرج يؤكد الإصدارين: python: Python 3.13.17 node: v26.13.0. وقد يتساءل البعض: لماذا نتعلم لغتين معاً بدل أن نركز على واحدة؟ والإجابة أننا لا نطلب منك أن تحفظ اللغتين، فاختر واحدة وركز عليها، ولكن ضع الأخرى بجانبها وأنت تقرأ، لأن كل فكرة تراها مكتوبة بطريقتين تفهمها كفكرة وليس كسطر محفوظ. وPython الأسهل في القراءة للمبتدئ وتستخدم في التحليل والأتمتة والذكاء الاصطناعي، وJavaScript لغة المتصفح التي لا مفر منها في تطوير الويب، والانتقال من إحداهما إلى الأخرى بعد هذا المقال سوف يكون أسهل بكثير مما تتوقع.
البرنامج تعليمات تنفذ بالترتيب Statements
قبل أول متغير تحتاج أن تعرف قاعدة واحدة تحكم كل ما سيأتي: البرنامج سلسلة من التعليمات Statements، والمفسر ينفذها واحدة بعد الأخرى من أعلى الملف إلى أسفله، ولا يرجع إلى سطر سابق ولا يقفز إلى سطر لاحق إلا إذا طلبت منه ذلك صراحة بشرط أو حلقة أو استدعاء دالة. وهذا يعني أن ترتيب الأسطر جزء من معنى البرنامج: إذا استخدمت متغيراً في السطر الثالث وعرفته في السطر الخامس فلن يعرف البرنامج عنه شيئاً عندما يصل إلى السطر الثالث. وكل ما في هذا المقال، من الشروط إلى الحلقات إلى الدوال، ليس إلا طرقاً لتغيير هذا الترتيب البسيط: الشرط يتجاوز أسطراً، والحلقة تعيد أسطراً، والدالة تقفز إلى أسطر في مكان آخر ثم ترجع.
وفي أي سطر يمكن أن تكتب تعليقاً Comment يتجاهله المفسر تماماً، ويبدأ بعلامة # في Python وبعلامتي // في JavaScript. والتعليق مكتوب لك ولزملائك وليس للحاسب، والتعليق الجيد يشرح لماذا كتبت السطر بهذا الشكل وليس ماذا يفعل، فالكود نفسه يقول ماذا يفعل. وسوف ترى في مثال الـ Dockerfile أعلاه تعليقاً من هذا النوع: لا يقول «ثبت libatomic1» وإنما يقول لماذا احتجنا تثبيتها، وهذا ما ستحتاج أن تعرفه بعد سنة عندما تتساءل هل يمكن حذف هذا السطر.
المتغيرات Variables: أسماء تضعها على القيم
البرنامج يحتاج أن يتذكر أشياء: الميزانية، وما صرفته، واسمك. والمتغير Variable هو اسم تختاره وتربطه بقيمة، ثم تستخدم الاسم بدل القيمة في بقية البرنامج. والمثال التالي أول خطوة في برنامج المصاريف:
name = "سارة"
budget = 3000
spent = 1250.5
paid_rent = True
print(name, budget, spent, paid_rent)
print(type(name), type(budget), type(spent), type(paid_rent))
remaining = budget - spent
print("remaining:", remaining)
budget = budget + 500
print("new budget:", budget)const name = "سارة";
let budget = 3000;
const spent = 1250.5;
const paidRent = true;
console.log(name, budget, spent, paidRent);
console.log(typeof name, typeof budget, typeof spent, typeof paidRent);
const remaining = budget - spent;
console.log("remaining:", remaining);
budget = budget + 500;
console.log("new budget:", budget);والمخرج من اللغتين كما يلي، Python أولاً ثم JavaScript:
سارة 3000 1250.5 True
<class 'str'> <class 'int'> <class 'float'> <class 'bool'>
remaining: 1749.5
new budget: 3500
سارة 3000 1250.5 true
string number number boolean
remaining: 1749.5
new budget: 3500في الكود والمخرج أعلاه لاحظ التالي:
- علامة
=ليست مساواة كما في الرياضيات، وإنما إسناد Assignment: احسب ما على اليمين، ثم اربط الاسم الذي على اليسار بالنتيجة. ولذلك السطرbudget = budget + 500منطقي تماماً في البرمجة ومستحيل في الرياضيات: خذ القيمة الحالية 3000 وأضف 500، ثم اجعل الاسم يشير إلى 3500. - في JavaScript تعرف المتغير أول مرة بكلمة
letإذا كانت قيمته سوف تتغير، أوconstإذا لن تتغير، ولذلك عرفناbudgetبـletلأننا غيرناه. أما Python فلا تحتاج كلمة تعريف، فأول إسناد ينشئ المتغير. - الاسم
paid_rentفي Python صارpaidRentفي JavaScript، وهذا ليس شرطاً في اللغة وإنما عرف يتبعه أغلب المبرمجين: الكلمات تفصل بشرطة سفلية في Python كما يوصي دليل الأسلوب PEP 8، وبحرف كبير في JavaScript. والتزم بعرف لغتك لأن من يقرأ كودك يتوقعه. - القيمة المنطقية تكتب
Trueبحرف كبير في Python وtrueبحرف صغير في JavaScript، ولو كتبتtrueفي Python لظهر خطأ أن الاسم غير معرف.
والصورة التالية تبين ما يحدث في الذاكرة بشكل مبسط: كل اسم يشير إلى قيمة، وعندما أسندنا قيمة جديدة إلى budget تحرك الاسم إلى القيمة 3500 وبقيت 3000 بلا اسم، فتتخلص منها اللغة لاحقاً. وإذا أردت أن تعرف أين تعيش هذه القيم فعلاً في الذاكرة وكيف تتخلص اللغة منها فقد شرحنا ذلك في مقال الذاكرة للمبرمجين.

ولأسماء المتغيرات قواعد بسيطة: تبدأ بحرف أو شرطة سفلية وليس برقم، ولا تحتوي مسافات، ولا تكون كلمة محجوزة في اللغة مثل if أو for، والحروف الكبيرة والصغيرة مختلفة، فـ Budget وbudget متغيران مختلفان. واللغتان تسمحان تقنياً بأسماء عربية، لكن لا تفعل ذلك، والسبب أن أدوات كثيرة ولوحات مفاتيح زملائك وكل المكتبات التي ستستخدمها تفترض أسماء إنجليزية، والنصوص العربية مكانها داخل علامات التنصيص كقيم. والأهم من القواعد أن تختار أسماء تشرح نفسها: remaining أفضل من r، وpaid_rent أفضل من flag2، لأنك سوف تقرأ هذا الكود بعد شهر ولن تتذكر ما كان في رأسك.
الأنواع Types: لماذا يهم نوع القيمة؟
لاحظ في المخرج أن كل قيمة لها نوع Type، والدالة type في Python والكلمة typeof في JavaScript تخبرانك به. والأنواع الأساسية التي سوف تستخدمها كل يوم أربعة:
- النص String: أي حروف بين علامتي تنصيص، مثل
"سارة"، واسمهstrفي Python وstringفي JavaScript. - العدد الصحيح Integer: رقم بلا كسور مثل 3000، واسمه
intفي Python. - العدد العشري Float: رقم بكسور مثل 1250.5، واسمه
floatفي Python. أما JavaScript فلا تفرق بين الاثنين، وكل الأرقام عندها من نوع واحد اسمهnumber، وهذا الفرق يظهر في نتائج القسمة كما سنرى. - القيمة المنطقية Boolean: إما صح أو خطأ،
TrueوFalseفي Python، وtrueوfalseفي JavaScript، وهي نتيجة أي مقارنة.
وهناك نوع خامس يعني «لا توجد قيمة»: اسمه None في Python، وفي JavaScript اثنان هما undefined وnull، وسوف تقابله في قسم الدوال. ولماذا يهم النوع؟ لأن نفس العملية تعني أشياء مختلفة بحسب نوع القيم، والسطر التالي من نفس المثال يوضح ذلك:
print(7 / 2, 7 // 2, 7 % 2, 2 ** 10)
print("10" + "5", int("10") + 5, str(10) + "5")console.log(7 / 2, Math.floor(7 / 2), 7 % 2, 2 ** 10);
console.log("10" + "5", Number("10") + 5, String(10) + "5");3.5 3 1 1024
105 15 105والمخرج متطابق في اللغتين. والسطر الأول يبين عمليات الحساب: القسمة العادية / تعطي 3.5، والقسمة الصحيحة تعطي 3 وهي عامل // في Python ودالة Math.floor في JavaScript، وباقي القسمة % يعطي 1، والأس ** يعطي 1024. أما السطر الثاني فهو الأهم: "10" + "5" أعطى 105 وليس 15، لأن + بين نصين تعني اللصق Concatenation وليس الجمع، والنص "10" ليس الرقم 10 حتى لو بدا مثله. ولكي تجمع يجب أن تحول النص إلى رقم أولاً بـ int أو Number، وهذا التحويل Type Conversion سوف تحتاجه كثيراً، لأن كل ما يأتي من المستخدم أو من ملف أو من الإنترنت يصل نصاً حتى لو كان رقماً.
وهنا تبدأ اللغتان في الاختلاف، والجدول التالي يجمع نتائج حقيقية من التجربة لعبارات تكتبها في أسبوعك الأول:

والقاعدة التي تلخص الجدول: Python لغة صارمة في الأنواع، فإذا طلبت منها أن تجمع نصاً ورقماً توقفت وقالت لك إنها لا تعرف ماذا تقصد، أما JavaScript فتحاول أن تخمن ما تقصده، فتحول الرقم إلى نص عند الجمع، وتحول النص إلى رقم عند الطرح، فيعطي "5" + 2 النتيجة 52 ويعطي "5" - 2 النتيجة 3. وهذا السلوك موصوف بدقة في المواصفة وفي دليل JavaScript على MDN، لكنه مصدر أخطاء لا تنتهي للمبتدئين، والسبب أن البرنامج لا يتوقف وإنما يكمل بقيمة خاطئة. وآخر سطر في الجدول، 0.1 + 0.2، يعطي نفس النتيجة الغريبة في اللغتين لأن السبب ليس في اللغة وإنما في طريقة تخزين الكسور في الحاسب، وقد شرحنا ذلك في مقال الأرقام العشرية Floating Point من الداخل، والخلاصة لبرنامج المصاريف أن تقرب المبالغ عند العرض بـ round أو toFixed.
ومن المفاجآت التي ظهرت لنا في التجربة وتهم القارئ العربي تحديداً: الدالة int("١٢٠") في Python أعطت 120، لأنها تقبل الأرقام العربية الهندية كما تقبل الأرقام الغربية بحسب وثائق الدوال المدمجة، أما Number("١٢٠") في JavaScript فأعطت NaN، أي «ليس رقماً» Not a Number. فإذا كان برنامجك يستقبل أرقاماً يكتبها مستخدمون عرب فلا تفترض أن اللغتين تتصرفان بنفس الطريقة، وجرب بنفسك.
العمليات Operators: المقارنة والمنطق
قبل أن نكتب أول شرط نحتاج أدواته. فالمقارنة Comparison بين قيمتين تعطي دائماً قيمة منطقية، والعوامل في اللغتين متشابهة: < أصغر، و> أكبر، و<= و>=، و!= لا يساوي. والفرق في المساواة: Python تستخدم ==، أما JavaScript ففيها == و===، والأولى تحول الأنواع قبل المقارنة والثانية لا تحول. ونتيجة التجربة توضح الفرق:
console.log("5" == 5, "5" === 5, 0 == "", null == undefined);true false true trueفالنص "5" يساوي الرقم 5 بـ ==، والصفر يساوي النص الفارغ، وهذا نادراً ما يكون ما تقصده. ولذلك القاعدة في JavaScript بسيطة: استخدم === و!== دائماً، وانس وجود == إلا إذا كنت تعرف لماذا تحتاجها، والتفاصيل كاملة في صفحة المقارنة والمساواة على MDN. أما العوامل المنطقية Logical Operators التي تجمع شرطين فهي كلمات في Python: and وor وnot، ورموز في JavaScript: && و|| و!، والمعنى واحد.
الشروط Conditions: البرنامج يقرر
الآن نجعل برنامج المصاريف يقرر: إذا تجاوزت الميزانية يحذرك، وإذا بقي أقل من 10% منها ينبهك، وإلا يطمئنك. والشرط Condition يعني أن جزءاً من الكود ينفذ فقط إذا كانت عبارة منطقية صحيحة:
budget = 3000
spent = 2700
remaining = budget - spent
if remaining < 0:
print("تجاوزت الميزانية بمقدار", -remaining)
elif remaining < budget * 0.1:
print("انتبه: بقي أقل من 10% من الميزانية:", remaining)
else:
print("وضعك جيد، المتبقي:", remaining)
is_weekend = True
has_coupon = False
if is_weekend and not has_coupon:
print("لا خصم اليوم")
print(remaining < 500, remaining == 300, remaining != 300)const budget = 3000;
const spent = 2700;
const remaining = budget - spent;
if (remaining < 0) {
console.log("تجاوزت الميزانية بمقدار", -remaining);
} else if (remaining < budget * 0.1) {
console.log("انتبه: بقي أقل من 10% من الميزانية:", remaining);
} else {
console.log("وضعك جيد، المتبقي:", remaining);
}
const isWeekend = true;
const hasCoupon = false;
if (isWeekend && !hasCoupon) {
console.log("لا خصم اليوم");
}
console.log(remaining < 500, remaining === 300, remaining !== 300);وضعك جيد، المتبقي: 300
لا خصم اليوم
True True Falseوالمخرج في JavaScript نفسه مع true true false بحروف صغيرة. وفي الكود أعلاه لاحظ التالي:
- اللغتان تفحصان الشروط بالترتيب من الأعلى، وأول شرط صحيح ينفذ ما تحته ثم يتجاوز البرنامج كل البقية، و
elseتنفذ فقط إذا لم يصح أي شرط. والكلمةelifفي Python هيelse ifفي JavaScript. - Python تحدد الكود الذي يتبع الشرط بالإزاحة Indentation، أي المسافات في أول السطر، وبالنقطتين في آخر سطر الشرط. أما JavaScript فتحدده بالأقواس المعقوفة
{ }وتضع الشرط نفسه بين قوسين. ولذلك الإزاحة في Python جزء من معنى الكود وليست تجميلاً، وسوف ترى في قسم الأخطاء ماذا يحدث إذا نسيتها. - المتبقي كان 300 بالضبط، وعشر الميزانية 300 أيضاً، فلم يظهر التنبيه لأن 300 ليست أصغر من 300، فوصلنا إلى
else.
والنقطة الأخيرة تستحق وقفة، والصورة التالية تبين المسار الذي سلكه البرنامج:

هل كان يجب أن يظهر التنبيه عندما يكون المتبقي 10% بالضبط؟ هذا ليس سؤالاً برمجياً وإنما قرار في فهم المسألة، والكود سوف ينفذ ما كتبته حرفياً، فإذا أردت أن يشمل التنبيه الحد نفسه فاكتب <=. وأغلب أخطاء الشروط في البرامج الحقيقية ليست شرطاً خاطئاً تماماً، وإنما شرط صحيح إلا عند حده، ولذلك عندما تختبر أي شرط جرب القيمة التي على الحد بالضبط، وقيمة أقل منها بقليل، وقيمة أكبر منها بقليل.
القوائم Lists والحلقات Loops: كرر الخطوة على كل مصروف
مصاريف الشهر ليست رقماً واحداً وإنما قائمة، والقائمة List في Python أو المصفوفة Array في JavaScript تحفظ عدة قيم بالترتيب تحت اسم واحد، وتكتب بين قوسين مربعين. وكل عنصر له رقم موقع Index يبدأ من صفر وليس من واحد، فالعنصر الأول موقعه 0. والحلقة Loop تكرر نفس الخطوة على كل عنصر، أو تكرر حتى يتحقق شرط، والمثال التالي يجمع المصاريف ويعرضها ثم يجيب عن سؤال: كم مصروفاً أستطيع أن أدفع قبل أن أتجاوز 1500؟
expenses = [450, 120.5, 80, 900, 35]
print("count:", len(expenses), "first:", expenses[0], "last:", expenses[-1])
total = 0
for amount in expenses:
total = total + amount
print("total:", total)
for i in range(len(expenses)):
print(i, expenses[i])
budget = 1500
spent = 0
day = 0
while spent + expenses[day] <= budget:
spent += expenses[day]
day += 1
print("stopped before item", day, "spent", spent)
big = []
for amount in expenses:
if amount < 100:
continue
big.append(amount)
print("100 or more:", big)const expenses = [450, 120.5, 80, 900, 35];
console.log("count:", expenses.length, "first:", expenses[0], "last:", expenses[expenses.length - 1]);
let total = 0;
for (const amount of expenses) {
total = total + amount;
}
console.log("total:", total);
for (let i = 0; i < expenses.length; i++) {
console.log(i, expenses[i]);
}
const budget = 1500;
let spent = 0;
let day = 0;
while (spent + expenses[day] <= budget) {
spent += expenses[day];
day++;
}
console.log("stopped before item", day, "spent", spent);
const big = [];
for (const amount of expenses) {
if (amount < 100) continue;
big.push(amount);
}
console.log("100 or more:", big);والمخرج متطابق في اللغتين إلا في شكل طباعة القائمة في آخر سطر:
count: 5 first: 450 last: 35
total: 1585.5
0 450
1 120.5
2 80
3 900
4 35
stopped before item 3 spent 650.5
100 or more: [450, 120.5, 900]في الكود والمخرج أعلاه لاحظ التالي:
- آخر عنصر في Python يمكن أن تصل إليه بالموقع
-1، أما JavaScript فتحتاجexpenses[expenses.length - 1]، لأن الموقع السالب لا يعني شيئاً فيها. - الحلقة الأولى
for amount in expensesفي Python وfor (const amount of expenses)في JavaScript تعطيك كل عنصر بنفسه، وهي الشكل الذي تستخدمه في أغلب الأحوال. ولاحظ أنfor ... inفي JavaScript شيء آخر يعطيك المواقع وليس القيم، وهذا فخ معروف لمن ينتقل من Python. - الحلقة الثانية تعطيك الموقع
iوتصل إلى العنصر من خلاله، وتحتاجها عندما يهمك الموقع نفسه. والدالةrange(len(expenses))في Python تعطي الأرقام من 0 إلى 4، والحلقة الثلاثية في JavaScript تقول ابدأ من 0، واستمر ما دامiأصغر من الطول، وزد واحداً بعد كل دورة. - الحلقة
whileلا تعرف مسبقاً كم مرة سوف تدور، وإنما تكرر ما دام الشرط صحيحاً، وتوقفت قبل العنصر الذي موقعه 3 لأن إضافة 900 كانت ستجعل المجموع 1550.5. - الكلمة
continueتتجاوز بقية الدورة الحالية وتنتقل إلى العنصر التالي، فلم يدخل 80 و35 في قائمة المصاريف الكبيرة. وأختهاbreakتخرج من الحلقة كلها.

وفي while خطر لا يوجد في for: إذا نسيت أن تغير المتغير الذي يعتمد عليه الشرط بقي الشرط صحيحاً إلى الأبد، وهذه الحلقة التي لا تنتهي Infinite Loop. وقد كتبنا عمداً حلقة تطبع اليوم دون أن تزيد العداد، وشغلناها مع الأمر timeout 2 الذي يوقف أي برنامج بعد ثانيتين:
i = 0
while i < 3:
print("day", i)[exit 124]
lines printed in 2 seconds: 56661
day 0
day 0
day 0طبعت الحلقة 56,661 سطراً في ثانيتين، كلها day 0، ثم أوقفها timeout برمز الخروج 124 الذي يعني أن الوقت انتهى. وعلى جهازك لن يوقفها أحد، فإذا رأيت برنامجك يطبع بلا توقف أو يبدو معلقاً فاضغط Ctrl+C في الطرفية، ثم ابحث عن الحلقة التي لا يتغير شرطها. وتفاصيل الحلقات في اللغتين في دليل Python لأدوات التحكم ودليل الحلقات على MDN.
الدوال Functions: اكتب الخطوات مرة واستدعها كثيراً
جمع قائمة من الأرقام عملية سوف تحتاجها في أكثر من مكان: لمصاريف الطعام، ولمصاريف المواصلات، وللشهر كله. والحل البسيط أن تنسخ الحلقة في كل مكان، وهذا يعمل حتى تكتشف خطأ فيها فتحتاج أن تصلحه في خمسة أماكن، وتنسى واحداً. والحل الصحيح أن تضع الخطوات في دالة Function لها اسم، وتعطيها ما تحتاجه عبر المعاملات Parameters، وتعيد لك النتيجة بالكلمة return:
def total(items):
s = 0
for x in items:
s += x
return s
def with_vat(amount, rate=0.15):
return round(amount * (1 + rate), 2)
def show(amount):
print("المبلغ:", amount)
food = [35, 22.5, 60]
print(total(food))
print(with_vat(100), with_vat(100, rate=0.05))
result = show(10)
print("show returned:", result)
print(total([]))function total(items) {
let s = 0;
for (const x of items) {
s += x;
}
return s;
}
function withVat(amount, rate = 0.15) {
return Math.round(amount * (1 + rate) * 100) / 100;
}
function show(amount) {
console.log("المبلغ:", amount);
}
const food = [35, 22.5, 60];
console.log(total(food));
console.log(withVat(100), withVat(100, 0.05));
const result = show(10);
console.log("show returned:", result);
console.log(total([]));117.5
115.0 105.0
المبلغ: 10
show returned: None
0
117.5
115 105
المبلغ: 10
show returned: undefined
0في الكود والمخرج أعلاه لاحظ التالي:
- الدالة تعرف بـ
defفي Python وfunctionفي JavaScript، والمعاملات بين القوسين أسماء مؤقتة تأخذ القيم التي ترسلها عند الاستدعاء، فداخلtotal(food)يصبحitemsهو قائمة الطعام. - المعامل
rateله قيمة افتراضية Default هي 0.15، فإذا لم ترسل غيرها استخدمت، وإذا أرسلت 0.05 استخدمت بدلاً منها. وطريقة التقريب مختلفة: Python أعطت115.0لأنroundتعيد عدداً عشرياً، وJavaScript أعطت115لأن نوعnumberواحد ولا يطبع الكسر الصفري. - الدالة
showتطبع ولا تعيد شيئاً، فعندما حفظنا نتيجتها فيresultكانتNoneفي Python وundefinedفي JavaScript. وهذا هو الفرق بين الطباعة والإعادة الذي يخلط فيه أغلب المبتدئين:printتعرض القيمة لك أنت على الشاشة، وreturnتعطي القيمة للكود الذي استدعى الدالة ليستخدمها. total([])أعطت 0، لأن الحلقة لم تدر أي دورة وبقي المجموع على قيمته الأولى، وهي النتيجة الصحيحة لمجموع قائمة فارغة.

ولماذا يهم هذا الفرق؟ لأن الدالة التي تعيد قيمة يمكن أن تبني عليها: تمرر نتيجتها إلى دالة أخرى، أو تقارنها في شرط، أو تختبرها آلياً. أما الدالة التي تطبع فقط فنتيجتها تذهب إلى الشاشة وتضيع. ولذلك القاعدة: الدوال التي تحسب تعيد، والطباعة تكون في مكان واحد في آخر البرنامج. وللدوال فائدة أخرى أعمق، وهي أن المتغيرات التي تنشئها داخل الدالة مثل s تعيش داخلها فقط ولا تراها بقية البرنامج، وهذا ما يسمى نطاق المتغير Scope، وهو يحميك من أن يغير جزء من البرنامج متغيراً يستخدمه جزء آخر بنفس الاسم دون أن تنتبه.
وفكرة الدالة هي نفسها فكرة التفكيك التي رأيناها في مقال التفكير الحاسوبي: كل خطوة في شجرة التفكيك تصبح دالة باسم يشرح ما تفعله، والبرنامج الرئيسي يصبح سلسلة استدعاءات تقرؤها كأنها الكود الوهمي نفسه.
البرنامج الكامل: القواميس Dictionaries وتقرير المصاريف الشهري
الآن نجمع كل ما سبق في برنامج واحد. ونحتاج أداة أخيرة: كل مصروف ليس رقماً فقط وإنما له اسم وفئة ومبلغ، ولذلك نستخدم القاموس Dictionary في Python أو الكائن Object في JavaScript، وهو مجموعة أزواج من مفتاح وقيمة، تصل إلى القيمة باسم المفتاح بدل رقم الموقع:
"""Monthly expenses report: everything from the article in one small program."""
BUDGET = 3000
expenses = [
{"item": "إيجار", "category": "سكن", "amount": 1500},
{"item": "بقالة", "category": "طعام", "amount": 420.75},
{"item": "مطعم", "category": "طعام", "amount": 180},
{"item": "بنزين", "category": "مواصلات", "amount": 260},
{"item": "اشتراك", "category": "ترفيه", "amount": 45},
{"item": "مطعم", "category": "طعام", "amount": 210},
]
def by_category(items):
sums = {}
for e in items:
sums[e["category"]] = sums.get(e["category"], 0) + e["amount"]
return sums
def status(spent, budget):
if spent > budget:
return "تجاوزت الميزانية"
if spent > budget * 0.9:
return "قريب من الحد"
return "ضمن الميزانية"
def main():
sums = by_category(expenses)
spent = sum(sums.values())
for cat, amount in sorted(sums.items(), key=lambda kv: kv[1], reverse=True):
share = amount / spent * 100
print(f"{cat:<8} {amount:>9.2f} {share:5.1f}%")
print(f"المجموع {spent:>9.2f}")
print("الحالة:", status(spent, BUDGET), "| المتبقي:", round(BUDGET - spent, 2))
main()// Monthly expenses report: everything from the article in one small program.
const BUDGET = 3000;
const expenses = [
{ item: "إيجار", category: "سكن", amount: 1500 },
{ item: "بقالة", category: "طعام", amount: 420.75 },
{ item: "مطعم", category: "طعام", amount: 180 },
{ item: "بنزين", category: "مواصلات", amount: 260 },
{ item: "اشتراك", category: "ترفيه", amount: 45 },
{ item: "مطعم", category: "طعام", amount: 210 },
];
function byCategory(items) {
const sums = {};
for (const e of items) {
sums[e.category] = (sums[e.category] ?? 0) + e.amount;
}
return sums;
}
function status(spent, budget) {
if (spent > budget) return "تجاوزت الميزانية";
if (spent > budget * 0.9) return "قريب من الحد";
return "ضمن الميزانية";
}
function main() {
const sums = byCategory(expenses);
const spent = Object.values(sums).reduce((a, b) => a + b, 0);
const rows = Object.entries(sums).sort((a, b) => b[1] - a[1]);
for (const [cat, amount] of rows) {
const share = (amount / spent) * 100;
console.log(`${cat.padEnd(8)} ${amount.toFixed(2).padStart(9)} ${share.toFixed(1).padStart(5)}%`);
}
console.log(`المجموع ${spent.toFixed(2).padStart(9)}`);
console.log("الحالة:", status(spent, BUDGET), "| المتبقي:", Math.round((BUDGET - spent) * 100) / 100);
}
main();والمخرج من اللغتين متطابق حرفاً بحرف:
سكن 1500.00 57.3%
طعام 810.75 31.0%
مواصلات 260.00 9.9%
ترفيه 45.00 1.7%
المجموع 2615.75
الحالة: ضمن الميزانية | المتبقي: 384.25في البرنامج أعلاه لاحظ التالي:
- الدالة
by_categoryهي نمط العد بالقاموس الذي رأيناه في مقال التفكير الحاسوبي، ولكنها تجمع المبالغ بدل أن تعد التكرار: لكل مصروف تأخذ مجموع فئته الحالي، أو صفراً إذا كانت الفئة جديدة، وتضيف المبلغ. والطعام ظهر ثلاث مرات فصار مجموعه 420.75 + 180 + 210 = 810.75. sums.get(key, 0)في Python وsums[key] ?? 0في JavaScript يقولان نفس الشيء: أعطني القيمة، أو صفراً إذا لم يوجد المفتاح. ولو كتبتsums[key]مباشرة لظهر خطأKeyErrorفي Python عند أول فئة جديدة، ولأعطت JavaScriptundefinedثمNaNبهدوء.- الدالة
statusتعيد نصاً ولا تطبعه، وmainوحدها تطبع، فيمكن أن تختبرstatus(3200, 3000)بسهولة وتتأكد أنها تعيد «تجاوزت الميزانية» دون أن تشغل البرنامج كله. - الترتيب من الأكبر إلى الأصغر يستخدم دالة صغيرة بلا اسم تخبر الترتيب ماذا يقارن، وهي
lambdaفي Python والسهم=>في JavaScript، ولا تحتاج أن تفهمها الآن بالكامل، والمهم أنها تقول «رتب بحسب المبلغ». - التنسيق
{amount:>9.2f}في Python وtoFixed(2).padStart(9)في JavaScript يعرضان الرقم بخانتين عشريتين في عرض ثابت، وهذا ما جعل الأعمدة متساوية، وهو أيضاً ما حل مشكلة الكسور العشرية عند العرض. BUDGETبحروف كبيرة عرف يعني «ثابت لا يتغير». وفي JavaScript تفرضهconstفعلاً، أما Python فلا يوجد فيها ثابت حقيقي والحروف الكبيرة اتفاق بين المبرمجين فقط.
وهذا البرنامج على صغره يحتوي كل شيء: متغيرات بأنواع مختلفة، وقائمة من القواميس، وحلقات، وشروط، ودوال تعيد قيماً، وبرنامج رئيسي يجمعها. وإذا قرأت main وحدها فهي تقرأ مثل وصف المسألة: اجمع بحسب الفئة، ثم احسب المجموع، ثم اعرض كل فئة، ثم اعرض الحالة.
أخطاء المبتدئين الحقيقية: اقرأ رسالة الخطأ Error Message قبل أن تبحث
الآن الجزء الذي لا تعرضه أغلب الدروس: سوف نرتكب أشهر أخطاء الأسبوع الأول عمداً، كل خطأ في ملف صغير، ونرى الرسالة الحقيقية في كل لغة. والهدف ليس أن تحفظ الرسائل، وإنما أن تتعود على قراءتها، فأغلب المبتدئين ينسخون الرسالة إلى محرك البحث قبل أن يقرؤوا آخر سطر فيها، وغالباً يكون الحل مكتوباً فيه.
اسم مكتوب خطأ: NameError و ReferenceError
total = 1250
print(totl)Traceback (most recent call last):
File "/lab/errors/e01_name.py", line 2, in <module>
print(totl)
^^^^
NameError: name 'totl' is not defined. Did you mean: 'total'?
[exit 1]/lab/errors/e01_name.js:2
console.log(totl);
^
ReferenceError: totl is not defined
at Object.<anonymous> (/lab/errors/e01_name.js:2:13)
at Module._compile (node:internal/modules/cjs/loader:1968:14)
...
Node.js v26.13.0
[exit 1]أبسط خطأ وأكثره تكراراً: حرف ناقص في اسم متغير. ولاحظ أن Python لم تكتف بأن تقول إن الاسم غير معرف، وإنما اقترحت الاسم الصحيح Did you mean: 'total'?، وهذه الاقتراحات أضيفت في Python 3.10 كما تذكر ملاحظات الإصدار، وهي سبب إضافي لأن تستخدم إصداراً حديثاً. أما Node فتعطيك الملف والسطر والعمود 2:13 وسهماً تحت الاسم، ثم قائمة أسطر تبدأ بـ node:internal هي من داخل Node نفسه ولا علاقة لها بكودك، فتجاهلها.
نص مع رقم: TypeError في Python وصمت في JavaScript
remaining = 300
print("المتبقي: " + remaining)Traceback (most recent call last):
File "/lab/errors/e02_concat.py", line 2, in <module>
print("المتبقي: " + remaining)
~~~~~~~~~~~~^~~~~~~~~~~
TypeError: can only concatenate str (not "int") to str
[exit 1]Python تقول بالضبط ما المشكلة: يمكن لصق نص بنص فقط وليس برقم، والعلامة ^ تحت + تحدد العملية. والحل str(remaining)، أو الأفضل أن تستخدم الصيغة f"المتبقي: {remaining}" التي تحول بنفسها. أما نفس السطر في JavaScript فطبع المتبقي: 300 دون أي شكوى، وفي نفس الملف طبع "5" + 2, "5" - 2, "5" * "2" النتائج 52 3 10. وهنا يبدو أن JavaScript أريح، لكن تذكر أن نفس السلوك هو الذي يجعل "120" + 80 تعطي 12080 في برنامج مصاريف يقرأ المبالغ من نموذج إدخال.
الإزاحة Indentation والأقواس: IndentationError و SyntaxError
spent = 3200
if spent > 3000:
print("تجاوزت الميزانية") File "/lab/errors/e03_indent.py", line 3
print("تجاوزت الميزانية")
^^^^^
IndentationError: expected an indented block after 'if' statement on line 2
[exit 1] File "/lab/errors/e04_colon.py", line 2
if spent > 3000
^
SyntaxError: expected ':'
[exit 1]هذان خطآن في شكل الكود وليس في منطقه، ولذلك لاحظ أن الرسالة لا تبدأ بـ Traceback: Python رفضت الملف كله قبل أن تنفذ أي سطر منه. والرسالة الأولى تقول إن السطر بعد if يجب أن يكون مزاحاً، والثانية تقول إن النقطتين ناقصتان، والسهم يشير إلى المكان الذي توقعتهما فيه. وفي JavaScript نسينا القوس المعقوف الذي يغلق الشرط، فكانت الرسالة SyntaxError: Unexpected end of input أي أن الملف انتهى والقوس ما زال مفتوحاً، والسطر الذي تشير إليه هو آخر سطر في الملف وليس السطر الذي نسيت فيه القوس، فابحث للأعلى. والمحرر الجيد يمنع أغلب هذه الأخطاء لأنه يلون الأقواس المتقابلة ويزيح الأسطر تلقائياً.
علامة الإسناد Assignment بدل المقارنة: = بدل ==
spent = 3000
if spent = 3000:
print("وصلت الحد") File "/lab/errors/e05_assign.py", line 2
if spent = 3000:
^^^^^^^^^^^^
SyntaxError: invalid syntax. Maybe you meant '==' or ':=' instead of '='?
[exit 1]let spent = 2000;
if (spent = 3000) {
console.log("وصلت الحد، spent =", spent);
}وصلت الحد، spent = 3000
[exit 0]هذا أخطر خطأ في القسم كله، والسبب في المخرج الثاني. Python رفضت السطر واقترحت ==. أما JavaScript فنفذته بنجاح: أسندت 3000 إلى spent بدل أن تقارن، ولأن نتيجة الإسناد 3000 وهي قيمة تعتبرها JavaScript صحيحة، دخل البرنامج الشرط وطبع أنك وصلت الحد، مع أن المصروف كان 2000، وغير قيمته إلى 3000 لبقية البرنامج. ورمز الخروج 0، أي أن البرنامج نفسه يقول إن كل شيء بخير. ولهذا السبب تحذر أدوات فحص الكود مثل ESLint من هذا السطر بالقاعدة no-cond-assign، ولهذا السبب أيضاً يعتبر كثير من المبرمجين أن الخطأ الصاخب أرحم من الخطأ الصامت.
تحويل نص إلى رقم: ValueError و NaN
$ python3 e06_int.py
120
Traceback (most recent call last):
File "/lab/errors/e06_int.py", line 3, in <module>
amount = int("120 ريال")
~~~^^^^^^^^^^^^
ValueError: invalid literal for int() with base 10: '120 ريال'
[exit 1]
$ node e06_number.js
120 NaN 120 NaN
NaNالسطر الأول في Python حول "١٢٠" بالأرقام العربية الهندية إلى 120 بنجاح، ثم توقف عند "120 ريال" لأن النص فيه كلمة ليست رقماً، والرسالة تعرض النص الذي فشل كما هو. وفي JavaScript أعطت Number("120") الرقم 120، وNumber("120 ريال") أعطت NaN، أما parseInt("120 ريال") فأعطت 120 لأنها تقرأ الأرقام من أول النص وتتوقف عند أول حرف ليس رقماً، وparseInt("ريال 120") أعطت NaN لأن أول حرف ليس رقماً، وNumber("١٢٠") أعطت NaN. والقيمة NaN خطيرة لأنها تنتشر: أي عملية حسابية فيها NaN نتيجتها NaN، فتصل إلى آخر البرنامج وتظهر في التقرير دون أن تعرف من أين جاءت. وتفاصيلها في صفحة NaN على MDN.
القائمة الفارغة والموقع الخارج: ZeroDivisionError و IndexError
$ python3 e07_zero.py
Traceback (most recent call last):
File "/lab/errors/e07_zero.py", line 2, in <module>
average = sum(expenses) / len(expenses)
~~~~~~~~~~~~~~^~~~~~~~~~~~~~~
ZeroDivisionError: division by zero
[exit 1]
$ node e07_zero.js
NaN Infinity -Infinityمتوسط مصاريف قائمة فارغة يعني قسمة صفر على صفر، وهي الحالة الخاصة نفسها التي أسقطت أول نسخة في مقال التفكير الحاسوبي. Python توقفت، وJavaScript أعطت NaN، وأعطت 1 / 0 القيمة Infinity. ثم الخطأ الأشهر في الحلقات، وهو أن تمشي خطوة أبعد من آخر القائمة، والسبب غالباً أنك تذكرت أن القائمة فيها 3 عناصر ونسيت أن مواقعها 0 و1 و2:
expenses = [450, 120.5, 80]
for i in range(len(expenses) + 1):
print(i, expenses[i])0 450
1 120.5
2 80
Traceback (most recent call last):
File "/lab/errors/e08_index.py", line 3, in <module>
print(i, expenses[i])
~~~~~~~~^^^
IndexError: list index out of range
[exit 1]وفي JavaScript كتبنا الحلقة بـ <= بدل <، وهو نفس الخطأ بخطوة واحدة Off-by-one، فطبعت السطر الرابع 3 undefined دون أي خطأ، ثم عندما حاولنا أن نستخدم ذلك العنصر في السطر التالي ظهرت الرسالة:
0 450
1 120.5
2 80
3 undefined
/lab/errors/e08_index.js:5
console.log(expenses[3].toFixed(2));
^
TypeError: Cannot read properties of undefined (reading 'toFixed')
at Object.<anonymous> (/lab/errors/e08_index.js:5:25)
...
Node.js v26.13.0
[exit 1]ولاحظ الفرق المهم بين اللغتين هنا: Python أوقفت البرنامج في نفس السطر الذي حدث فيه الخطأ، أما JavaScript فأعطت undefined وأكملت، وظهر الخطأ في سطر لاحق. وفي برنامج حقيقي قد يكون هذا السطر اللاحق في ملف آخر بعد مئة سطر، فتقضي وقتاً تبحث في المكان الخطأ. وعندما ترى Cannot read properties of undefined فاسأل: من أين جاءت هذه undefined؟ وارجع خطوة خطوة إلى الخلف.
الدالة التي تطبع ولا تعيد Return، والمقارنة بين نص ورقم
$ python3 e09_return.py
30
Traceback (most recent call last):
File "/lab/errors/e09_return.py", line 8, in <module>
print(result + 5)
~~~~~~~^~~
TypeError: unsupported operand type(s) for +: 'NoneType' and 'int'
[exit 1]
$ node e09_return.js
30
NaNالدالة في هذا المثال تحسب المجموع وتطبعه بـ print بدل أن تعيده، فظهر 30 على الشاشة، لكن المتغير result أصبح None، فلما أضفنا إليه 5 قالت Python إنها لا تعرف كيف تجمع NoneType مع int. وكلمة NoneType في أي رسالة خطأ إشارة شبه مؤكدة إلى دالة نسيت فيها return. أما JavaScript فأعطت NaN بهدوء. ثم المقارنة بين نص ورقم، وهي تحدث كثيراً عندما تقرأ مبلغاً من مدخلات المستخدم وتنسى تحويله:
$ python3 e13_compare.py
Traceback (most recent call last):
File "/lab/errors/e13_compare.py", line 6, in <module>
print(check("3200"))
~~~~~^^^^^^^^
File "/lab/errors/e13_compare.py", line 2, in check
if spent > 3000:
^^^^^^^^^^^^
TypeError: '>' not supported between instances of 'str' and 'int'
[exit 1]
$ node e13_compare.js
over ok false trueوهذه أول رسالة فيها مستويان، والصورة التالية تبين كيف تقرؤها مع رسالة Node:

الـ Traceback في Python يعرض سلسلة الاستدعاءات من الأقدم إلى الأحدث، ولذلك يقول في أوله most recent call last: السطر 6 في البرنامج الرئيسي استدعى check("3200")، وداخل check في السطر 2 حدثت المقارنة التي فشلت. فالخطأ ظهر في السطر 2، لكن سببه الحقيقي في السطر 6 الذي أرسل نصاً بدل رقم، وهذا يحدث كثيراً: المكان الذي يظهر فيه الخطأ ليس دائماً المكان الذي تصلحه. أما JavaScript فأعطت over لأنها حولت النص "3200" إلى رقم وقارنت، وأعطت check("abc") النتيجة ok لأن "abc" تحولت إلى NaN وأي مقارنة مع NaN خاطئة. والأغرب "10" > "9" التي أعطت false، لأن المقارنة بين نصين حرفاً بحرف كالقاموس، والحرف "1" يأتي قبل "9"، فـ «10» أصغر من «9» كنص. وقد شرحت وثائق Python عن الأخطاء والاستثناءات كيف تقرأ هذه الرسائل وكيف تتعامل معها في الكود، وقد خصصنا لتصحيح الأخطاء مقال قراءة رسالة الخطأ والتصحيح Debugging.
تعديل ثابت Constant معرف بـ const في JavaScript
/lab/errors/e10_const.js:2
budget = 3500;
^
TypeError: Assignment to constant variable.
at Object.<anonymous> (/lab/errors/e10_const.js:2:8)
...
Node.js v26.13.0
[exit 1]عرفنا budget بـ const ثم حاولنا تغييره، فرفضت JavaScript. وهذا خطأ مفيد وليس عائقاً، فهو يعني أن const تحميك من تغيير قيمة قررت أنها ثابتة، ولذلك القاعدة الشائعة أن تعرف كل متغير بـ const، ولا تغيرها إلى let إلا عندما تحتاج فعلاً أن تغير القيمة، كما تشرح صفحة const على MDN.
الخطأ الصاخب والخطأ الصامت Silent Error
إذا جمعت كل ما سبق في جدول واحد ستجد نمطاً واضحاً:

وليس هذا حكماً بأن إحدى اللغتين أفضل، فلكل لغة فلسفتها: Python تنص في المبادئ المعروفة باسم The Zen of Python على أن الأخطاء لا يجب أن تمر بصمت إلا إذا أسكتها المبرمج عن قصد، أما JavaScript فتحاول أن تكمل التنفيذ بأقرب قيمة تراها منطقية، وهذا مريح عندما تكون القيمة صحيحة ومكلف عندما لا تكون. والنتيجة العملية لك كمبتدئ: في Python اقرأ الرسالة، فهي غالباً تقول لك الحل. وفي JavaScript لا تثق بأن غياب الرسالة يعني أن الكود صحيح، واطبع القيم الوسيطة وتحقق منها، واستخدم === وconst، وعندما تكبر مشاريعك سوف تجد أن أدوات مثل TypeScript وESLint موجودة أصلاً لتجعل أخطاء JavaScript صاخبة.
مقارنة سريعة بين اللغتين Syntax
| الفكرة | Python | JavaScript |
|---|---|---|
| تعريف متغير | budget = 3000 | let budget = 3000; أو const |
| الطباعة | print(x) | console.log(x) |
| معرفة النوع | type(x) | typeof x |
| صح وخطأ | True وFalse | true وfalse |
| لا توجد قيمة | None | undefined وnull |
| المساواة | == | === (وتجنب ==) |
| و، أو، ليس | and or not | && || ! |
| كتلة الشرط | نقطتان وإزاحة | قوسان للشرط و{ } للكتلة |
| وإلا إذا | elif | else if |
| المرور على قائمة | for x in items: | for (const x of items) |
| طول القائمة | len(items) | items.length |
| إضافة عنصر | items.append(x) | items.push(x) |
| تعريف دالة | def f(a, b=1): | function f(a, b = 1) { } |
| نص إلى رقم | int("10") وfloat("1.5") | Number("10") |
| إدراج قيمة في نص | f"المتبقي: {x}" | `المتبقي: ${x}` |
| التعليق | # تعليق | // تعليق |
قائمة التحقق قبل أن تشغل برنامجك Checklist
- هل كل متغير له اسم يشرح ما فيه، وبعرف لغتك (
snake_caseفي Python وcamelCaseفي JavaScript)؟ - هل حولت كل قيمة تأتي من المستخدم أو من ملف إلى النوع الصحيح قبل أن تحسب بها؟
- هل جربت كل شرط على حده بالضبط، وعلى قيمة أصغر منه وأكبر منه؟
- هل تعرف أين تتوقف كل حلقة
while، وما المتغير الذي يتغير فيها؟ - هل جربت برنامجك على قائمة فارغة وعلى قائمة فيها عنصر واحد؟
- هل كل دالة تحسب شيئاً تعيده بـ
return، والطباعة في مكان واحد؟ - في JavaScript: هل استخدمت
===وconst، وهل تحققت من عدم ظهورNaNأوundefinedفي المخرج؟ - عند الخطأ: هل قرأت آخر سطر في رسالة Python، أو أول سطر في رسالة Node، قبل أن تبحث في الإنترنت؟
تدريب عملي
- أضف إلى برنامج المصاريف دالة
biggest(items)تعيد أكبر مصروف واسمه، بلغة واحدة أولاً ثم بالأخرى، وجربها على قائمة فارغة. ماذا قررت أن تعيد في هذه الحالة؟ - اجعل البرنامج يقرأ الميزانية من المستخدم بـ
input()في Python، ولاحظ أنها تصل نصاً، ثم جرب أن تكتب3000 ريالواقرأ الرسالة، ثم أصلحها برسالة واضحة للمستخدم. - غير شرط التنبيه في
statusليكون قبل الحد بـ 10% بالضبط، واكتب ثلاث استدعاءات تختبر الحد وما قبله وما بعده. - خذ ملفاً من مجلد
errorsوأصلحه، ثم اكتب خطأ جديداً لم نذكره وشغله في اللغتين، وقارن الرسالتين.
حذف المختبر Cleanup
الأمثلة عملت داخل Container بالخيار --rm، ويبقى بعدها الـ Image التي بنيناها والـ Images الرسمية التي نزلها Docker، واحذفها إذا لم تكن تحتاجها:
docker rmi first-steps node:26-slim python:3.13-slimالخلاصة
وصلنا لنهاية الموضوع، وأهم ما فيه:
- المتغير اسم يشير إلى قيمة، وعلامة
=إسناد وليست مساواة، وفي JavaScript تعرفه بـconstأوlet. - كل قيمة لها نوع، والأنواع الأساسية النص والعدد الصحيح والعشري والقيمة المنطقية، وPython تفرق بين الصحيح والعشري بينما JavaScript لها
numberواحد. - Python ترفض خلط الأنواع برسالة خطأ، وJavaScript تحول بنفسها فيعطي
"5" + 2النتيجة 52، واستخدم===دائماً في JavaScript. - الشروط تنفذ طريقاً واحداً بالترتيب، وحد الشرط قرار يجب أن تتخذه وتختبره.
- القوائم تبدأ من الموقع 0، و
forتمر على كل العناصر، وwhileتتوقف بشرط، والحلقة التي لا يتغير شرطها لا تنتهي. - الدالة تأخذ معاملات وتعيد قيمة بـ
return، والدالة التي تطبع فقط تعيدNoneأوundefined. - اقرأ رسالة الخطأ قبل أن تبحث: آخر سطر في Python وأول سطر في Node، والمكان الذي يظهر فيه الخطأ ليس دائماً مكان سببه.
- في أغلب أخطاء المبتدئين Python تتوقف وJavaScript تكمل بقيمة خاطئة، والخطأ الصامت هو الأخطر.
وبعد هذا المقال تكمل المرحلة بقراءة الأخطاء وتصحيحها بالمصحح Debugger، ثم قراءة الملفات وكتابتها، ثم الاستدعاء الذاتي Recursion، وهذه مقالاتها مع مقالات مرتبطة بما رأيته هنا:
دليلقراءة ال Stack Trace وتصحيح الأخطاء Debugging للمبتدئينكيف تقرأ رسالة الخطأ في Python وJavaScript سطراً بسطر، ومتى تستخدم الطباعة أو نقاط التوقف في VS Code، وكيف يجد git bisect التعديل الذي أدخل الخطأ.
دليلالملفات Files والتدفقات Streams: كيف يقرأ برنامجك ملفاً؟لماذا يظهر النص العربي في ملف CSV من Excel مشوهاً، ولماذا يتوقف تقرير بسيط عندما يكبر ملف السجلات، وكيف تقرأ الملفات وتكتبها بأمان في Python وNode.js بقياسات حقيقية.
دليلالاستدعاء الذاتي Recursion: كيف تفكر فيه ومتى تتجنبهكيف تكتب دالة تستدعي نفسها لشجرة تعليقات أو مجلدات، وما الذي يحدث في ال Stack، وإلى أي عمق تصل Python وNode.js بالقياس، ومتى تكون الحلقة هي الحل الأنسب.
دليلبيئة البرمجة للمبتدئين: المحرر VS Code والطرفية Terminalنجهز المحرر والطرفية وPython وNode على جهاز نظيف، ونفهم كل رسالة خطأ تظهر في الأيام الأولى مثل command not found وPATH وpython3 قبل أن نكتب أول برنامج.
دليلكيف تفكر كمبرمج؟ التفكير الحاسوبي Computational Thinkingنحل مسألة واحدة من الفهم والتفكيك والكود الوهمي والتتبع اليدوي حتى ثلاث طرق مختبرة ومقاسة، لترى كيف يتحول سؤال غامض إلى خطوات يكتبها الحاسب دون تخمين.
دليلFloating Point من الداخل: كيف يخزن الحاسب الكسوركيف يحفظ معيار IEEE 754 الكسور في البتات، ولماذا تكبر الفجوة بين الأرقام وتضيع خانات المعرفات، ومتى تحتاج النوع العشري.سجل التحديثات
- أكتوبر 2026: كتابة المقال، وتشغيل كل الأمثلة وملفات الأخطاء داخل Container من python:3.13-slim مع Python 3.13.17 وNode.js 26.13.0 المنسوخ من node:26-slim، على Docker Engine 29.8.0.