لوحة المتصدرين
المحتوى الأكثر حصولًا على سمعة جيدة
المحتوى الأعلى تقييمًا في 07/06/24 في كل الموقع
-
السلام عليكم عندي استفسار بخصوص المشاريع انا لا اعرف كيف اكتب الخوارزميه التي تكون قبل تنفيذ المشروع4 نقاط
-
معلش تحملوني... أخوان بخصوص database كم عامود تتحمل اذا كان هناك 30 عامود ممكن تتحمل؟ واذا كانت الاستضافة مدفوعة... وسؤال اخر ممكن يكون هناك اتصال بين form والاكسل اي بمعنى ممكن معلومات الذي يتم إدخالها وإرسالها عن طريق form ممكن ترسل إلى الأكسل مباشر...2 نقاط
-
تمنى الأفاده ما مستقبل برمجه وأي لغه يجب أن تعلم أريد مساعده أرجوكم في بالموقع طريقه حفظ رموز و لغات لكي لا أنساها كيف تمنى الأفاده2 نقاط
-
2 نقاط
-
االسلام عليكم مش المفروض هنا من خلال الكود ده يجب اكبر رقم ليه ظهر خطاء ؟ price = [50,60,100,500,1000,1000000,20000000,91000000000] price(max(price))2 نقاط
-
اقصد اكثر من سكربت استعملت لأن فيه عندي حقول select تحتاج إلى شروط كثيرة ف مستحيل تكون من خلال سكربت واحد او اثنين احيانا ل select واحد احتاج سكربت لوحده...2 نقاط
-
احتاج الى كود.... فيه select فيه خيارات اثنين الخيار الأول نعم والخيار الثاني لا... لما يتم اختيار نعم يظهر حقل ارسال صورة... ولما يختار لا لايظهر شيء علمآ خيارات نعم او لا اريد ان تظهر في اسم العامود الخاص بها هوا book1 واسم العامود الذي تظهر فيها صوره skool22 نقاط
-
احتاج الى كود... فيه 5 خيارات كل خيار عنده الضغط عليه يظهر حقل إضافة صورة وعند ارسال الصورة كل صورة تذهب إلى عامود محدد... يعني أيضآ نحتاج 5 حقول مع سلكت 5 خيارات فيه... العامود الأول hoos1 العامود الثاني hoos2 العامود الثالث hoos3 العامود الرابع hoos4 العامود الخامس hoos52 نقاط
-
هل متابعة دورة دورة الذكاء الاصطناعي تحتاج الي اساسيات قبل البدأ فيها وهل ضعف مستواي في الرياضيات يشكل حاجز لمتابعة هذه الدورة2 نقاط
-
توجد أسباب عديدة لهذه المشكلة منها المنفذ بورت مستعمل من قبل برنامج اخر نقوم بتغيير البورت كالأتى my.ini<----config : وبعدها غير قيمة المنفذ الى 3307 وبعدها قم بحفظ الملف بالضغط على (ctr+ sl ) قم أيضا بتجربة تشغيل البرنامج كمسؤول كما أشار مصطفى ومحمد وتم حل هذه المشكلة مرات عديدة سابقا يرجى النظر لهذه الإجابات بالتوفيق...1 نقطة
-
1 نقطة
-
بخصوص اسماء الاعمدة اذا كان هناك اسم عامود inf1 والثاني اسمه inf2... هل ممكن يحدث مشكله لأن الفرق بين الاعمده ربما قد يكون حرف واحد او رقم؟1 نقطة
-
1 نقطة
-
وعليكم السلام ورحمة الله وبركاته . الخطأ بسيط جدا . أعتقد أنك بدلا من كتابة print قمت بكتابة price لذلك لا توجد دالة تسمى price price = [50,60,100,500,1000,1000000,20000000,91000000000] print(max(price))1 نقطة
-
الكود الذي كتبته يحتوي على خطأ بسيط حيث أنك إستخدمت price بدل print يمكنك تصحيح الكود فقط بالشكل التالي: price = [50, 60, 100, 500, 1000, 1000000, 20000000, 91000000000] max_price = max(price) print(max_price)1 نقطة
-
هذا بالفعل أنك قمت بتقسيم الإسكريبت إلى عدة سكريبتات كل سكريبت مسؤل عن شئ معين وهذا بالفعل سيحسن تنفيذ الإسكريبت و الإدخال في قاعدة البيانات . فلا تقلق من كثرة الإسكريبتات حيث كل سكريبت يعمل بمفرده حين الذهاب إلى مسار الخاص بالإسكريت فلو كان السيرفر يحوى آلاف الإسكريبتات سيعمل فقط الإسكريبت الذى يتم الذهاب إليه لذلك لا قلق من هذه الناحية . ولكن يفضل تنظيم الإسكريبتات حتي فيما بعد إذا أردت إضافة أو تعديل أو حتي إصلاح سكريبت معين فلا تتوه من كثرة الملفات وحتي تجد ما تريد البحث عنه بسرعه1 نقطة
-
لا مشكلة في ذلك أهم شيء قم بتنظيم السكربتات في ملفات منفصلة وضعها في مجلد محدد داخل مشروعك و قم بإداراتها بالشكل المناسب.1 نقطة
-
نعم، يمكن أن تسبب وجود عدة سكربتات مشاكل في بعض الأحيان، خاصة إذا لم تتم إدارتها بشكل صحيح، فمثلا إذا كانت هناك سكربتات متعددة تعمل على معالجة نموذج واحد، فقد يحدث تضارب بينها مما يؤدي إلى سلوك غير متوقع أو أخطاء، كما أنه كثيرا ما يؤثر استخدام العديد من السكربتات على أداء النظام وقد يتسبب في بطء في استجابة النموذج، لذا يجب أن تأخذ هذه الأمور بعين الإعتبار.1 نقطة
-
إذا كانت قاعدة البيانات مدفوعة الاستضافة فغالبا يمكن أن تتحمل عدد كبير من الأعمدة. بشكل عام لا يوجد عدد محدد يمكن التأكد منه بدون المعرفة الكاملة لمتطلبات التطبيق الخاص بك ومواصفات الاستضافة ومع ذلك فإن 30 عمود هو عدد متوسط وليس بالكبير ويمكن التعامل معه بشكل طبيعي في معظم أنظمة إدارة قواعد البيانات وهذا أيضا يعتمد على مدي عدد البيانات الموجود في القاعدة . ولكن يمكنك بالفعل تخزين أى شئ المشكلة ستكمن فقط في إحتمال أن يكون السيرفر بطئ في إحضار البيانات والبحث في قاعدة البيانات لذلك إذا لم تهتم بسرعة التطبيق فلابأس. أما بالنسبة إلى excel فإذا كنت تقصد أنك تريد إنشاء ملف excel ووضع البيانات المرسلة من form به فنعم بالطبع يمكنك ذلك بالتأكيد . يمكنك إستخدام مكتبة php الرسمية في ذلك وهى تسمى phpspreadsheet ماذا تقصد بأكثر من سكريبت في الفورم ؟1 نقطة
-
عدد الأعمدة التي يمكن أن تتحملها قاعدة البيانات يعتمد على عدة عوامل مثل نوع قاعدة البيانات، والاستضافة، وتكوينات الخادم، في العادة قواعد البيانات الحديثة والاستضافات المدفوعة تتيح لك إمكانية إنشاء جداول تحتوي على عشرات الأعمدة بدون مشاكل، و يمكنك تحسين أداء قاعدة البيانات بتنظيم الجداول واستخدام فهارس بشكل صحيح. بالنسبة للاتصال بين النموذج وملف Excel، يمكنك ذلك بطرق مختلفة، إذا كنت تستخدم خادم ويب معين مثل Apache أو Nginx، يمكنك استخدام سكريبتات برمجية لمعالجة البيانات المرسلة من النموذج وكتابتها إلى ملف Excel، يمكنك الإعتماد في هذا على PHPExcel.1 نقطة
-
لقد كتبت هذه اللعبة لعبة X O import os def clear(): os.system("cls "if os.name == "nt" else "clear") class Player: def __init__(self): self.name = "" self.symbol="" def choose_name(self): while True : name = input("enter your name (letters only)") if name.isalpha()==True: self.name=name break else: print("invalid name . please use letters only") def choose_symbol(self): h=["X","O"] while True: symbol=input(f"{self.name},choose your symbol ({"/".join(h)})") if symbol.isalpha() and len (symbol)==1: if symbol in h: self.symbol=symbol h.remove(symbol) break else: print(f"please enter{h}") continue else: print("please enter one leater only") continue class menu: def display_main_menu(self): print("welcome to my X-O game!") print("1.Start game") print("2.Quit Game") while True: choice=input("Enter your choice (1 or 2): ") if choice==1 or 2: break else: print("wrong!! please inter 1 to start game or 2 to Quit game") continue return choice def display_endgame_menu(self): menu_text=""" Game over! 1. Restart Game 2.Quit Game Enter your choice (1 or 2):""" while True: choice=input(menu_text) if choice==1 or 2: break else: print("wrong!! please inter 1 to restart game or 2 to Quit game") return choice class Board: def __init__(self) : self.board=[str(i)for i in range(1,10)] def display_board(self): for i in range(0,9,3): print("|".join(self.board[i:i+3])) if i <6: print("-"*5) def update_board(self, choice, symbol): if self.is_valid_move(choice): self.board[choice-1]=symbol return True return False def is_valid_move(self,choice): return self.board[choice-1].isdigit() def reset_baord(self): self.board=[str(i)for i in range(1,10)] class Game: def __init__(self): self.players= [Player(),Player()] self.board = Board() self.menu = menu() self.current_player_index = 0 def start_game(self): choise = self.menu.display_main_menu() if choise=="1": self.setup_players() self.play_game() else: self.quit_game() def setup_players(self): for number ,player in enumerate( self.players,start=1): print(f"Player{number},enter your details:") player.choose_name() player.choose_symbol() print("-"*20) clear() def play_game(self): while True: self.play_turn() if self.check_win() or self.check_drow(): self.who_win() choice = self.menu.display_endgame_menu() if choice =="1": self.restart_game() else: self.quit_game() break def restart_game(self): self.board.reset_baord() self.current_player_index = 0 self.play_game() def check_win(self): win_combinstions=[ [0,1,2],[3,4,5],[6,7,8], [0,3,6],[1,4,7],[2,5,8], [0,4,8],[2,4,6] ] for combo in win_combinstions: if (self.board.board[combo[0]] == self.board.board[combo[1]]==self.board.board[combo[2]]): return True return False def check_drow(self): return all(not cell.isdigit() for cell in self.board.board) def play_turn(self): player = self.players[self.current_player_index] self.board.display_board() print(f"{player.name}'s turn ({player.symbol})") while True: try: cell_choice = int(input("choose a cell (1-9): ")) if 1 <= cell_choice <= 9 and self.board.update_board(cell_choice, player.symbol): # Valid move, switch turns and clear screen self.switch_player() clear() # Call clear() after switching player break else: print("invalid move , try again.") except: print("please enter a number between 1 and 9.") def who_win(self): H=self.players[self.current_player_index] if self.check_win: print(f"{H.name} is win") def switch_player(self): self.current_player_index= 1 - self.current_player_index clear() def quit_game(self): print("thank you for playing!") game = Game() game.start_game() هذا هو الكود و الخطا في هذا الجزء من الكود def choose_symbol(self): h=["X","O"] while True: symbol=input(f"{self.name},choose your symbol ({"/".join(h)})") if symbol.isalpha() and len (symbol)==1: if symbol in h: self.symbol=symbol h.remove(symbol) break else: print(f"please enter{h}") continue else: print("please enter one leater only") continue الدالة remov لا تعمل1 نقطة
-
طب اعطيني فكرة او اعد كتابة الشيفرة حيث اني حاولت ان افكر في ان اجعل الاعب الاول هو الذي يختارX او O و الثاني يتم اختيار العنصر الثاني تلقائيا لكن لم اعرف كيف اكتب هذا السطر من الاوامر ارجو ان تساعدني1 نقطة
-
def choose_symbol(self): h=["X","O"] while True: symbol=input(f"{self.name},choose your symbol ({'/'.join(h)})") if symbol.isalpha() and len (symbol)==1: symbol.upper() if symbol in h: self.symbol=symbol h.remove(symbol) break else: print(f"please enter{h}") continue else: print("please enter one leater only") continue ما زال هذا الجزء لا يعمل بشكل صحيح1 نقطة
-
في حال كان في الأكاديمية هنا فالأمر ممنوع مشروع التخرج يتم إتمامه من قبلك بدون مساعدة.1 نقطة
-
1 نقطة
-
لماذا تريد 5 حقول مع 5 إختيارات ؟! إذا كان فقط سيتم رفع ملف واحد وإختيار خيار واحد حيث أن هذا النظام خاطئ وليس جيدا من حيث قاعدة البيانات التي تحجز أعمدة فارغة دون داعي ومن حيث كتابة أكواد كثيرة جدا ومستقبلا إذا أردت إضافة خيار جديد سيتوجب عليك إضافة عمود في قاعدة البيانات مع إضافة خيار أخر وحقل أخر و إضافة رفع الملف الجديد في الخادم . يمكنك إضافة فقط عمودين في قاعدة البيانات عمود خاص بالإختيار الذى تم إختياره وعمود أخر بمكان الصورة . أى مثلا عمود option مع عمود hoss . والآن هذا ملف الواجهة الأمامية html : <!DOCTYPE html> <html lang="ar"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> </head> <body> <form action="upload.php" method="post" enctype="multipart/form-data"> <label for="option">من فضلك إختر من التالي:</label> <select id="option" name="option"> <option value="">اختر...</option> <option value="1">الخيار الأول</option> <option value="2">الخيار الثاني</option> <option value="3">الخيار الثاني</option> <option value="4">الخيار الثاني</option> <option value="5">الخيار الثاني</option> </select> <div> <label for="image">حقل الصورة:</label> <input type="file" name="image" accept="image/*"> </div> <button type="submit">إرسال</button> </form> </body> </html> وهذا هو كود الواجهة الخلفية upload.php : <?php if ($_SERVER["REQUEST_METHOD"] == "POST") { $option = $_POST["option"]; if ($option !in_array($option, [1,2,3,4,5])) { echo "من فضلك إختر خيار صحيح"; die(); } $path = "img". $option ."/"; $targetFile = $path . basename($_FILES["image"]["name"]); // الاتصال بقاعدة البيانات $servername = "localhost"; $username = "root"; $password = ""; $dbname = "db_name"; $conn = new mysqli($servername, $username, $password, $dbname); if ($conn->connect_error) { die("فشل الاتصال: " . $conn->connect_error); } if (move_uploaded_file($_FILES["image"]["tmp_name"], $targetFile)) { $sql = "INSERT INTO table_name (hoss$option) VALUES ('$targetFile')"; if ($conn->query($sql) === TRUE) { echo "تم رفع الملف بنجاح"; } else { echo "عذراً، حدث خطأ أثناء رفع الملف."; die(); } $conn->close(); } else { echo "عذراً، حدث خطأ أثناء رفع الملف."; } }1 نقطة
-
ستحتاج إلى حلقة للتكرار وعرض الخيارات الخمسة، ثم حلقة أخرى في PHP للتكرار على تلك الحقول، وعليك تحديث البيانات الخاصة باستعلام قاعدة البيانات لوضع الصورة في العمود المناسب. <body> <?php if ($_SERVER["REQUEST_METHOD"] == "POST") { for ($i = 1; $i <= 5; $i++) { if (isset($_FILES["image" . $i]) && $_FILES["image" . $i]["error"] == 0) { $image_name = $_FILES["image" . $i]["name"]; $image_tmp = $_FILES["image" . $i]["tmp_name"]; move_uploaded_file($image_tmp, "uploads/" . $image_name); $sql = "UPDATE table_name SET hoos" . $i . " = '$image_name' WHERE id = id_name"; if ($conn->query($sql) === TRUE) { echo "تم تحميل الصورة " . $i . " بنجاح.<br>"; } else { echo "حدث خطأ أثناء تحميل الصورة " . $i . ": " . $conn->error . "<br>"; } } } $conn->close(); } ?> <form method="post" enctype="multipart/form-data"> <?php for ($i = 1; $i <= 5; $i++) { echo " <label for='option" . $i . "'>الخيار " . $i . ":</label> <select name='option" . $i . "'> <option value=''>اختر</option> <option value='option1'>الخيار 1</option> <option value='option2'>الخيار 2</option> <option value='option3'>الخيار 3</option> </select> <input type='file' name='image" . $i . "'><br><br> "; } ?> <input type="submit" value="إرسال"> </form> </body> عامًة وجود خمسة أعمدة منفصلة (hoos1، hoos2، ... hoos5) لخمس صور محتملة تصميم غير مرن وغير قابل للتطوير مستقبلاً.1 نقطة
-
استطعت ان ابني حسوب قرام بنفسي من الصفر, ولدي ثقة جيدة جدا بفهمي للارافيل, هل اقدم للاختبار بعد؟1 نقطة
-
السلام عليكم لا أعرف إن كانت هذه هي الطريقة الصحيحة للتواصل معكم لكن أريد أن أبلغكم أنني حاولت صباح اليوم شراء دورة تطوير المواقع بلغة بايثون و إنتهى بي المطاف بدورة أخرى و هي دورة الذكاء الاصطناعي فهل توجد إمكانية تغيير الدورة و آسف على إزعاجكم1 نقطة
-
في البداية لن تتمكن من ذلك إلا من خلال الدفع لأصحاب بعض المواقع ذات الجودة الجيدة للإعلان عن موقعك ووضع رابط له في المحتوى. ولا أنصحك بالقيام ببناء أية روابط إلا بعد مرور 3 أشهر أو حتى 6 أشهر من وجود موقعك في جوجل، لأن جوجل تعلم أنه ليس من الطبيعي أن يشار إلى موقع جديد من خلال روابط خلفية. للعلم رابط واحد من موقع لديه Authority مرتفع أي تصنيف عالي، أفضل من 100 رابط من مواقع ذات تصنيف منخفض. ولا تُفكر في بناء روابط في أي موقع لديه spam score مرتفع. أيضًا موقعك مختص بالطقس، فلا تقوم ببناء رابط خلفي من موقع مختص بالطبخ مثلاً، فذلك سيضر بموقعك أكثر من نفعه، يجب أن يكون هناك صلة بالمحتوى الخاص بموقعك، مثلاً موقع تقني يقوم بالإعلان عنك كموقع طقس جيد أو وضع رابط لك أو موقع به مقالات تتحدث عن الطقس والمناخ وهكذا. أيضًا يمكنك إنشاء محتوى عملاق جدير بالنشر والإشارة إليه من قبل الآخرين، أو حتى تصميم موقع ذو تجربة مستخدم مرتفعة.1 نقطة
-
كنصيحة، لو أردت تحسين ظهور موقعك في جوجل فالأمر بحاجة إلى صبر واستمرارية، لا تتوقع أي نتيجة على المدى القصير، على المدى الطويل ستجد نتيجة خاصًة بعد السنة الأولى بعد أن يكتسب موقعك ثقة جوجل ليقوم بترتيبه في مرتبة أعلى من مواقع أخرى حسب محتواه وسرعة الموقع وتجربة المستخدم والفائدة التي يقدمها له. بعد الأمور في موقعك قد تستغرق 3 أشهر لحين ظهور نتيجة خاصًة في بدايات الموقع. بخصوص التحديثات في الكونسول، من الطبيعي أن يحدث ذلك حيث ستعمل عناكب جوجل على أرشفة موقعك مرة في الأسبوع مثلاً، وسيتعين عليك إضافة الروابط بنفسك. لذا الزحف والفهرسة قد تستغرق من بضعة أيام إلى بضعة أسابيع لاكتشاف موقعك الإلكتروني الجديد وزحف إليه وفهرسة صفحاته. بمجرد الزحف إلى موقعك وفهرسته، يستغرق الأمر من يومين إلى أسبوع حتى تبدأ البيانات في الظهور في Search Console. وبعد فترة يصبح الأمر أسرع فأسرع، لحين أن يصل الأمر أن يصبح يومي.1 نقطة
-
لا يخفى على أحد اليوم مدى أهمية تطوير مواقع الويب فالجميع اليوم يسعى لإنشاء موقع إلكتروني خاص به بدءًا من الأشخاص ووصولًا للعلامات التجارية الكبرى إما للتعريف عن أنفسهم وإثبات وجودهم في الفضاء الرقمي، أو لبيع منتجاتهم وخدماتهم، أو تسيير معاملات عملائهم عبر الإنترنت، وغيرها من الفوائد التي لا حصر لها. لكن هل تعلم أن معظم مواقع الويب المتقدمة التي تتعامل معها لا تقتصر على الجزء الذي تراه أمامك على الشاشة والذي يعرف بالواجهات الأمامية لمواقع الويب بل هناك أيضًا الواجهات الخلفية الخفية المسؤولة عن إنجاز كافة الوظائف التي يتضمنها الموقع وجعل كل عناصره من أزرار وقوائم تتفاعل معك بالشكل الصحيح وهي تستخدم تقنيات وأدوات برمجية مختلفة عن تلك التي تستخدمها الواجهات الأمامية. نسلط الضوء في مقال اليوم على كل ما يخص تطوير الواجهات الخلفية لمواقع الويب، ونستعرض أهم المهارات والتقنيات التي تحتاج لتعلمها وأهم المسؤوليات المنوطة بها، فإذا كنت ترغب في العمل كمطور واجهة خلفية لكنك لا تعرف من أين تبدأ وما هي خطوات التعلم الصحيحة فهذا المقال سيفيدك حتمًا. كيف تعمل مواقع الويب؟ قبل الغوص في تفاصيل تطوير مواقع الإنترنت ومفهوم الواجهة الأمامية والخلفية لموقع الويب دعنا نتعرف بداية على المصطلحات الأساسية للويب والكيفية التي تعمل مواقع الويب وفقها. مبدئيًا لدينا نوعان من الأجهزة المتصلة بشبكة الإنترنت هما العميل client وهو جهازك الحالي وهو ببساطة هو الجهاز المتصل بالإنترنت التابع للمستخدم الذي يتصفح الإنترنت من خلال تطبيق خاص مثبت فيه هو متصفح الويب web browser مثل جوجل كروم أو فايرفوكس. خادم الويب web server وهو حاسوب بعيد مصمم للعمل كمستودع لملفات مواقع الويب من أكواد برمجية وملفات صور والنصوص وغيرها من العناصر التي تراها على موقع الويب وتسهل وصولك إليها فمطور المواقع يقوم بتحميل كل هذه الملفات إلى الخادم وأنت تتصل به وتعرض هذه الملفات على جهازك. يجب أن يزود الخادم ببرامج خاصة تمكنه من استقبال الطلبات الواردة إليه ومعالجتها والرد عليها ويجب أن يكون متصلًا بالإنترنت بصورة دائمة ويلبي طلبات العملاء بسرعة مع العلم بأن تسمية الخادم تختلف بحسب طبيعة الخدمة التي يقدمها فإذا كان الخادم يوفر خدمة قاعدة بيانات يسمى خادم قاعدة البيانات وإذا كانت يخزن صفحات الويب يسمى خادم ويب له عنوان URL فريد يمكن الوصول إليه عبر الإنترنت. الفرق بين الواجهة الأمامية Frontend والواجهة الخلفية Backend قبل الغوص في تفاصيل تطوير الواجهة الخلفية من الضروري أن تفهم الفرق بين الواجهة الأمامية frontend والواجهة الخلفية backend وهما مصطلحان يهدفان إلى فصل الاهتمامات بين طبقة العرض أو واجهة المستخدم أو ما يعرف بالواجهة الأمامية للتطبيقات والمواقع وبين طبقة الوصول إلى البيانات أو ما يعرف بالواجهة الخلفية التي تعمل خلف الكواليس. هل يبدو هذا صعب الفهم! حسنًا لنفترض أنك تتصفح أحد متاجر بيع الملابس على الإنترنت أنت تتفاعل الآن مع ما يسمى بالواجهة الأمامية للموقع فكافة أجزاء موقع الويب التي تراها أمامك والتي تعرض على متصفحك من عناوين وفقرات وصور وأزرار وأيقونات هي في الواجهة الأمامية للموقع. لكن من أين تم جلب قائمة المنتجات التي تراها في واجهة المستخدم؟ الجواب أنها خزنت في قاعدة البيانات المخزنة في الواجهة الخلفية وتم عرضها على الواجهة الأمامية لديك لتتصفحها، بعدها أعجبتك عدة قطع وقررت شراءها وحددت خياراتك المفضلة كاللون والقياس وما إلى ذلك ونقرت فوق زر إضافة لسلة المشتريات ثم أدخلت كافة بياناتك المطلوبة لكنك لم تكمل عملية الشراء لأن ثمن المشتريات لم يكن بحوزتك فخرجت من الموقع، برأيك أين تم حفظ هذه البيانات؟ نعم صحيح كما توقعت أيضًا في قاعدة بيانات الموقع المخزنة على الخادم. بعد ذلك بأيام عدت للموقع ونقرت فوق زر سلة المشتريات لتكمل عملية الشراء، سيقوم الكود المربوط بهذا الزر بسحب كل المعلومات التي أدخلتها من قاعدة البيانات ويحسب ثمنها ويرسلها إليك، كل هذا تم في الواجهة الخلفية للموقع، وبالتالي الواجهة الخلفية للموقع هي الجزء الخفي الذي يعمل وراء الكواليس، أما الكيفية التي ستعرض بها هذه البيانات لك فهي مسؤولية مطور الواجهة الأمامية. فأي كود مسؤول عن الجوانب المرئية لموقع الويب كتبه مطور الواجهة الأمامية وهو ينفذ في المتصفح، وأي كود يتعامل مع قواعد البيانات وواجهات برمجة التطبيقات كتبه مطور الواجهة الخلفية وهو ينفذ في الخادم. هناك نوع ثالث من المطورين يطلق عليه مطور الويب الكامل fullstack web developer أو المطور الكامل full stack developer وهو الذي يتولى بنفسه كتابة كل من كود الواجهة الخلفية والواجهة الأمامية للمواقع والتطبيقات ويملك خبرة بكليهما. للمزيد من التفصيل حول الواجهة الأمامية، يمكنك الرجوع إلى مقال تطوير الواجهة الأمامية لمواقع الويب Frontend Web Development الشامل. ما هو تطوير الواجهة الخلفية للويب؟ تطوير الواجهة الخلفية للويب أوما يعرف بتطوير النظم الخلفية backend development أو التطوير من طرف الخادم server-side هو عملية كتابة الشيفرات البرمجية التي تعمل على خوادم الويب web servers والتي تجعل موقع الويب يعمل خلف الكواليس ويتفاعل مع المستخدمين ويلبي كافة طلباتهم. الهدف من تطوير الواجهة الخلفية هو خلق تواصل بين الواجهات الأمامية وبين قاعدة البيانات عبر الخادم فالمستخدم يتفاعل مع الموقع عن طريق إدخال البيانات بعدها نخزن هذه البيانات في قاعدة البيانات الموجودة على نفس الخادم أو خادم منفصل، وأخيرًا يعيد الخادم النتائج المطلوبة ويعرضها على المتصفح أو ما يعرف بالعميل أو الواجهة الأمامية للموقع. لا غنى عن تطوير الواجهات الخلفية في مواقع الويب الديناميكية dynamic websites التي تتفاعل مع المستخدمين وتحتاج إلى تخزين البيانات وفهرستها وحفظها واستردادها وتعديلها وحذفه مثل مواقع التجارة الإلكترونية والمواقع التي تتطلب ملء نماذج وتخزين البيانات المتغيرة مثل وصف المنتجات والمنشورات وملفات تعريف المستخدمين وما إلى ذلك. أما مواقع الويب الثابتة أو الساكنة static websites فهي لا تتطلب تطوير الواجهة الخلفية وتكون مجرد مواقع بسيطة بمحتوى ثابت ولا تتفاعل مع المستخدمين ولا تستطيع تخزين أي شيء وتسعمل خوادم شبه جاهزة لتخديم البيانات. أهمية تطوير الواجهة الخلفية في سوق العمل لا شك أن أهمية تطوير الواجهة الخلفية ينتج عن انتشار تطبيقات الويب وليس فقط مواقع الويب والاعتماد الكبير على عالم الإنترنت والتطبيقات السحابية من برامج محاسبة وفيديو وإدارة مهام …إلخ. فجميع هذه التطبيقات تحتاج إلى بيانات ديناميكية وبالتالي تحتاج جميعها لتطوير واجهات خلفية. وحسب استطلاع الرأي الذي أجراه موقع موقع Stackoverflow الشهير للعام 2022 والذي يشارك فيه عدد كبير من المطورين والمبرمجين حول العالم تبين أن أكثر غالبية المشاركين في الاستطلاع ونسبتهم 46.82% هم في المقام الأول مطورون كاملون full-stack يليهم مباشرة مطورو الواجهة الخلفية back-end بنسبة 43.38% وبعدهم مطورو الواجهة الأمامية front-end بنسبة 25.96% ويلي ذلك باقي التخصصات البرمجية الأخرى. هذا يدل على أهمية مجال تطوير الويب عمومًا وتطوير الواجهة الخلفية على وجه الخصوص والطلب الكبير عليه في سوق العمل وإلا لم يكن الإقبال عليه ليصل لهذه النسب المرتفعة. كما يعد تطوير تطوير الواجهة الخلفية مجالًا وظيفيًا مربحًا وسريع النمو حيث متوسط الراتب السنوي لمطور الواجهة الخلفية وفق نفس الاستبيان في الولايات المتحدة، في عام 2022، بما قدر بـ 68 ألف دولار سنويًا وهذا الرقم بلا شك يختلف من بلد لآخر ويعتمد على سنوات خبرة المطور إلا أنه يظل رقمًا مرتفعًا مقارنة بباقي المهن والتخصصات. وبالتالي فإن استثمارك في تعلم تطوير الواجهات الخلفية للويب يفتح أمامك الكثير من فرص العمل المجزية والمضمونة، وطالما أن الأشخاص والشركات بحاجة إلى مواقع الويب والمتاجر الإلكترونية فستكون هناك حاجة إلى مطوري ويب. مهام ومسؤوليات مطور الواجهة الخلفية إذا قررت العمل كمطور واجهة خلفية فهذا يعني أنك ستكون المسؤول الأساسي عن تطوير كافة أجزاء موقع الويب التي تعمل من جانب الخادم وصيانتها واختبارها وتصحيحها. يمكن القول أنك مسؤول عن العمود الفقري لتطبيقات الويب والعمل في هذا المنصب يتطلب منك القيام بعدة مهام تشمل ما يلي: التواصل مع العملاء وأعضاء الفريق البرمجي وفهم احتياجات الموقع أو التطبيق وتحديد كافة البيانات والوظائف المطلوبة للعمل في الواجهة الخلفية. كتابة الشيفرات البرمجية التي تحقق وظائف الموقع باستخدام لغات البرمجة التي تعمل من جانب الخادم. معالجة كافة طلبات مستخدمي الموقع. صيانة عناصر الواجهة الخلفية واستكشاف أي أخطاء فيها وإصلاحها. إنشاء قواعد بيانات الموقع وتحسينها وإدارتها والاستعلام منها. إنشاء واجهات برمجة التطبيقات API من جانب الخادم والتي يحتاجها مطورو الواجهة الأمامية لاسترداد البيانات وعرضها. إعداد وإدارة بيئات الاستضافة وتثبيت البرنامج الضرورية على الخادم. تحسين أداء تطبيقات الويب وأمانها وتخفيف زمن تحميلها واستجابتها وتعزيز تجربة مستخدميها. دمج الخدمات الخارجية مثل بوابات الدفع والخدمات السحابية مثل Amazon Web Services و Azure مع الموقع. إجراء عمليات النسخ الاحتياطي واستعادة ملفات الموقع وقاعدة البيانات في حال وقوع أي طارئ. التأكد من أمان الموقع وحمايته من الاختراق. بعد الاطلاع على قائمة المهام أعلاه هل تجد مجال تطوير الواجهات الخلفية مناسب لك؟ أنصحك بمتابعة قراءة هذا المقال لتتعرف على أهم لغات البرمجة لتطوير الواجهات الخلفية وأهم النصائح التي تساعدك لتصبح مطور واجهات خلفية محترف. لغات تطوير الواجهة الخلفية Backend يمكن لمطوري الواجهات الخلفية استخدام العديد من لغات البرمجة والأدوات وأطر العمل التي تسهل عملية بناء تطبيقات تتصل بالخادم وتتفاعل مع قواعد البيانات، ستكون أول خطوة عليك القيام بها كمطور واجهات خلفية هو تحديد اللغات التي ستستخدمها، ولتسهيل المهمة عليك فقد جمعنا لك قائمة بأفضل لغات تطوير النظم الخلفية التي يمكنك استخدامها في عملك وأهم مميزاتها وأطر عملها. أهم لغات تطوير الواجهة الخلفية: لغة جافا سكريبت JavaScript مع Node.js لغة بايثون Python لغة PHP لغة روبي Ruby لغة جافا Java لغة C# دعنا نتناول كل لغة من بينها بمزيد من التفصيل ونتعرف على وأطر عملها ودورها الفعال في تطوير الواجهات الخلفية للويب. لغة جافا سكريبت JavaScript تعد جافا سكريبت واحدة من أكثر لغات البرمجة شهرة وانتشارًا بين أوساط المبرمجين بفضل سهولتها وإمكانية استخدامها لكل من تطوير الواجهة الخلفية والأمامية للويب، فهي تستخدم بشكل أساسي لإنشاء مواقع تفاعلية وديناميكية من جانب العميل وتعمل ضمن المتصفحات فقط، لكن بات بمقدورها بفضل بيئة تشغيل Node.js العمل على جانب الخادم وبرمجة الواجهات الخلفية بخروجها من بيئة المتصفح وبهذا يمكنك تعلم جافا سكريبت من العمل كمطور كامل fullstack ويفتح لك فرصًا أكبر. توفر جافا سكريبت كذلك الكثير من أطر عمل تطوير الواجهة الخلفية التي توفر مجموعة من الأكواد والمكتبات الجاهزة للاستخدام والتي تبسط مهام تطوير المواقع وتجعلها أسرع وأكثر كفاءة ومن ضمنها Express.js و Meteor.js وBackbone.js لكل إطار عمل منها ميزات مختلفة ويصلح لأنواع مواقع معينة أكثر من غيرها لذا عليك البحث في الإطار الذي تود استخدامه والتأكد أنه يلبي متطلباتك بشكل جيد. لغة بايثون Python تعد لغة بايثون إحدى أسهل لغات البرمجة وأكثرها شعبية بفضل بنيتها البسيطة والمقروءة، وهي تستخدم في العديد من المجالات ومن بينها تطوير الواجهات الخلفية للويب وهي تستخدم من قبل كبرى الشركات التقنية مثل إنستغرام و Dropbox. ويمكن استخدام أطر عمل بايثون مثل إطار العمل جانغو Django أو فلاسك flask أو FastAPI لتقليل كتابة التعليمات البرمجية وتبسيط عملية تطوير الواجهات الخلفية لمواقع الويب المتقدمة بشكل أسرع و أكثر أمانًا. لغة PHP ذاع صيت لغة PHP لفترة طويلة كواحدة من أشهر لغات الويب وهي تستخدم في تطوير حوالي 80% مواقع الويب في العالم لكونها لغة سريعة ومرنة يمكنها تطوير كافة أنواع المواقع من المدونات البسيطة إلى المتاجر الرقمية المتقدمة. لم تفقد PHP قوتها أمام اللغات الأخرى بفضل قوة أطر العمل المبنية عليها ولعل أشهرها إطار العمل لارافيل Laravel وهو إطار عمل مفتوح المصدر لتطوير الواجهات الخلفية لمواقع الويب بشكل منظم وسهل وآمن وسهل الاستخدام. إلى جانب العديد من أنظمة إدارة المحتوى CMS التي تجعل من تطوير المواقع وإنشاء المحتوى وتعديله وإدارته غاية في السهولة ولا يستلزم أي مهارات تقنية ومن أشهرها ووردبريس WordPress ودروبال Drupal. لغة روبي Ruby روبي هي لغة برمجة نصية مفتوحة المصدر أطلقت عام 1995 وقد اكتسبت هذه اللغة شعبية كبيرة بين أوساط المبرمجين بفضل تركيبتها التي تشبه إلى حد كبير اللغة الإنجليزية المحكية لذا تعد من أسهل لغات البرمجة المستخدمة في تطوير تطبيقات للويب. ورغم أنها ليست بشهرة اللغات السابقة في العالم العربي إلا أن شعبيتها تزداد بالتدريج على مستوى العالم وستجد الكثير من الوظائف المتوفرة عند تعلمها لاسيما عندما تتقن إطار عملها الشهير Ruby on Rails في تطوير تطبيقاتك فهو يسهل ويسرع عملك بشكل لافت. لغة جافا Java جافا هي لغة برمجة عريقة تم إطلاقها عام 1991 وهي لغة قوية ومتعددة الأغراض تعتمد مبدأ البرمجة كائنية التوجه OOP وعلى الرغم من إمكانية تشغيلها على المتصفح إلا أنها مصممة بشكل أساسي لتطوير الواجهات الخلفية للتطبيقات. بالرغم من أن شعبية جافا آخذة في الانخفاض وكونها لغة صعبة التعلم مقارنة بلغات البرمجة الأحدث إلا أنها لاتزال تستخدم في الكثير من مواقع الويب نظرًا لقوتها وضمان أمان التطبيقات المبنية بها لأنها لا تنفذ على الجهاز نفسه بل تنفذ على آلة جافا الافتراضية Java Virtual Machine أو اختصارًا JVM ما يضمن حماية التطبيقات وعزلها. كما تتضمن جافا العديد المكتبات وأطر عمل الواجهة الخلفية التي تسهل تطوير التطبيقات وصيانتها مثل Spring لذا تعد أحد الخيارات التي يمكنك الاعتماد عليها كمطور واجهات خلفية. لغة C# تعد لغة C# واحدة من أفضل لغات البرمجة وهي لغة عريقة عمرها يزيد على عشرين عامًا ولازالت تصلح لجميع أنواع التطبيقات ولبناء تطبيقات الويب يمكن استخدامها مع إطار عمل دوت نت NET Framework. الشهير الذي يستخدم لبناء تطبيقات ومواقع الويب بناء على لغة C# ويوفر بيئة تطوير واجهات خلفية قوية وفعالة يوفر وظائف مخصصة لإدارة كميات كبيرة من البيانات وإعدادات أمان قوية والكثير من الميزات الجاهزة ويسمح بالعمل مع واجهات برمجة تطبيقات الويب الأصلية. تقنيات وأدوات مهمة لتطوير الواجهة الخلفية Backend بعد أن تعرفت على أهم لغات برمجة وأطر عمل تطوير الواجهات الخلفية للويب دعنا نتعرف الآن على التقنيات والمهارات والأمور الأخرى التي تفيدك بشكل كبير في العمل تطوير الواجهات الخلفية. واجهات برمجة تطبيقات API قواعد البيانات وأبرز أنواعها ونظم إدارتها معرفة أساسية بالشبكات وأمنها أسس التعامل مع الخوادم والاستضافات ومزودات الخدمة السحابية نظم التحكم بالإصدارات Git أدوات إدارة الحزم Package Management System استخدام الحاويات Containers الإحاطة بأساسيات لغتي HTML و CSS وإليك معلومات إضافية عن كل تقنية من هذه التقنيات وكيف تفيدك كمطور ويب. واجهات برمجة تطبيقات API من الضروري كمطور واجهة الخلفية أن تتعلم مفهوم واجهة برمجة التطبيقات Application Programming Interface أو اختصارًا API وهو آلية تمكن التطبيقات والمواقع من التواصل مع بعضها والحصول على البيانات الخاصة ببعضها مهما كانت التقنية التي طورت بها هذه التطبيقات وسواء كانت تطبيقات واجهة خلفية تعمل من طرف الخادم أو تطبيقات واجهة خلفية تعمل من طرف العميل. ومن أبرز الأمثلة على أنواع واجهات برمجة التطبيقات نذكر RESTful API الذي يستخدم تنسيق JSON لتبادل البيانات و GraphQL الذي يعد مفهومًا لنقل وتبادل البيانات على شكل استعلامات queries، ويجب أن تحيط بهذين المفهومين جيدًا كمطور واجهات أمامية. وللمزيد حول مفهوم واجهات برمجة التطبيقات وكيفية استخدامها أنصح بمشاهدة فيديو ما هي الواجهة البرمجية API التالي: قواعد البيانات وأبرز أنواعها ونظم إدارتها يحتاج أي مطور واجهات خلفية لامتلاك معرفة في طريقة التعامل مع قواعد البيانات ونظم إدارتها وأنواعها المختلفة ومن أهمها: قواعد البيانات العلائقية التي تستخدم لغة SQL وتخزن بيانات التطبيقات ضمن جداول مرتبطة ببعضها البعض ومن أبرز أنظمة إدارة قواعد البيانات العلائقية نذكر MySQL و SQLite و PostgreSQL. قواعد البيانات غير العلائقية NoSQL وهي أشهر بدائل قواعد البيانات العلائقية التي تخزن بيانات التطبيقات بطرق أخرى غير الجدول العلائقي ومن أشهر نظم إدراتها نذكر MongoDB وكاساندرا. وقد ناقشنا في مقال دليلك الشامل إلى قواعد البيانات DataBase المزيد من الأنواع وميزات واستخدامات كل نوع من بينها بالتفصيل. معرفة أساسية بالشبكات وأمنها تحتاج كمطور واجهة خلفية إلى امتلاك معرفة أساسية ببروتوكولات الاتصال عبر الويب مثل: بروتوكول HTTP بروتوكول HTTPS وشهادة SSL بروتوكول WebSocket كما يحتاج أي مطور ويب لفهم آلية عمل شبكة الإنترنت والمفاهيم الأساسية المرتبطة بها مثل عناوين بروتوكول الإنترنت IP Addresses وأسماء النطاقات في شبكة الإنترنت. ولا يجب أن يغفل عن مفاهيم أمن الويب واتخاذ كافة إجراءات الحماية اللازمة لحماية قواعد بيانات موقع الويب وحماية مسارات واجهة برمجة التطبيقات من الثغرات الأمنية ومن محاولة سرقة البيانات ويستخدم أساليب المصادقة والأذونات التي تتحكم في وصول المستخدمين للتطبيقات ويشفر أي بيانات حساسة ويتحقق من صحة أي مدخلات من قبل المستخدمين وغيرها الكثير من إجراءات الحماية. قد ترغب في مشاهدة الفيديو التالي للتعرف على مزيد من المعلومات حول أشهر طرق الاختراق وسبل الوقاية منها. معرفة جيدة بنظم التشغيل يحتاج مطور الواجهة الخلفية لامتلاك معرفة أساسية بنظم التشغيل وخصوصًا نظام التشغيل لينكس لأن أغلب الخوادم تعمل بنظام لينكس وبالتالي تعلمه يمكنك من معرفة أساسيات التعامل مع الخوادم والاستضافات ومزودات الخدمة السحابية والتعامل مع واجهات سطر الأوامر أو الطرفية وطرق رفع الملفات إلى الاستضافة والوصول إلى قاعدة البيانات المخزنة عليه. كما يحتاج لامتلاك معرفة كافية بمنصات استضافة الويب وطريقة نشر وتشغيل الموقع على خادم ويب مثل خادم أباتشي Apache و خادم إنجن إكس Nginx ومعرفة طريقة التعامل مع مزودات الخدمة السحابية والاستفادة من أنواع الخدمات السحابية المختلفة، ولمزيد من المعلومات يمكنك متابعة العديد من الدروس حول الحوسبة السحابية في أكاديمية حسوب. نظم التحكم بالإصدارات Git تساعد نظم التحكم بالإصدارات المطور في إدارة التغييرات التي يقوم بإجرائها على الشيفرات البرمجية لمشاريعه وتطبيقاته ومن أشهرها نذكر Git وهي عبارة أداة برمجية تمكنك من العودة بسهولة إلى إصدار سابق يعمل بشكل صحيح من مشروعك في حال حدث خطأ ما بدلاً من البحث عن سبب الخطأ ومحاولة التراجع عنه يدويًا، كما أنه يفيد المطور بشكل كبير عند العمل على مشروع مشترك مع فريق عمل فهو ينظم التعديلات المختلفة ويمنع تضاربها. والجدير بالذكر أن التعامل مع أنظمة التحكم بالإصدارات هو ضرورة ولا يستطيع أي مبرمج أو مطور ويب العمل دون خبرة بها وستجد في أكاديمية حسوب مجموعة غنية ومنوعة من المقالات والدروس حول Git التي تساعدك في تعلم التعامل مع نظم التحكم بالإصدارات من الصفر حتى الاحتراف. أدوات إدارة الحزم Package Management System تتضمن معظم التطبيقات اليوم حزمًا ومكتبات خارجية ومن الصعب تطوير التطبيقات اليوم دون الاعتماد على أحد الأدوات البرمجية المساعدة في إدارة هذه الحزم الخارجية وعدم تعارضها، ومن الأمثلة عليها مثل npm الذي يأتي مع Node.js الذي يُستخدم مع لغة جافا سكريبت أو composer المستخدم مع لغة PHP. فمعظم مشاريع الويب اليوم تتضمن الكثير من الحزم التي يعتمد بعضها على بعض وهذه البرمجيات أساسية لمساعدتك في تحديد الحزم اللازمة لمشروعك ويحدد التبعيات المناسبة بنفسه والتأكد من توافقها مع التبعيات الأخرى ويتحكم في إصدارات الحزم التي تقوم بتثبيتها. استخدام الحاويات Containers بالرغم من أن تعلم استخدام الحاويات Containers يعد مهارة إضافية وليس لزامًا عليك كمطور واجهة خلفية إلا أن تعلمها يساعدك في بناء مشاريعك واختبارها ونشرها بكفاءة فالحاويات هي عبارة عن حزم برمجية تتضمن كافة العناصر الضرورية لتشغيل التطبيق في أي بيئة أو نظام تشغيل لكنه يكون معزول ضمن حاوية معزولة منطقيًا عن التطبيقات الأخرى بحيث لا يتعارض معها. يمكنك إنشاء الحاويات وتشغيلها باستخدام تقنية مثل دوكر Dockerوهي تقنية حاوية مفتوحة المصدر تعتمد على نظام لينكس وتسمح للمطورين ببناء وتشغيل التطبيقات واختبارها ونشرها بسهولة، وللاطلاع على مزيد من التفاصيل حول مفهوم الحاويات وأهميتها لمطوري التطبيقات يمكنك مشاهدة الفيديو التالي: الإحاطة بأساسيات لغتي HTML و CSS يحتاج مطور الواجهات الخلفية معرفة الجوانب الأساسية لكل من HTML و CSS اللتان تستخدمان لبناء الواجهات الأمامية فحتى لو لم يستخدمها في عمله فهو يحتاج للتواصل مع مطوري الواجهة الأمامية بشأنها وهي تقنيات سهلة التعلم. من أهم الأساسيات التي ينبغي تعلمها في لغة HTML هي طريقة إنشاء صفحة ويب ثابتة وإنشاء النماذج ومعالجتها وآليات الإدخال المختلفة إضافة إلى معرفة طريقة إنشاء قواعد التنسيق في CSS وكيفية عمل المحددات والفرق بين المعرف id والصنف class. يمكنك معرفة المزيد حول هاتين اللغتين من خلال مطالعة مقال تعلم لغة HTML ومقال أساسيات لغة CSS. خارطة طريق لتعلم تطوير الواجهات الخلفية هل تشعر بالتشتت من كثرة التقنيات التي عليك تعلمها لتصبح مطور واجهة خلفية ولا تعرف من أي واحدة تبدأ وكيف تنظم خطوات التعلم؟ دعني أساعدك وأوضح لك خارطة طريق لتعلم مجال تطوير الواجهات الخلفية والتي تعرض بوضوح طريق مطور الواجهات الخلفية backend في مجال تطوير الويب بالكامل بدءًا من المرحلة الأساسية وحتى المتقدمة وأغلب تلك المواضيع تجد عنها في أكاديمية حسوب وموسوعة حسوب فابحث عنها: أفضل المصادر العربية لتعلم تطوير تطبيقات الواجهة الخلفية إذا كنت تبحث عن مصادر عربية لتعلم تطوير الواجهة الخلفية ستجد العديد منها في أكاديمية حسوب التي تعد أكبر منصة عربية لتعليم البرمجة فهي توفر عدة دورات تدريبية لتطوير التطبيقات منها: دورة تطوير التطبيقات باستخدام لغة JavaScript دورة تطوير تطبيقات الويب باستخدام لغة PHP دورة تطوير التطبيقات باستخدام لغة Python دورة تطوير تطبيقات الويب باستخدام لغة Ruby تتميز كل دورة من هذه الدورات بكونها معدة بعناية من نخبة من المبرمجين في المجال وتتضمن العديد من المسارات التي تبدأ معك من أساسيات اللغة وتعلمك أهم أطر عملها وتنتهي بك في تطوير تطبيقات عملية احترافية تؤهلك للدخول في سوق العمل كما أنك تحصل على متابعة دورية من قبل فريق من المدربين الذين يجيبون على كافة تساؤلاتك بخصوص ما تتعلمه ويوجهونك خلال تطبيق المشاريع، وستحصل في نهاية كل دورة على شهادة معتمدة من أكاديمية حسوب تؤهلك لدخول سوق العمل. كما توفر أكاديمية حسوب مئات المقالات والسلاسل التعلميمة توفر شروحات مميزة لكافة اللغات والأطر والتقنيات التي ذكرناها في سياق مقالنا مثل مقال المدخل الشامل لتعلم تطوير الويب وبرمجة المواقع الذي يرشدك لكل ما يخص تطوير الويب بكافة أقسامه وتخصصاته وغيرها الكثير من المقالات المتقدمة لذا احرص على متابعة دروس الأكاديمية أولًا بأول. وإذا كنت تفضل الدراسة من الكتب لما توفره من تسلسل منهجي في طرح المعلومات ستجد في قسم الكتب في أكاديمية حسوب العديد من الكتب القيمة المتخصصة بشرح لغات البرمجة وقواعد البيانات وتقنيات تطوير الويب وغيرها من العناوين المهمة والمفيدة لأي مطور. أسئلة شائعة حول تطوير الواجهات الخلفية في بداية مشوارك في تطوير الواجهة الخلفية قد تدور بذهنك عدة تساؤلات حول هذا التخصص ومدى صعوبته وملائمته لك وإليك أبرز هذه التساؤلات والإجابات عليها 1. هل يمكن أن أتعلم تطوير الواجهة الخلفية بنفسي؟ نعم بكل تأكيد فتطوير الويب عمومًا وتطوير الواجهة الخلفية خصوصًا لا تتطلب منك الحصول على شهادة جامعية لتمارسها بل يمكنك تعلمها ذاتيًا واكتساب كافة المهارات اللازمة لتطوير مواقع وتطبيقات الويب بنفسك ولحسن الحظ موارد التعلم كثيرة ومتاحة ويمكنك الاعتماد عليها للتعلم الذاتي. وتذكر أن مفتاح نجاحك كمطور واجهة خلفية ليس مرتبطًا بالشهادات بقدر ما يعتمد على اكتساب المهارات المناسبة التي تمكنك من دخول سوق العمل وتطوير مشاريع تعكس تمكنك من هذه التقنيات والمهارات وتعزز فرصتك في العثور على العمل الذي يحقق طموحك. 2. هل وظيفة تطوير الواجهات الخلفية مناسبة لي؟ بالرغم من أن المهارات التقنية هي أول ما ينظر له عند توظيف مطور واجهات خلفية لكن هذا ليس كل شيء فكي تصبح مطور واجهة خلفية محترف يجب أن تمتلك بعض الصفات الأخرى مثل المهارات التحليلية والقدرة على حل المشكلات إلى جانب مهارات التواصل لاسيما عندما تعمل ضمن فريق عمل يضم عدة مطورين، كما يجب أن تمتلك حس الفضول والرغبة في تعلم كل جيد فكل يوم هناك إصدارات وأدوات وإضافات جديدة وعليك بذل الوقت والجهد لتعلمها، لذا تأكد من أنك شخص يمتلك هذه الصفات ولا تركز فقط على الجوانب الفنية وقرر بناء على ذلك إن كان العمل كمطور واجهات خلفية يناسبك أم لا. 3. هل يجب علي تعلم كل التقنيات الواردة في هذا المقال لأبدأ العمل كمطور واجهة خلفية؟ لحسن الحظ لا يلزمك تعلم كل هذه التقنيات لتبدأ تطوير الويب الخلفي فإذا امتلكت المهارات الأساسية بإحدى لغات الواجهة الخلفية وتعلمت إطار عمل واحد على الأقل وأتيحت لك فرصة عمل على مستوى المبتدئين لا تتردد واقبلها فورًا، سيطلب منك حينها البدء بمشاريع عملية صغيرة تناسب مهاراتك وبعدها ستتاح لك الفرصة لتوسيع مجموعة المهارات الخاصة بك وتعلم لغات وأدوات برمجة جديدة ومن جديد أؤكد لك بأن الخبرة العملية هي أكثر ما يساعدك على التطور. 4. ما هو الوقت اللازم لتعلم تطوير الواجهات الخلفية Backend؟ لا شك أن تعلم تطوير الواجهة الخلفية يتطلب معرفة ترسانة مختلفة من لغات البرمجة ومهاراتها وهذا يتطلب بلا شك بعض الوقت والالتزام لكن رغم ذلك تعتمد المدة اللازمة لتعلم تطوير الواجهة الخلفية على عدة عوامل أهمها مدى قابليتك وسرعتك في التعلم وكفاءة المصادر التي تتعلم منها ومدى التزامك بخطة منهجية وعدد الساعات التي تقضيهًا يوميًا في للتعلم. ولا شك أن المعرفة المسبقة بعلوم الحاسوب وأساسيات الشبكات وكنت على دراية بإحدى لغات البرمجة وكتابة الشيفرات البرمجية فهذا يختصر عليك زمن التعلم بشكل كبير، ويمكن القول أن فترة التعلم تتراوح وسطيًا بين 6 أشهر إلى عام لتصبح جاهزًا لدخول سوق العمل في حال اتبعت كل الخطوات والنصائح التي أوردناها في سياق المقال بعدها يمكن أن تتابع تطوير نفسك بشكل تدريجي وتصبح خبيرًا في المجال. 5. ما مدى صعوبة تعلم تطوير الواجهة الخلفية للويب؟ ستشعر بالطبع ببعض الصعوبات عند تعلم تطوير الواجهات الخلفية لأول مرة وستواجه الكثير من التحديات والعثرات خلال رحلة التعلم، لكن بقليل من المثابرة والاستعانة بمصادر تعلم مناسبة وفعالة يمكنك تجاوزها بسهولة وتذكر أنك تستثمر في مجال وظيفي مربح ومضمون لذا يستحق الأمر بذل الجهد. 6. أيهما أصعب تطوير الواجهة الخلفية أم الأمامية؟ برأيي الشخصي يعتمد الجواب على هذا السؤال على طبيعة الشخص وميوله فالبعض يرى أن تطوير الواجهة الخلفية هو الأصعب لكونه يتطلب تعلم تقنيات برمجية أكثر ويستلزم تعلم التعامل مع قواعد البيانات والخوادم والشبكات وضمان أمان التطبيقات كما أنه يتطلب امتلاك عدة مهارات شخصية أهمها التفكير المنطقي والتحليلي التي عليك تطويرها إلى جانب تطوير مهاراتك الفنية لتنجح في سوق العمل. بالمقابل يمكن أن يرى بعض الأشخاص أن تطوير الواجهات الأمامية هو الأصعب لكونه لا يخضع لقواعد ثابتة ويحتاج للاهتمام بالكثير من الجوانب المرئية مثل تجاوب التصميم مع كافة أنواع الأجهزة وأحجام الشاشات وفهم الاختلافات بين المتصفحات واختبار توافق التصاميم عليها ومراعاة تجربة المستخدم ولا ننسى الجانب الإبداعي والذوق في التصميم الإبداعي الذي قد لا يتوفر لدى جميع الأشخاص. ولمساعدتك في هذا الشأن أنصحك بالاطلاع على مقال دليلك الشامل إلى تطوير الواجهات الأمامية لتتعرف على كل ما يخص هذا المجال وتقرر بنفسك أين تبدو الأمور أسهل بالنسبة لك. الخلاصة تعرفنا في مقال اليوم على مجال تطوير الوجهة الخلفية للويب الذي يعنى بتطوير أجزاء تطبيقات ومواقع الويب التي تعمل من جهة الخادم وتعرفنا على أهم الفروقات بين تطوير الواجهة الأمامية الخلفية، وأبرز التقنيات واللغات والأطر المستخدمة في برمجة الواجهة الخلفية وتحقيق وظائف التطبيقات والمواقع الإلكترونية بكفاءة وأهم الخطوات والمصادر التي تساعدك على تعلمها بسهولة وكفاءة. وفي ختام مقالنا جاوبنا على أبرز الأسئلة التي قد تتبادر لذهن أي مبتدئ يرغب بتعلم تطوير الواجهات الخلفية، وفي حال كان لديك سؤال آخر لم نجب عليه في سياق المقال يمكنك تركه في قسم التعليقات أسفل المقال. اقرأ أيضًا المدخل الشامل لتعلم تطوير الويب وبرمجة المواقع برمجة مواقع الويب: دليلك المختصر مدخل إلى تطوير البرمجيات Software Development ما هي صفحات الويب1 نقطة
-
تُسمى الأخطاء في برامج الحاسوب عادةً بالزلات bugs، ونحن من نضع هذه الزلات في برامجنا بأيدينا حين نخطئ في شيء ما، أو ننسى رمزًا، أو محرفًا، أو نضع واحدًا في غير محله، وإن كان يحلو للكثير منا أن يظن بأنها تزحف من تلقاء نفسها إلى داخل الشيفرة، ولو قلنا أن البرنامج هو فكرة متبلورة في ذهن المبرمج، فاحتمالية حدوث ثغرة أو خطأ برمجي في هذا البرنامج لن تخرج من أحد شيئين: الفكرة نفسها معيبة أو مشوهة. حدوث خطأ أثناء ترجمة البرنامج من فكرة إلى شيفرة برمجية. والحالة الثانية أيسر في اكتشافها وحلها من الأولى، إذ يكون البرنامج سليمًا عدا هذه الثغرة أو الخطأ؛ أما إن كان البرنامج كله مشوهًا نتيجة عيب في كل من الفكرة والمبدأ اللذَين بُني عليهما، فسيكون ذلك أصعب بكثير، ويُطلق على عملية اكتشاف الأخطاء وتصحيحها في الاصطلاح الأجنبي debugging. اللغة سبب أغلب الأخطاء التي تحدث في البرامج التي نكتبها كما ذكرنا هو نحن المبرمجين، ولو كان الحاسوب يَعقل لقذف تلك المشاكل في وجوهنا، وإذا علمت مرونة جافاسكربت العالية لأدركت مدى صعوبة تنقيح الأخطاء الموجودة في شيفراتك التي تكتبها. تُعَدّ بنية الرابطات bindings، والخصائص properties مبهمةً إلى الحد الذي يندر معه اكتشاف الأخطاء الكتابية قبل تشغيل البرنامج، وحتى عند التشغيل، إذ تسمح لك بالقيام بأمور غير منطقية دون تنبيهك إليها مثل حساب `true * "monkey". لكن رغم تلك المرونة الكبيرة في جافاسكربت، إلا أن لها حدودًا لا تتسامح معها، فمثلًا، ستجعل الحاسوب ينبهك فورًا إذا كتبت برنامجًا لا يتبع قواعدها وبنيتها اللغوية؛ كما سيُحدث استدعاء شيء ما غير الدوال أو البحث عن خاصية في قيمة غير معرفة خطأً يُرسَل في تقرير حين يحاول البرنامج تنفيذ هذا الإجراء -أي عند الاستدعاء أو البحث في هاتين الحالتين-. لكن الغالب أنه لن تُنتج حساباتك الغير منطقية سوى NaN -أي ليس عددًا Not A Number-، أو قيمة غير معرفة undefined value، وسيتابع البرنامج تنفيذه ظانًا أنه يقوم بشيء مفيد، إذ لن تظهر المشكلة إلا لاحقًا بعد مرور تلك القيمة الزائفة على عدة دوال، وقد لا تُطلق إنذار الخطأ على الإطلاق، لكنها تتسبب في خطأ الخرج الناتج من البرنامج في نفس الوقت! وبناءً على ذلك فمن الصعب العثور على مصدر مثل تلك المشاكل. الوضع الصارم يمكن تقييد جافاسكربت للحد من مرونتها العالية، وذلك من خلال تفعيل الوضع الصارم strict mode فيها، ويكون هذا بوضع السلسلة النصية "use strict" في رأس الملف أو متن الدالة، انظر مثالًا لذلك كما يلي: function canYouSpotTheProblem() { "use strict"; for (counter = 0; counter < 10; counter++) { console.log("Happy happy"); } } canYouSpotTheProblem(); // → ReferenceError: counter is not defined إذا نسيت وضع let قبل الرابطة، كما في حالة counter التي في المثال أعلاه، فستُنشِئ جافاسكربت رابطةً عامةً global binding وستستخدِمها؛ أما في الوضع الصارم فلا يحدث ذلك، بل تبلغك اللغة بالخطأ، وذلك أكثر فائدةً لك في البرمجة؛ لكن يجب الانتباه إلى أن هذا لا يحدث حين تكون الرابطة موجودة أصلًا على أساس رابطة عامة، ففي تلك الحالة ستظل الحلقة التكرارية تستبدل قيمة الرابطة. كما تحمل رابطة this في الوضع الصارم قيمةً غير معرفة undefined في الدوال التي لا تُستدعى على أساس توابع methods؛ أما في الاستدعاء العادي، فستشير this إلى كائن النطاق العام global scope object الذي تكون خصائصه هي الرابطات العامة، فإن استدعيت تابعًا أو بانيًا بالخطأ في الوضع الصارم، فستعطيك جافاسكربت الخطأ بمجرد محاولة قراءة شيء من this بدلًا من الكتابة في النطاق العام، فمثلًا، انظر الشيفرة التالية التي تستدعي دالة باني دون كلمة new المفتاحية كي لا تشير this فيها إلى كائن باني جديد: function Person(name) { this.name = name; } let osama = Person("Osama"); // oops console.log(name); // → Osama ينجح هنا هذا الاستدعاء الزائف إلى person، لكنه يعيد قيمةً غير معرَّفة، وينشئ رابطة name العامة؛ أما في الوضع الصارم فستكون النتيجة مختلفةً، انظر كما يلي: "use strict"; function Person(name) { this.name = name; } let osama = Person("Osama"); // forgot new // → TypeError: Cannot set property 'name' of undefined كما ترى فقد أخبرتنا اللغة مباشرةً بوجود خطأ ما، وهذا أكثر فائدةً لنا لا ريب. لحسن حظنا فستشتكي البواني constructors التي أُنشئت باستخدام صيغة class إذا استُدعيت من غير new، مما يجعل هذه المشكلة أقل إزعاجًا حتى في الوضع العادي أو خارج الوضع الصارم، وإضافةً إلى ما سبق، فيملك الوضع الصارم بعض الخصائص الأخرى، إذ يرفض إعطاء دالة ما عوامل متعددة بالاسم نفسه، كما يزيل بعض مزايا اللغة المسببة لمشاكل مثل تعليمة with التي لن نذكرها مرةً أخرى في هذه السلسلة لكثرة مشاكلها. لن يؤذيك ولن يضرك استخدام الوضع الصارم عن طريق كتابة `"use strict" في المجمل، وإنما ينفعك ويفيدك في اكتشاف المشاكل. الأنواع Types تريد بعض اللغات معرفة أنواع الرابطات والتعبيرات قبل تشغيل البرنامج، حيث ستخبرك مباشرةً إذا استُخدِم أحد الأنواع بصورة متناقضة؛ أما جافاسكربت فلا تنظر إلى الأنواع إلا عند تشغيل البرنامج، كما تحاول ضمنيًا تحويل القيم إلى الأنواع التي تتوقعها هي عادةً. لكن مع هذا، تقدم الأنواع إطار عمل framework مفيدًا عند الحديث عن البرامج، فتَنتج أكثر الأخطاء من الجهل بنوع القيمة الداخلة إلى دالة أو الخارجة منها، فإذا كانت عندك هذه المعلومات مكتوبةً، فسيقل احتمال حدوث تلك الأخطاء لا ريب، حيث تستطيع إضافة تعليق مثل الذي في الشيفرة التالية قبل دالة goalOrientedRobot التي أنشأناها في المقال السابق لتصف نوعها: // (VillageState, Array) → {direction: string, memory: Array} function goalOrientedRobot(state, memory) { // ... } هناك العديد من الاصطلاحات المتّبَعة لإدخال الأنواع في برامج جافاسكربت، وتحتاج الأنواع إلى تحديد مدى تعقيدها لتستطيع وصف ما يكفي من الشيفرة بحيث يكون مفيدًا، فماذا يكون النوع الخاص بدالة randomPick مثلًا التي تعيد عنصرًا عشوائيًا من مصفوفة ما؟ سنحتاج إلى تحديد متغير نوع type variable، وليكن T مثلًا الذي سيمثل أي نوع، وبذلك نستطيع إعطاء randomPick نوعًا مثل ([T]) → T، وهي دالة من مصفوفة مكونة من Ts إلى T. إذا كانت الأنواع الموجودة في البرنامج معروفةً، فسيتمكن الحاسوب من التحقق منها بدلًا عنك، وذلك ليُخرج لك الأخطاء قبل تشغيل البرنامج. هناك العديد من أشكال جافاسكربت التي تضيف الأنواع إلى اللغة وتتحقق منهم، لعل أشهرها لغة TypeScript، وننصحك بتجربتها إن كنت تريد إضافة بعض الصرامة إلى برامجك؛ أما في هذه السلسلة فسنعمل بجافاسكربت العادية. الاختبار إذا لم تعيننا اللغة على إيجاد الأخطاء، فيجب البحث عنهم بالطريقة الصعبة من خلال تشغيل البرنامج، وعندها سنرى إن كان سيعمل كما خططنا له أم لا، لكن تنفيذ هذا يدويًا مرةً بعد مرة ليس الطريقة المثلى للبرمجة، حيث تُعَدّ مزعجةً وغير عملية بما أنها تستغرق وقتًا طويلًا وجهدًا مضنيًا لاختبار كل شيء في كل مرة تغير فيها شيئًا واحدًا. كما سنستغل قدرة الحواسيب الهائلة في العمليات التكرارية، بما أن الاختبار في حد ذاته عملية تكرارية، فلمَ لا نجعل هذه العملية مؤتمتةً؟ وسيكون ذلك بكتابة برنامج اختبار يتحقق من البرنامج الخاص بنا. وقد تقف هنا لحظة لتقول ألم نرد وسيلةً لنقلل بها الجهد الواقع علينا؟ ونجيبك أن بلى، لكن الجهد الذي ستبذله مرةً واحدةً فقط في كتابة هذا الاختبار، سيغنيك طيلة العمل على البرنامج محل المشروع نفسه الذي بين يديك، وستشعر أن بيديك قوةً خارقةً لا تأخذ سوى بضع ثواني، حيث سيتحقق من عمل برنامجك بكفاءة في كل المواقف التي كتبت اختبارات لها، وستلاحظ مباشرةً فور تعطيلك لشيء ما بالخطأ، بدلًا من مرور الخطأ وعدم الشعور به إلا حين تقابله صدفةً مرةً أخرى أثناء العمل على شيء جديد. تأخذ الاختبارات عادةً صورة برامج صغيرة معنونة لتتحقق من أجزاء بعينها في شيفرتك، فمثلًا، ستكون بعض الاختبارات القياسية لتابع toUpperCase -والتي لعل أحدًا غيرنا اختبرها من قبل- على الصورة التالية: function test(label, body) { if (!body()) console.log(`Failed: ${label}`); } test("convert Latin text to uppercase", () => { return "hello".toUpperCase() == "HELLO"; }); test("convert Greek text to uppercase", () => { return "Χαίρετε".toUpperCase() == "ΧΑΊΡΕΤΕ"; }); test("don't convert case-less characters", () => { return "مرحبا".toUpperCase() == "مرحبا"; }); تُنتِج كتابة الاختبارات التي تحاكي المثال أعلاه شيفرات متكررةً وغريبةً، ولحل هذه المشكلة فلدينا برامج ستساعدك على بناء وتشغيل تجميعات مختلفة من الاختبارات (حِزم اختبارات)، وذلك من خلال توفير لغة مناسبة في شكل دوال وتوابع تناسب كتابة الاختبارات، وكذلك بإخراج معلومات مفيدة حينما يفشل أحد تلك الاختبارات، ويطلق على ذلك عادةً اسم منفِّذات الاختبارات test runners. كلما زاد عدد الكائنات الخارجية التي تتعامل الشيفرة معها، صعُب إعداد سياق لاختبارها فيه، وقد كان أسلوب البرمجة الذي عرضناه في المقال السابع أسهل في الاختبار، حيث استخدَم قيمًا ثابتةً persistent values عوضًا عن كائنات متغيرة. التنقيح Debugging إذا عرفت أن ثمة شيء في برنامجك يجعله يتصرف على نحو لم تتوقعه أو ينتج أخطاءً، فالخطوة التالية منطقيًا هي معرفة ما هو ذلك الشي أو هذه المشكلة، وقد تكون تلك المشكلة واضحةً أحيانًا، إذ تشير رسالة الخطأ إلى سطر بعينه في برنامجك، حيث سترى المشكلة إذا نظرت إلى وصف الخطأ وذلك السطر بنفسك. لكن أحيانًا يكون السطر المسبب للمشكلة ضحيةً لاستخدام قيمة متذبذبة وغير مستقرة عليه، بحيث تكون تلك القيمة منتَجةً في مكان آخر، وتُستخدم في هذا السطر بصورة خاطئة، ومن المحتمل رؤيتك لهذا إن جربت حل التمارين التي في الفصول السابقة من هذه السلسلة. يحول البرنامج في المثال التالي العدد الصحيح إلى سلسلة نصية في نظام ما سواءً كان ثنائيًا، أو عشريًا، أو غيرهما، وذلك بأخذ آخر رقم، ثم تقسيم العدد للتخلص من ذلك الرقم، وهكذا دواليك، لكن يشير الخرج الذي ينتجه البرنامج الآن إلى وجود خطأ ما. function numberToString(n, base = 10) { let result = "", sign = ""; if (n < 0) { sign = "-"; n = -n; } do { result = String(n % base) + result; n /= base; } while (n > 0); return sign + result; } console.log(numberToString(13, 10)); // → 1.5e-3231.3e-3221.3e-3211.3e-3201.3e-3191.3e-3181.3… لعلك انتبهت إلى المشكلة إذا نظرت إلى الشيفرة أعلاه، لكن نريدك التخيل للحظة أنك لا تعرفها ولم تلاحظها. لنبحث في سياق الحل والتنقيح الذي يجب عمله، إذ نعلم بعدم تصرف برنامجنا على النحو الذي نريده ونريد معرفة السبب، فهنا يجب مقاومة الرغبة في إجراء تغييرات عشوائية في الشيفرة من دون تفكير مسبق، وتحليل للقرار والتغييرات التي تجريها. نريدك الآن الوقوف للحظة، والتفكير، وتحليل الموقف وما يحدث مع البرنامج، وجمع الملاحظات حول ذلك، للخروج بنظرية حول سبب الخطأ، ثم اختبار تلك النظرية، وستكون إحدى طرق ذلك بوضع بعض استدعاءات console.log في البرنامج لتحصل على معلومات إضافية عما يفعله، كما نريد هنا في حالتنا لـ n أخذ القيم 13، و1، ثم 0. لنكتب قيمتها في بداية الحلقة التكرارية: 13 1.3 0.13 0.013 … 1.5e-323 لا تعطي قسمة 13 على 10 عددًا صحيحًا، لذلك نريد n = Math.floor(n / base) بدلًا من n /= base كي يتحرك العدد إلى اليمين كما نريد. المزايا التي توفرها أداة المنقِّح debugger والتي تأتي مدمجةً في المتصفحات هي إحدى الوسائل التي نستطيع استخدامها كي ننظر في سلوك البرنامج ونختبره، إذ تكون في هذه المتصفحات مزية إنشاء نقطة توقف breakpoint عند سطر بعينه داخل البرنامج، حيث سيتوقف تنفيذ البرنامج عند وصوله إلى ذلك السطر كي تنظر أنت في قيم الرابطات عند هذه النقطة وتفحصها، ولأن المنقِّحات تختلف من متصفح لآخر، فلن نخوض بك في تفاصيل هذه المزايا أكثر من ذلك، إذ سنترك لك حرية النظر في أدوات المطور في متصفحك، أو البحث في شبكة الويب عن مزيد من المعلومات عن هذا الأمر. بالعودة إلى فحص سلوك البرنامج، فمن الممكن إنشاء نقطة توقف بوضع تعليمة debugger في برنامجك، وهي تتكون من تلك الكلمة المفتاحية فقط، فإن كانت أدوات المطور مفعَّلة في متصفحك، فسيتوقف البرنامج عند وصوله إلى هذه التعليمة. توليد الخطأ لا يمكن للمبرمج منع كل الأخطاء الوارد حدوثها في البرنامج، خاصةً إذا كان البرنامج يتواصل مع العالم الخارجي بأيّ طريقة كانت، إذ من الممكن تلقيه مدخلات خاطئة، أو تحميله بأحمال ومهام زائدة عن طاقته، أو تفشل الشبكة التي يتواصل من خلالها، وذلك على سبيل المثال لا الحصر. لا تشغل بالك بشأن تلك المشاكل إذا كنت تبرمج لنفسك فقط، حيث تستطيع تجاهلها إلى حين حدوثها، ثم تتعامل معها حينها، لكن إن كنت تبني برنامجًا أو أداةً لعميل لك، أو برنامجًا سيستخدمه غيرك، فيجب أن تضع في حساباتك احتمالات التصرفات غير السليمة وغير المتوقعة، فقد يكون الحل الأمثل أحيانًا بتجاهل المدخل ليتابع البرنامج العمل، أو تبليغ المستخدم برسالة تفيد ما حدث، لكن يتوجب على البرنامج فعل شيء ما على أساس استجابة للمشكلة الواقعة في أي حالة كانت. لنقل مثلًا أن لديك دالةً اسمها promptNumber، حيث تطلب من المستخدِم إدخال عدد ثم تعيده هي، فلو أدخل المستخدم كلمةً مثل "orange" مثلًا، فما الذي ستعيده هذه الدالة؟ أحد الخيارات المتاحة هي جعل الدالة تعيد قيمةً خاصةً، مثل null، أو undefined، أو -1 كما يلي: function promptNumber(question) { let result = Number(prompt(question)); if (Number.isNaN(result)) return null; else return result; } console.log(promptNumber("How many trees do you see?")); يجب على أيّ شيفرة تستدعي promptNumber الآن التحقق من قراءة العدد الفعلي، وإذا فشلت فيجب إصلاح ذلك بطريقة ما، ربما بإعادة السؤال، أو بكتابة قيمة افتراضية، أو بإعادة قيمة خاصة إلى مستدعيها للإشارة إلى فشلها في فعل ما طُلب منها. وسترى أن مواقف عديدة يصلحها إعادة قيمة خاصة للإشارة إلى خطأ ما، خاصةً في حالة شيوع الخطأ ووجوب وضعه في الحسبان صراحةً؛ لكن هذا له سيئاته، فماذا لو كانت الدالة تستطيع إعادة جميع القيم الممكنة؟ فسيكون عليك فعل شيء ما عند التعامل مع تلك الدالة مثل تغليف النتيجة بكائن لتتمكن من تمييز حالة النجاح من الفشل. function lastElement(array) { if (array.length == 0) { return {failed: true}; } else { return {element: array[array.length - 1]}; } } والمشكلة الثانية عند إعادة قيم خاصة هي أن هذه الإعادة تؤدي إلى شيفرات غريبة، فإن استدعى جزء من الشيفرة الدالة promptNumber عشرة مرات، فعليه التحقق من إعادة null عشرة مرات أيضًا ، وإن كانت إجابته في التحقق من null هي إعادة null نفسها، فعلى من يستدعي الدالة التحقق منها بدورها، وهكذا. الاعتراضات Exceptions إذا لم تستطع دالة ما تنفيذ وظيفتها على النحو الذي صممت من أجله، فسيكون الحل هو إيقاف ما نفعله وننتقل فورًا إلى المكان الذي يعرف كيف يعالج هذه المشكلة وذلك العجز، وهذا هو دور معالجة الاعتراضات exception handling. الاعتراضات ما هي إلا آليات تمكّن الشيفرة التي تواجه مشاكل من رفع اعتراض أو تبلغ به، وقد يكون الاعتراض أي قيمة، ويمكن تشبيه هذا البلاغ أو الرفع بإعادة مشحونة نوعًا ما من الدالة، حيث تقفز من الدالة الحالية وممن استدعاها أيضًا لتصل إلى الاستدعاء الأول الذي بدأ التنفيذ الحالي، ويسمى هذا فك المكدس unwinding the stack، ولعلك تذكر مكدس استدعاءات الدالة الذي ذكرناه في المقال الثالث من هذه السلسلة، إذ يصغِّر الاعتراض هذا المكدس، ملقيًا لكل سياقات الاستدعاء التي يقابلها. لن تكون الاعتراضات ذات فائدة إن ذهبت مباشرةً إلى قاع المكدس، وما زادت على أن أتت بطريقة جديدة لبعثرة البرنامج، وإنما تظهر قوتها حين تضع عقبات obstacles لالتقاط هذه الاعتراضات وهي ماضية في المكدس، فبمجرد التقاطك لاعتراض ما، فستستطيع التعامل معه ومعالجته لرؤية أصل المشكلة، ثم تتابع تشغيل البرنامج، انظر مثلًا كما يلي: function promptDirection(question) { let result = prompt(question); if (result.toLowerCase() == "left") return "L"; if (result.toLowerCase() == "right") return "R"; throw new Error("Invalid direction: " + result); } function look() { if (promptDirection("Which way?") == "L") { return "a house"; } else { return "two angry bears"; } } try { console.log("You see", look()); } catch (error) { console.log("Something went wrong: " + error); } تُستخدم كلمة throw المفتاحية لرفع الاعتراضات، وتُلتقط بتغليف جزء من الشيفرة في كتلة try، متبوعةً بكلمة catch المفتاحية، وحين تتسبب الشيفرة التي في كتلة try في رفع اعتراض، فستُقيَّم كتلة catch مع ربط الاسم الموجود بين أقواس بقيمة الاعتراض، وإذا انتهت كتلة catch، أو try دون مشاكل، فسيتابع البرنامج سيره أسفل تعليمة try/catch بكاملها. استخدمنا في هذه الحالة بانيError لإنشاء قيمة الاعتراض الخاصة بنا، وهو باني جافاسكربت قياسي ينشئ كائنًا مع خاصية message، كما تَجمع نُسَخ هذا الباني في أغلب بيئات جافاسكربت معلومات عن مكدس الاستدعاء الذي كان موجودًا عند إنشاء الاعتراض فيما يسمى بتعقب المكدس stack trace، وتخزن هذه المعلومات في خاصية stack، كما يمكن الاستفادة منها حين محاولة تصحيح مشكلة ما، إذ تخبرنا بالدالة التي حدثت فيها المشكلة وأي الدوال نفّذت هذه الاستدعاء الفاشل. تتجاهل دالة look احتمال أن promptDirection قد تخطئ، وهذه مزية كبيرة للاعتراضات، إذ لا تكون شيفرة معالجة الخطأ ضروريةً إلا عند النقطة التي يحدث فيها الخطأ وعند النقطة التي يعالَج فيها؛ أما الدوال التي بين ذلك فلا تكاد تكون مهمةً. التنظيف وراء الاعتراضات يُعَدّ تأثير الاعتراض نوعًا آخرًا من تدفق التحكم، فكل حدث يسبب اعتراض -وهو كل استدعاء دالة تقريبًا وكل وصول لخاصية- قد يجعل التحكم يترك شيفرتك فجأة. فإن كانت الشيفرة بها عدة آثار جانبية، فقد يمنع اعتراض ما بعض تلك الآثار من الحدوث، حتى لو كان تدفق التحكم المنتظم لها يشير إلى احتمال حدوثها كلها، فمثلًا، انظر إلى المثال التالي لشيفرة مصرفية سيئة. const accounts = { a: 100, b: 0, c: 20 }; function getAccount() { let accountName = prompt("Enter an account name"); if (!accounts.hasOwnProperty(accountName)) { throw new Error(`No such account: ${accountName}`); } return accountName; } function transfer(from, amount) { if (accounts[from] < amount) return; accounts[from] -= amount; accounts[getAccount()] += amount; } تحوِّل دالة transfer مبلغًا من المال من حساب ما إلى حساب آخر، مع طلب اسم الحساب الآخر أثناء التحويل، وإذا أُدخل اسم حساب غير صالح، فسترفع getAccount اعتراضًا. تنقل transfer المال أولًا من الحساب الأول، ثم تستدعي getAccount قبل إضافة المال إلى حساب جديد، فإن توقف سير عملها بسبب رفع اعتراض، فسسيختفي المال ويضيع بين الحسابين! يمكن كتابة تلك الشيفرة بأسلوب أذكى من ذلك من خلال استدعاء getAccount قبل البدء بنقل المال مثلًا، لكن للأسف فقد تحدث المشاكل المشابهة لهذه المشكلة بطريقة غير واضحة، كما تكون أقل أثرًا من أن تُلاحظ بمجرد النظر، فحتى الدوال التي لا يُتوقع منها رفع اعتراضات، قد ترفعها في ظروف استثنائية أو حين تحتوي على خطأ المبرمج. إحدى الطرق التي يمكن معالجة ذلك بها، هي التقليل من استخدام الآثار الجانبية، باستخدام أسلوب برمجة يحسب القيم الجديدة بدلًا من تغيير البيانات الموجودة فعليًا، على سبيل المثال لا الحصر، فإذا توقف جزء من الشيفرة عن العمل أثناء إنشاء قيمة جديدة، فلن يرى أحد هذه القيمة غير الجاهزة، ولن نرى مشكلةً من الأساس. لكن هذا قد لا يكون عمليًا في كل مرة، لذا سننظر في ميزة أخرى في تعليمة try، إذ يمكن أن تُتبَع بكتلة finally بدلًا من كتلة catch أو بالإضافة إليها، وتقول كتلة finally "شغِّل هذه الشيفرة مهما حدث، وذلك بعد محاولة تشغيل الشيفرة في كتلة try". انظر كما يلي: function transfer(from, amount) { if (accounts[from] < amount) return; let progress = 0; try { accounts[from] -= amount; progress = 1; accounts[getAccount()] += amount; progress = 2; } finally { if (progress == 1) { accounts[from] += amount; } } } تتتبع هذه النسخة من الدالة مدى تقدمها وسيرها، وإذا تسبب في حالة غير مستقرة للبرنامج عند خروجها، فإنها تصلح أثر هذا الخلل الذي أحدثته. لاحظ أن شيفرة finally رغم تشغيلها عند رفع اعتراض في كتلة try، إلا أنها لا تتدخل في الاعتراض نفسه، وعليه فإن المكدس سيستمر في تفكيك نفسه بعد تشغيل كتلة finally. كتابة مثل هذه البرامج التي تعمل بكفاءة ويعتمد عليها حتى في حالات ظهور اعتراضات في أماكن غير متوقعة أمر صعب وليس سهلًا. لا يبالي الكثير من الناس بهذا، فقد لا تحدث المشكلة إلا نادرًا جدًا بحيث لا يمكن ملاحظتها، وذلك بسبب عدم ظهور الاعتراضات إلا في الحالات الاستثنائية، وإذا أردنا القول بأن هذا شيء جيد أو سيء جدًا، فيجب النظر أولًا إلى الأثر والتخريب الذي يحدث عند فشل البرنامج أو تعطله. الالتقاط الانتقائي تعالج البيئة الاعتراض الذي يمر في المكدس كله دون التقاطه، ويختلف هنا ما يحدث باختلاف البيئة نفسها، ففي المتصفحات مثلًا، يُكتب وصف الخطأ إلى طرفية جافاسكربت والتي يمكن الوصول إليها من خلال أدوات المتصفح أو قائمة المطورين Developers Menu؛ أما في Node.js فستكون بيئة جافاسكربت الغير موجودة في متصفح والتي سنناقشها في مقال قادم، أكثر حذرًا بشأن تدمير البيانات، فتُخرِج العملية كلها عند حدوث اعتراض غير معالَج unhandled. بالنسبة لأخطاء المبرمجين، فأفضل شيء يمكن فعله هو ترك الخطأ يمر بسلام دون لمسه، كما يمثل الاعتراض الغير معالَج طريقةً معقولةً ومنطقيةً للإشارة إلى وجود عطل في البرنامج، وستعطيك طرفية جافاسكربت في المتصفحات الحديثة بعض المعلومات عن استدعاءات الدالة التي كانت في المكدس حين حدوث المشكلة؛ أما بالنسبة للمشاكل المتوقع حدوثها أثناء الاستخدام العادي، فسيُنبِئ توقف البرنامج بسبب اعتراض غير معالَج استراتيجيةً سيئةً جدًا. كما تتسبب الاستخدامات غير الصحيحة للغة مثل الإشارة المرجعية لرابطة غير موجودة، أو البحث عن خاصية في null، أو استدعاء شيء غير الدوال، في رفع اعتراضات، كما يمكن التقاط تلك الاعتراضات. كل ما نستطيع معرفته عند دخول متن catch هو أن شيئًا ما داخل متن try قد تسبب في رفع اعتراض، لكن لا نستطيع معرفة ماهية الاعتراض نفسه أو ما فعله. لا توفر جافاسكربت -في إغفال صارخ- دعمًا مباشرًا لاعتراضات الالتقاط الانتقائي، فإما تلتقطها كلها أو لا تدرك منها شيئًا، وقد يحلو للمرء افتراض أن الاعتراض الذي حصل عليه هو الذي كان يفكر فيه ويتوقعه حين كتب كتلة catch، بسبب هذا الخطأ في اللغة، لكن سيخبرك الواقع أنه قد لا يحدث هذا دومًا معك، فلعله تم اختراق افتراض آخر، أو لعلك تسببت في زلة أحدثت اعتراض؛ ويحاول المثال التالي استدعاء promptDirection إلى أن يحصل على إجابة صالحة: for (;;) { try { let dir = promtDirection("Where?"); // ← typo! console.log("You chose ", dir); break; } catch (e) { console.log("Not a valid direction. Try again."); } } تُعَدّ بُنية (;;)for طريقةً متعمَّدة لإنشاء حلقة تكرارية لا تنهي نفسها، ولا نخرج منها إلا حين حصولنا على اتجاه صالح، لكننا أخطأنا في تهجئة promptDirection التي أعطتنا الخطأ "متغير غير معرَّف" undefined variable، وتتعامل كتلة catch تعاملًا خاطئًا مع خطأ الرابطة على أنه مؤشر إدخال غير صالح، وذلك بسبب تجاهلها قيمة اعتراضها (e) مفترضةً أنها تعرف المشكلة، كما لا يتسبب هذا في حلقة لا نهائية فحسب، بل يدفن رسالة الخطأ المفيدة التي نريدها عن الرابطة التي أُخطئ في هجائها. تقول القاعدة العامة لا تَلتقط الاعتراضات التقاطًا كليًا إلا إذا كان بغرض توجيهها إلى مكان ما مثل توجيهها عبر الشبكة مثلًا لإخبار نظام آخر بتعطل برنامجنا، وعليك التفكير مليًا حتى حينئذ حول كيفية إخفاء المعلومات، وعلى ذلك فإننا نريد التقاط نوع محدد من الاعتراضات من خلال التحقق داخل كتلة catch مما إذا كان الاعتراض الذي حصلنا عليه هو الذي نريده أم لا، وإن لم يكن فنعيد رفعه، لكن كيف نعرف الاعتراض الذي نريده أصلًا؟ نستطيع موازنة خاصية message التابعة له برسالة الخطأ التي نتوقعها، لكن ليست هذه هي الطريقة المثلى للبرمجة، ففعلنا هذا ما هو إلا استخدام لمعلومات مخصصة لاطلاع العنصر البشري عليها أي الرسالة، وذلك لبناء قرار برمجي على هذه المعلومات، فإذا غير أحد هذه الرسالة أو ترجمها، فستتعطل الشيفرة مرةً أخرى، والحل البديل هو تعريف نوع جديد من الأخطاء واستخدام instanceof لتعريفه: class InputError extends Error {} function promptDirection(question) { let result = prompt(question); if (result.toLowerCase() == "left") return "L"; if (result.toLowerCase() == "right") return "R"; throw new InputError("Invalid direction: " + result); } يوسع صنف الخطأ الجديد Error، حيث لا يعرّف بانيًا خاصًا به، مما يعني أنه يرث باني Error الذي يتوقع رسالة نصية على أساس وسيط، كما لا يعرِّف أي شيء أصلًا، وهو -أي الصنف- فارغ. تتصرف كائنات InputError مثل كائنات Error باستثناء امتلاكها لصنف مختلف يمكننا تمييزها به، وتستطيع الحلقة التكرارية الآن التقاط هؤلاء بطريقة أكثر حذرًا: for (;;) { try { let dir = promptDirection("Where?"); console.log("You chose ", dir); break; } catch (e) { if (e instanceof InputError) { console.log("Not a valid direction. Try again."); } else { throw e; } } } وهذا سيلتقط نُسخًا من InputError فقط ويترك الاعتراضات غير المرتبطة به تمر، فإذا أعدت إدخال خطأ الهجاء مرةً أخرى، فسيبَلَّغ عن خطأ رابطة غير معرَّفة هذه المرة. التوكيدات Assertions التوكيدات هي عمليات تحقق داخل البرنامج، حيث تنظر هل الشيء موجود على الصورة التي يفترض به أن يكون عليها أم لا، وتُستخدَم للبحث عن أخطاء المبرمجين، وليس لمعالجة مواقف يمكن حدوثها في التشغيل العادي، فمثلًا، إذا وُصف firstElement على أساس دالة لا يمكن استدعاؤها على مصفوفة فارغة، فربما نكتبها كما يلي: function firstElement(array) { if (array.length == 0) { throw new Error("firstElement called with []"); } return array[0]; } ستبعثِر الشيفرة السابقة برنامجك بمجرد إساءة استخدامها، بدلًا من إعادة undefined بصمت مثل التي تحصل عليها حين تقرأ خاصية مصفوفة غير موجودة، ويجعل ذلك من الصعب لمثل تلك الأخطاء المرور دون ملاحظتها من أحد، وأسهل في معرفة سببها عند وقوعها. لكن هذا لا يعني أننا ننصح بكتابة توكيدات لكل نوع من المدخلات الخاطئة، فهذا عمل كثير جدًا، كما سينشئ لنا شيفرة غير صافية وملأى بأكواد غير ضرورية، بل احفظ هذه التوكيدات من أجل أخطاء يسهل ارتكابها، أو أخطاء تقع فيها بنفسك. خاتمة المدخلات الخاطئة والأخطاء عمومًا هي أشياء لازمة لنا في الحياة، ومن المهم في البرمجة العثور عليها، وتشخيصها، وإصلاحها، حيث ستظهر هنا في البرمجة على صورة زلات برمجية bugs. وقد تصير المشاكل أسهل في ملاحظتها إذا كان لديك حزمة اختبار مؤتمتة، أو إذا أضفت توكيدات إلى برامجك. مشكلة الأخطاء البرمجية موجودة بكل اللغات ومن المهم معرفتها والتعامل معها. يمكنك الاستعانة بالفيديو الآتي لمعرفة الأخطاء في مجال البرمجة عمومًا: يجب معالجة المشاكل التي تحدث بسبب عوامل خارجة عن تحكم البرنامج بلطف وحكمة، فقد تكون القيم الخاصة المعادة هي الطريقة المثلى لتتبع هذه المشاكل ومعالجتها، وإلا فربما نود استخدام الاعتراضات. سيتسبب رفع الاعتراضات في فك مكدس الاستدعاء حتى يصل إلى كتلة try/catch أو إلى نهاية المكدس، وستُعطَى قيمة الاعتراض إلى كتلة catch التي تلتقطها، مما يؤكد لنا أن هذا هو نوع الاعتراض الذي نريده قبل إجراء أي فعل عليه، كما يمكن استخدام كتل finally للمساعدة في تحديد تدفقات التحكم غير المتوقعة التي تحدث بسبب الاعتراضات، وذلك من أجل التأكد من تشغيل جزء من الشيفرة بعد انتهاء الكتلة. تدريبات Retry لنقل أنه لديك دالة primitiveMultiply التي تضرب عددين معًا في 20 بالمائة من الحالات، وترفع اعتراضًا في الثمانين بالمائة الباقية من نوع MultiplicatorUnitFailure. اكتب دالة تغلف هذه الدالة وتظل تحاول حتى نجاح أحد الاستدعاءات، كما تعيد النتيجة بعد ذلك، وتأكد من معالجة الاعتراضات التي تريد معالجتها فقط. تستطيع تعديل شيفرة التدريب لكتابة الحل وتشغيلها في طرفية المتصفح إن كنت تقرأ من متصفح، أو بنسخها إلى codepen. class MultiplicatorUnitFailure extends Error {} function primitiveMultiply(a, b) { if (Math.random() < 0.2) { return a * b; } else { throw new MultiplicatorUnitFailure("Klunk"); } } function reliableMultiply(a, b) { // شيفرتك هنا. } console.log(reliableMultiply(8, 8)); // → 64 إرشادات للحل يجب حدوث استدعاء primitiveMultiply داخل كتلة try قطعًا، ويجب على كتلة catch الموافقة لهذا رفع اعتراض مرةً أخرى إذا لم تكن نسخةً من MultiplicatorUnitFailure، كما تتأكد من إعادة محاولة الاستدعاء مرةً أخرى حين تكون نسخةً منها. استخدِم حلقةً تكراريةً لتكرار المحاولة، بحيث لا تتوقف إلا عند نجاح الاستدعاء، كما في مثال look الذي ذكرناه آنفًا في هذا المقال، أو استخدِم التعاودية recursion على أمل عدم الحصول على سلسلة نصية من الفشل لفترة طويلة بحيث تؤدي إلى طفحان المكدس، وهذا هو الخيار الآمن بالمناسبة. الصندوق المغلق انظر الكائن التالي: const box = { locked: true, unlock() { this.locked = false; }, lock() { this.locked = true; }, _content: [], get content() { if (this.locked) throw new Error("Locked!"); return this._content; } }; ما هذا إلا صندوق به قفل، وهناك مصفوفة داخل الصندوق، حيث لا تستطيع الوصول إليها إلا حين يُفتح الصندوق، وأنت ممنوع من الوصول إلى خاصية _content الخاصة مباشرةً. اكتب دالة اسمها withBoxUnlocked تأخذ قيمة دالة على أساس وسيط، وتفتح الصندوق، كما تشغِّل الدالة، ثم تتأكد أن الصندوق مقفل مرةً أخرى قبل الإعادة، بغض النظر عن إعادة الدالة الوسيطة بطريقة طبيعية أو رفع اعتراض. تستطيع تعديل شيفرة التدريب لكتابة الحل وتشغيلها في طرفية المتصفح إن كنت تقرأ من متصفح، أو بنسخها إلى codepen. const box = { locked: true, unlock() { this.locked = false; }, lock() { this.locked = true; }, _content: [], get content() { if (this.locked) throw new Error("Locked!"); return this._content; } }; function withBoxUnlocked(body) { // شيفرتك هنا. } withBoxUnlocked(function() { box.content.push("gold piece"); }); try { withBoxUnlocked(function() { throw new Error("Pirates on the horizon! Abort!"); }); } catch (e) { console.log("Error raised: " + e); } console.log(box.locked); // → true للمزيد من النقاط، حين يكون الصندوق مغلقًا، تأكد من بقائه مغلقًا إذا استدعيت withBoxUlocked. إرشادات للحل يستدعي هذا التدريب كتلة finally، ويجب على الدالة فتح الصندوق أولًا، ثم تستدعي الدالة الوسيطة من داخل متن كتلة try، وبعدها تقوم كتلة finally التي تليها بغلق الصندوق. للتأكد من عدم إغلاق الصندوق إذا لم يكن مغلقًا من البداية، تحقق من إغلاقه عند بدء الدالة، ولا تفتحه وتغلقه إلا إذا كان مغلقًا في البداية. ترجمة -بتصرف- للفصل الثامن من كتاب Elequent Javascript لصاحبه Marijn Haverbeke. اقرأ أيضًا المقال السابق: مشروع تطبيقي لبناء رجل آلي (روبوت) عبر جافاسكريبت التعامل مع الأخطاء، جرب... التقط try..catch في جافاسكربت البواني وتهيئة الكائنات Object Initialization في جافا1 نقطة