لوحة المتصدرين
المحتوى الأكثر حصولًا على سمعة جيدة
المحتوى الأعلى تقييمًا في 07/01/24 في كل الموقع
-
السلام عليكم ورحمة الله وبركاته اسعد الله أيامكم بكل خير... أنشأت فورم جمع معلومات المتقدمين.... حين يدخل المتقدم على الفورم يدخل معلوماته المطلوبة ومن يضغط ارسال تنرسل المعلومات الى قاعدة بيانات وايضآ عملت صفحة فيها جدول تعرض فيها جميع البيانات المرسله.... إلى الآن كل شيء شغال الحمدلله..... السؤال هوَ وضعت شروط لكل حقل.... وفيه عندي احد الحقول هو عباره عن select يعني خيارات واريد اضع شرط لكل خيار مثلاً هل المتقدم ذكر او أنثى اذا المتقدم اختار ذكر اريد يظهر عندي في الجدول البيانات رقم 10 واذا اختار أنثى أريده يظهر عندي في الجدول رقم 5.... اتمنى الإجابة بكل توضيح لأن مبتدأ في مجال البرمجة تحياتي4 نقاط
-
3 نقاط
-
هل علينا حفظ جميع الدوال اذا كان الجواب لا اصنعوا ملفا فيه اهم الدوال التي نستخدمها لكي نحفظها انهم جدا كثيرين2 نقاط
-
انا منزل بيانات من موقع Kaggle فا البيانات ده عباره عن ملف في صور رجل وملف تاني في صور نساء عشان ان عاوز اعمل نموذج يعرف انوع الجنس من خلال الصور فا البيانات ده كده عبار عن مُسماة labeled تمام كده بس في سوال كمان اوالان البيانات ده عبار عن صور فا ازي الخورزميا هننعرف علها يعني هي عكس مثل بيانات لمرض القلب يعني فيه featureوكمان output ؟ فا هنا هساخدمها ازي في classification والا ده بيستخدم فقط مع الشبكات العصبيه مع العلم ان لسه هستدرس الشكبات العصبيه انا عاوز افهم الجزاي ده1 نقطة
-
السلام عليكم. لطالما لدي إشكال في دور Token based authentication في حماية تطبيقات الويب أعلم طريقة عملها, يقوم الخادم بالتحقق من بيانات دخول المستخدم, إذا كانت صحيحة يقوم بتوليد Token الذي يتم إرساله بعد كل طلب يقوم به المستخدم بعدها لكن لا أدري ما المشكلة لو أننا اكتفينا فقط بالتحقق الأولي (التحقق من بيانات دخول المستخدم), بعدها ندع المستخدم يرسل الطلبات بدون التحقق منها مادام أنه لم يقم بتسجيل الخروج أرجو توضيح الأمر1 نقطة
-
1 نقطة
-
هذا كود العرض @foreach ($students as $student) <tr> <td>{{$student->name}} </td> <td>{{$student->num_std}}</td> <td>{{$student->adress_std}}</td> <td>{{$student->email_std}}</td> <td>{{$student->phone}}</td> </tr> @endforeach هذا كود كنترول class StudentController extends Controller { /** * Display a listing of the resource. */ public function index() { // $students = student::all(); return view('student.index', compact('students')); } /** * Show the form for creating a new resource. */ public function create() { // }1 نقطة
-
مرحبًا ماجد، بشكل عام في البرمجة ليس عليك أبدًا حفظ الدوال، من المهم التركيز على فهم اللغة أو إطار العمل و آلية عمله و لكن الدوال يتم ذكرها للقيام بتطبيقات عملية و لتعرف أنه توجد دالة للقيام بذلك، بالتالي عندما تحتاج شيء ما ببساطة تقوم بالبحث عنه على غوغل. و عندما تظهر الدالة أمامك ستتذكرها. بالإضافة إلى ذلك فعند القيام بالكثير من التطبيقات العملية فإنك تلقائيًا ستجد أنك حفظت الدالات. أما القيام بوضع كافة الدالات في ملف و حفظهم فهو أمر غير جيد نهائيًا فهو يشجعك على الحفظ أكثر من الفهم و هذا أمر ضار لأي مبرمج. تحياتي.1 نقطة
-
1 نقطة
-
السؤال غير واضح أرجو توضيحه، هل المقصود هو هيكلية ملفات openCart أم طريقة تنظيم الأقسام categories به؟ عامًة تنقسم هيكلية ملفات أوبن كارت إلى قسمين رئيسيين، أولاً . المجلد الرئيسي root directory ويحتوي على ملفات النظام الأساسية والمجلدات الفرعية التي تشكل هيكل المتجر. من أهم الملفات به: index.php: ملف الدخول الرئيسي للمتجر. config.php: ملف الإعدادات الرئيسية للمتجر. .htaccess: ملف إعدادات خادم الويب. ثم مجلد الإدارة admin directory وبه جميع الملفات المتعلقة بلوحة تحكم المتجر، والوصول إليه عن طريق إضافة /admin إلى عنوان URL الرئيسي للمتجر، وأهم الملفات به: index.php: ملف الدخول الرئيسي للوحة التحكم. config.php: ملف الإعدادات الرئيسية للوحة التحكم. داخل كل من تلك المجلدات الرئيسية، ستجد العديد من المجلدات الفرعية التي تحتوي على ملفات محددة لوظائف معينة، مثل: catalog: يحتوي على جميع ملفات واجهة المتجر، مثل القوالب، والوحدات، واللغات. system: يحتوي على ملفات نواة نظام أوبن كارت، مثل مكتبات PHP وإعدادات قواعد البيانات. image: يحتوي على جميع الصور المستخدمة في المتجر، مثل صور المنتجات والشعارات. download: يحتوي على جميع الملفات القابلة للتحميل من قبل الزوار، مثل الكتيبات الإلكترونية. وباستطاعتك تعديل مظهر متجرك عن طريق تعديل ملفات القوالب الموجودة في مجلد catalog/view/theme.1 نقطة
-
السلام عليكم هل اذا قدمت علي شهادة اتمام الدورة ستغلق علي الدورة ام ساستطيع فتحها لأراجع بعض الاشياء ان نسيت لان البرمجة هي مصدر دائم وتتحدث دائما1 نقطة
-
وعليكم السلام ورحمة الله وبركاته، الدورة متاحة لك مدى الحياة حتى بعد حصولك على الشهادة تستطيع بالطبع الرجوع لها فى أى وقت تشاء للمراجعة. وحتى بعد تحديثها تستطيع الإطلاع على الأشياء الجديدة فيها ومتوفر لك الدعم فيها من استفسارات وإجابة على أسئلتك. بالتوفيق...1 نقطة
-
وعليكم السلام ورحمة الله، كلا لن تغلق الدورة. أي دورة اشتريتها سوف تبقى مفتوحة لك إلى الأبد. بل سوف تستفيد أيضا من التحديثات التي سوف تأتي لاحقا في المستقبل حتى بعد إكمالك الدراسة. للمزيد من المعلومات يمكنك مراجعة صفحة الأسئلة الشائعة: https://academy.hsoub.com/pages/faq/1 نقطة
-
يوجد سبعة أنواع للبيانات في JavaScript كما رأينا في فصل أنواع البيانات. ستة من هذه الأنواع تُدعى "أساسية" (primitive) لأنها تحوي قيمة شيء واحد فقط (سواء كان نصًا أو رقمًا أو أي شيء آخر). في المُقابل، تُستخدم الكائنات لتخزين مجموعة من البيانات المتنوعة والوحدات المعقدة المُرَمَّزة بمفاتيح. تُضمَّن الكائنات في ما يقارب جميع نواحي JavaScript، لذا يجب علينا أن نفهمها قبل التعمق في أي شيء آخر. يمكن إنشاء أي كائن باستخدام الأقواس المعقوصة {…} مع قائمة اختيارية بالخاصيات. الخاصية هي زوج من "مفتاح: قيمة" (key: value) إذ يكون المفتاح عبارة عن نص (يُدعى "اسم الخاصية")، والقيمة يمكن أن تكون أي شيء. يمكننا تخيل الكائن كخزانة تحوي ملفات. يُخزن كل جزء من هذه البيانات في الملف الخاص به باستخدام المفتاح. يمكن إيجاد، أو إضافة، أو حذف ملف باستخدام اسمه. يمكن إنشاء كائن فارغ (خزانة فارغة) باستخدام إحدى الصيغتين التاليتين: let user = new Object(); // (object constructor) صياغة باني كائن let user = {}; // (object literal) صياغة مختصرة لكائن عبر الأقواس تُستخدم الأقواس المعقوصة {...} عادة، وهذا النوع من التصريح يُسمى «الصياغة المختصرة لتعريف كائن» (object literal). القيم المُجرَّدة والخاصيات يمكننا إضافة بعض الخاصيات (properties) إلى الكائن المعرَّف بالأقواس {...} مباشرة بشكل أزواج "مفتاح: قيمة": let user = { // كائن name: "John", // name عبر المفتاح John خزِّن القيمة age: 30 // age خزِّن القيمة 30 عبر المفتاح }; لدى كل خاصية مفتاح (يُدعى أيضًا "اسم " أو "مُعَرِّف") قبل النقطتين ":" وقيمة لهذه الخاصية بعد النقطتين. يوجد خاصيتين في الكائن user: اسم الخاصية الأولى هو "name" وقيمتها هي "John". اسم الخاصية الثانية هو "age" وقيمتها هي "30". يمكن تخيل الكائن السابق user كخزانة بملفين مُسَمَّيان "name" و "age". يمكننا إضافة، وحذف، وقراءة الملفات من الخزانة في أي وقت. يمكن الوصول إلى قيم الخاصيات باستخدام الصيغة النُقَطية (dot notation): // الحصول على قيم خاصيات الكائن: alert( user.name ); // John alert( user.age ); // 30 يمكن للقيمة أن تكون من أي نوع، لِنُضِف قيمة من نوع بيانات منطقية (boolean): user.isAdmin = true; يمكننا استخدام المُعامِل delete لحذف خاصية: delete user.age; يمكننا أيضا استخدام خاصيات بأسماء تحوي أكثر من كلمة، لكن يجب وضعها بين علامات الاقتباس "": let user = { name: "John", age: 30, "likes birds": true // يجب أن تكون الخاصية ذات الاسم المُحتوي على أكثر من كلمة بين علامتي اقتباس }; يمكن إضافة فاصلة بعد آخر خاصية في القائمة: let user = { name: "John", age: 30, } وتُسمى بفاصلة "مُعَلِّقَة" أو "زائدة" فهي تجعل إضافة أو حذف أو التنقل بين الخاصيات أسهل لأن جميع الأسطر تُصبِح متشابهة. الأقواس المعقوفة لا تعمل طريقة الوصول إلى الخاصيات ذات الأسماء المحتوية على أكثر من كلمة باستخدام الصيغة النُقَطية: // تعرض هذه التعليمة وجود خطأ في الصياغة user.likes birds = true ذلك لأن الصيغة النُقطية تحتاج لاسم متغير صحيح. لا يحوي مسافات أو حدود أخرى. يوجد بديل يعمل مع أي نص "صيغة الأقواس المعقوفة" []: let user = {}; // تخزين user["likes birds"] = true; // استرجاع alert(user["likes birds"]); // true // حذف delete user["likes birds"]; يعمل كل شيء وفق المطلوب الآن. يُرجى ملاحظة أنَّ النص بداخل الأقواس مُحاط بعلامتي اقتباس (تعمل علامات التنصيص الأخرى بطريقة صحيحة أيضًا). تتيح لنا الأقواس المعقوفة أيضًا جلب اسم خاصية ناتجة عن قيمة أي تعبير - بدلًا من اسم الخاصية الفعلي - مثل استعمال اسم من متغير كما يلي: let key = "likes birds"; // user["likes birds"] = true; يماثل قول user[key] = true; يمكن حساب قيمة المتغير key أثناء التنفيذ أو قد تعتمد قيمته على مدخلات المستخدمين ثم نستخدمه للوصول إلى الخاصية مما يعطي مرونة كبيرة في التعامل. إليك المثال التالي: let user = { name: "John", age: 30 }; let key = prompt("What do you want to know about the user?", "name"); // الوصول باستخدام متغير alert( user[key] ); // John (if enter "name") لا يمكن استخدام الصيغة النُقطية بالطريقة نفسها: let user = { name: "John", age: 30 }; let key = "name"; alert( user.key ) // غير معروف الخاصيات المحسوبة يمكن استخدام الأقواس المعقوفة في كائن معرَّف بالأقواس، وهذا ما يسمى بالخاصيات المحسوبة. إليك المثال التالي: let fruit = prompt("Which fruit to buy?", "apple"); let bag = { [fruit]: 5, // fruit يؤخذ اسم الخاصية من المتغير }; alert( bag.apple ); // fruit="apple" قيمتها 5 إذا كانت معنى الخاصية المحسوبة سهل: تعني [fruit] أنَّ اسم الخاصية يجب أن يُؤخذ من fruit؛ لذا، إن أدخل الزائر "apple"، ستصبح قيمة bag هي {apple: 5}. يعمل الأمر السابق بالطريقة التالية ذاتها: let fruit = prompt("Which fruit to buy?", "apple"); let bag = {}; // fruit خُذ اسم الخاصية من المتغير bag[fruit] = 5; لكن شكله يبدو أفضل، أليس كذلك؟! يمكن استخدام تعابير أكثر تعقيدًا داخل الأقواس المعقوفة: let fruit = 'apple'; let bag = { [fruit + 'Computers']: 5 // bag.appleComputers = 5 }; الأقواس المعقوفة أكثر قوة من الصيغة النُقطية. فهي تسمح باستخدام أي اسم للخاصيات والمتغيرات. لكنها أكثر تعقيدا في الكتابة. لذا، إن كانت أسماء المتغيرات سهلة ومعروفة، تُستخدَم الصيغة النُقَطية غالبًا. وإن أردنا بعض التعقيد، نستخدم إلى الأقواس المعقوفة. يمكن استخدام الأسماء المحجوزة مع أسماء الخاصيات لا يمكن لمتغير أن يحمل اسم إحدى الكلمات المحجوزة في اللغة مثل "for"، أو "let"، أو "return"، …الخ. لكن لا تُطبَّق هذه القاعدة على أسماء خاصيات الكائنات. فيمكن استخدام أي اسم معها: let obj = { for: 1, let: 2, return: 3 }; alert( obj.for + obj.let + obj.return ); // 6 عمومًا، يمكن استخدام أي اسم، لكن هناك استثناء: "__proto__" لهذا الاسم معاملة خاصة لأسباب تاريخية. مثلًا، لا يمكننا استخدام الاسم على أنَّه قيمة لغير كائن: let obj = {}; obj.__proto__ = 5; alert(obj.__proto__); // لا تعمل وفق المطلوب [object Object] كما نرى في الشيفرة، تم تجاهل تخزين القيمة الأولية 5. تخزين أزواج الخاصيات التحكمية في كائن وإتاحة تحديد مفاتيح هذه الأزواج للزائر قد يجعل الشيفرة مصدرًا للأخطاء ومليئًا بالثغرات. في تلك الحالة، قد يختار الزائر - الخبير والماكر - الاسم __proto__ ليكون مفتاحًا ويخرِّب البنية المنطقية للشيفرة (كما في المثال أعلاه). يوجد طريقة لجعل الكائنات تتعامل مع __proto__ بِعدِّها خاصية عادية وسنتطرق لها لاحقًا بعد فهم الكائنات بشكل أعمق. يوجد أيضا هيكل بيانات آخر يدعى Map، والذي ستتعلمه في القسم الذي يتحدث عن نوعي البيانات Map و Set، اللذين يدعمان استعمال أي نوع مع المفاتيح. اختزال قيم الخاصيات نستخدم غالبا قيم متغيرات موجودة مسبقًا لتكون قيمًا لأسماء الخاصيات في الشيفرات الحقيقية. اطلع مثلًا على الشيفرة التالية: function makeUser(name, age) { return { name: name, age: age // ... خاصيات أخرى }; } let user = makeUser("John", 30); alert(user.name); // John تحمل الخاصيات في المثال السابق نفس أسماء المتغيرات. يُعدُّ إنشاء خاصية من متغير موجود مسبقًا حالة استخدام شائعة. أي أنه يوجد اختزال خاص لقيمة الخاصية لاختصار الشيفرة. بدلا من كتابة name:name، يمكننا كتابة name فقط، كما يلي: function makeUser(name, age) { return { name, // name: name يماثل كتابة age // age: age يماثل كتابة // ... }; } يمكننا استخدام كلًا من الخاصيات الاعتيادية والاختزال في الكائن ذاته: let user = { name, // name:name يماثل age: 30 }; فحص الكينونة قابلية الوصول إلى أي خاصية في الكائن هي إحدى مميزات الكائنات، ولكن ألَا يوجد أي خطأ في حال لم تكن الخاصية موجودة؟! عند محاولة الوصول إلى خاصية غير موجودة، تُرجَع القيمة undefined. مما يُعطي طريقة متعارفة لفحص كينونة (وجود) خاصية ما من عدمه بموازنتها مع القيمة "undefined" ببساطة: let user = {}; alert( user.noSuchProperty === undefined ); // تحقق هذه الموازنة يشير إلى عدم وجود الخاصية يوجد أيضا مُعامل خاص "in" لفحص تواجد أي خاصية. طريقة استخدام هذا المعامل كالتالي: "key" in object مثلا: let user = { name: "John", age: 30 }; alert( "age" in user ); // true, user.age موجود alert( "blabla" in user ); // false, user.blabla غير موجود لاحظ أنه من الضروري وجود اسم الخاصية على يسار in. ويكون عادة نصًا بين علامتي تنصيص. إن لم نستخدم علامتي التنصيص فهذا يعني فحص متغير يحمل الاسم ذاته مثل: let user = { age: 30 }; let key = "age"; alert( key in user ); // key إذ تؤخذ قيمة المتغير true تطبع القيمة // user ويُتحقق من وجود خاصية بذلك الاسم في الكائن استخدام "in" مع الخاصيات التي تُخزن القيمة undefined تفحص عملية الموازنة الصارمة "=== undefined" غالبًا وجود الخاصية وفق المطلوب. لكن يوجد حالة خاصة تفشل فيها هذه العملية، بينما لا يفشل المعامل in إن استعمل مكانها. هذه الحالة هي عند وجود الخاصية في الكائن لكنها تُخزن القيمة undefined: let obj = { test: undefined }; alert( obj.test ); // ولكن هل تُعدُّ الخاصية موجودة أم لا؟ undefined يطبع القيمة alert( "test" in obj ); // وتَعدُّ الخاصية موجودة في الكائن true تُطبع القيمة الخاصية obj.test موجودة فعليًا في الشيفرة أعلاه، لذا يعمل المُعامل in بصحة. تحدث مثل هذه الحالات نادرًا فقط لأن القيمة undefined لا تُستخدَم بكثرة. نستخدم غالبا القيمة null للقيم الفارغة أو الغير معرفة، لذلك يُعد المُعامل in قليل الاستخدام في الشيفرات. الحلقة for…in يوجد شكل خاص للحلقة for..in للمرور خلال جميع مفاتيح كائنٍ ما. هذه الحلقة مختلفة تمامًا عما درسناه سابقًا، أي الحلقة for(;;). صياغة الحلقة تكون بالشكل التالي: for (key in object) { // يتنفذ ما بداخل الحلقة لكل مفتاح ضمن خاصيات الكائن } مثلا، لنطبع جميع خاصيات الكائن user: let user = { name: "John", age: 30, isAdmin: true }; for (let key in user) { // المفاتيح alert( key ); // name, age, isAdmin // قيم المفاتيح alert( user[key] ); // John, 30, true } لاحظ أن جميع تراكيب "for" تتيح لنا تعريف متغير التكرار بِداخل الحلقة، مثل let key في المثال السابق. يمكننا أيضًا استخدام اسم متغير آخر بدلًا من key. إليك مثال يُستخدم بكثرة: for (let prop in obj) الترتيب في الكائنات هل الكائنات مرتبة؟ بمعنى آخر، إن تنقلنا في حلقة خلال كائن، هل نحصل على جميع الخاصيات بنفس الترتيب الذي أُضيفت به؟ وهل يمكننا الاعتماد على هذا؟ الإجابة باختصار هي: "مرتب بطريقة خاصة": الخاصيات الرقمية يُعاد ترتيبها، تظهر باقي الخاصيات بترتيب الإنشاء ذاته كما في التفاصيل التالية. لنرَ مثالًا لكائن بِرموز الهاتف: let codes = { "49": "Germany", "41": "Switzerland", "44": "Great Britain", // .., "1": "USA" }; for (let code in codes) { alert(code); // 1, 41, 44, 49 } قد تُستخدم الشيفرة لاقتراح قائمة من الخيارات للمستخدم. إن كنا نبني موقعًا لزوار من ألمانيا فقد نريد أن تظهر 49 أولا. لكن، عند تشغيل الشيفرة، نرى شيئا مختلفًا تماما: تظهر USA (1) أولًا ثم Switzerland (41) وهكذا. تُستخدم رموز الهواتف بترتيب تصاعدي لأنها أعدادٌ، لذا نرى 1, 41, 44, 49. خاصيات عددية؟ ما هذا؟ تعني "الخاصية العددية" (integer property) نصًا يمكن تحويله من وإلى عدد دون أن يتغير. لذا فإن 49 هو اسم خاصية عددي لأنه عند تحويله إلى عدد وإرجاعه لنص يبقى كما هو. لكن "1.2" و "+49" ليست كذلك: // هي دالة تحذف الجزء العشري Math.trunc alert( String(Math.trunc(Number("49"))) ); // "49", الخاصية العددية ذاتها alert( String(Math.trunc(Number("+49"))) ); // "49" مختلفة عن "49+" => إذًا ليست خاصية عددية alert( String(Math.trunc(Number("1.2"))) ); // "1" مختلفة عن "1.2" => إذًا ليست خاصية عددية في المقابل، إن كانت المفاتيح غير عددية، فتُعرَض بالترتيب الذي أُنشِئت به. إليك مثال على ذلك: let user = { name: "John", surname: "Smith" }; user.age = 25; // add one more // تُعرض الخاصيات الغير رقمية بترتيب الإنشاء for (let prop in user) { alert( prop ); // name, surname, age } لذا، لحل مشكلة رموز الهواتف يمكننا التحايل وجعلها غير عددية بإضافة "+" قبل كل رمز كما يلي: let codes = { "+49": "Germany", "+41": "Switzerland", "+44": "Great Britain", // .., "+1": "USA" }; for (let code in codes) { alert( +code ); // 49, 41, 44, 1 } الآن تعمل وفق المطلوب! النسخ بالمرجع إحدى الاختلافات الرئيسية بين الكائنات والمتغيرات الأولية هي أنَّ الكائنات تُخزن وتُنسخ "بالمرجع". بينما تُخزن/تُنسخ القيم الأولية: النصوص، والأرقام، والقيم المنطقية بِعدِّها قيمةً كاملةً. إليك المثال التوضيحي التالي: let message = "Hello!"; let phrase = message; لدينا في هذه الشيفرة متغيرين مستقلين كلاهما يُخزن النص "Hello! ". الكائنات ليست كذلك. لا يُخزِّن المتغير الكائن نفسه، وإنما "عنوانه في الذاكرة". بمعنى آخر، "مرجع للكائن" هنا صورة للكائن: let user = { name: "John" }; كما نرى، يُخزَّن المتغير في مكان ما في الذاكرة ويُخزِّن المتغير user مرجعًا إليه. عند نسخ متغير من نوع كائن، يُنسَخ المرجع الذي يشير إلى ذلك الكائن ولا يُنسَخ الكائن نفسه ويُكرَّر. إذا تخيلنا الكائن كخزانة، فإن المتغير هو مفتاح الخزانة ونسخ المتغير يكرِّر المفتاح وليس الخزانة ذاتها. إليك مثال يوضح ما ذكرناه: let user = { name: "John" }; let admin = user; // يُنسَخ المرجع الآن، أصبح لدينا متغيرين، كلاهما يحمل مرجعًا للكائن ذاته: يمكننا استخدام كلا المتغيرين (المفتاحين) للوصول إلى الخزانة وتعديل محتواها: let user = { name: 'John' }; let admin = user; admin.name = 'Pete'; // "admin" عُدِّلت باستخدام المرجع alert(user.name); // "user" أي يمكن رؤية التعديلات من المرجع 'Pete' تُعرَض القيمة يوضح المثال أعلاه وجود كائن واحد فقط. كما لو كان لدينا خزامة بمفتاحين، واستخدمنا أحدهما (admin) للوصول إلى الخزانة. ثم إذا استخدمنا الآخر (user) لاحقًا، فسنرى إنعكاس التغييرات التي أجراها الأول. الموازنة بالمرجع يعمل مُعاملي المساواة == والمساواة الصارمة === بنفس الطريقة للكائنات. يكون الكائنان متساويان إذا كانَا الكائن نفسه فقط. أي، إذا كان متغيران يشيران للكائن ذاته، فهما متساويان: let a = {}; let b = a; // نفس المرجع alert( a == b ); // true, كلا المتغيرين يشيران للكائن نفسه alert( a === b ); // true وهنا متغيران مستقلان ليسا متساويين أي يشيران إلى كائنين منفصلين حتى وإن كانا متماثلين تمامًا: let a = {}; let b = {}; // متغيران منفصلان alert( a == b ); // خطأ يُحوَّل الكائن إلى قيمة أولية (أساسية) في الموازنات مثل obj1 > obj2 أو obj == 5. سندرس كيفية تحويل الكائنات قريبًا، لكن، في الحقيقة، مثل هذه الموازنات تكون نادرة الضرورة وتنتج غالبًا من خطأ في كتابة الشيفرة. الكائنات الثابتة يمكن تغيير الكائن المُعَرَّف على أنَّه ثابت (أي وسم المتغير الذي يحوي الكائن بالكلمة المفتاحية const) دون حصول أي أخطاء. إليك الشيفرة التالية مثلًا: const user = { name: "John" }; user.age = 25; // (*) alert(user.age); // 25 قد يبدو أن السطر (*) سيسبب خطأ، لكن لا يوجد أي مشكلة البتة. ذلك لأنَّ const تحافظ على قيمة user ذاتها وتمنع تغييرها. ويُحزِّن user المرجع للكائن نفسه دائمًا ولا يتغيِّر بتعديل الكائن نفسه. يدخل السطر (*) إلى الكائن ولا يعيد تعيين قيمة المتغير user. يمكن أن يعطي const خطأ إن حاولنا تغيير قيمة المتغير user مثل: const user = { name: "John" }; // (user خطأ (لا يمكن تغيير قيمة المتغير user = { name: "Pete" }; لكن، ماذا إن أردنا إنشاء خاصيات ثابتة ضمن الكائن؟ سيُعطي user.age = 25 آنذاك خطأ، وذلك ممكن أيضًا. سيتم شرحه في الفصل رايات الخاصيات وواصفاتها. الاستنساخ والدمج إذًا، نسخ كائن يُنشِئ مرجعًا إضافيًّا له. لكن ماذا إن كنا نريد تكرار كائن فعليًّا أي إنشاء نسخة مستقلة منه؟ ذلك ممكن أيضًا، لكنه أصعب قليلًا لعدم وجود دالة تقوم بذلك في JavaScript ذلك لأنَّه لا حاجة لذلك بكثرة. النسخ بالمرجع كافٍ غالبًا. لكن إن أردنا ذلك فعلًا، فإننا نحتاج لإنشاء كائن وتكرار هيكل الكائن الموجود عبر التنقل خلال خاصياته ونسخها على المستوى الأولي. وإليك مثال على ذلك: let user = { name: "John", age: 30 }; let clone = {}; // الكائن الجديد الفارغ // إليه user ننسخ جميع خاصيات المتغير for (let key in user) { clone[key] = user[key]; } // الآن أصبحت النسخة مستقلة تماما clone.name = "Pete"; // تغيير البيانات في النسخة alert( user.name ); // في الكائن الأصلي John تظل يمكننا استخدام الدالة Object.assign للغرض ذاته. الصياغة الدالة هي: Object.assign(dest, [src1, src2, src3...]) تُعد المُعاملات dest، و src1، وحتى srcN كائنات (يمكن أن تكون بالعدد المُراد). تنسخ الدالة خاصيات جميع الكائنات src1, ..., srcN إلى الكائن dest. بمعنى آخر، تُنسخ جميع الخاصيات لجميع المُعاملات بدءًا من المُعامل الثاني إلى المُعامل الأول. ثم يتم إرجاع dest. مثلا، يمكننا استخدام الدالة لدمج عدة كائنات إلى كائن واحد: let user = { name: "John" }; let permissions1 = { canView: true }; let permissions2 = { canEdit: true }; // user إلى permissions2 و permissions1 تنسخ جميع الخاصيات من Object.assign(user, permissions1, permissions2); // now user = { name: "John", canView: true, canEdit: true } إن كان الكائن user يحوي أحد أسماء الخاصيات مسبقًا، فسيتم إعادة كتابة محتواها: let user = { name: "John" }; // isAdmin وإضافة name إعادة كتابة Object.assign(user, { name: "Pete", isAdmin: true }); // now user = { name: "Pete", isAdmin: true } يمكننا أيضًا استخدام الدالة Object.assign بدلًا من الحلقة للاستنساخ البسيط: let user = { name: "John", age: 30 }; let clone = Object.assign({}, user); تنسخ الدالة جميع خاصيات الكائن user إلى الكائن الفارغ وتُرجِعه كما في الحلقة لكن بشكل أقصر. حتى الآن، عدَدْنا جميع خاصيات user أولية (أساسية)، لكن قد تكون بعض الخاصيات مرجعًا لكائن آخر مثل الشيفرة التالية فما العمل؟ let user = { name: "John", sizes: { height: 182, width: 50 } }; alert( user.sizes.height ); // 182 في هذه الحالة نسخ clone.sizes = user.sizes ليس كافيًا لأنَّ user.sizes عبارة عن كائن، فسيُنسَخ على أنَّه مرجعٌ. هكذا، سيصبح لدى clone و user الحجم ذاته: let user = { name: "John", sizes: { height: 182, width: 50 } }; let clone = Object.assign({}, user); alert( user.sizes === clone.sizes ); // صحيح، الكائن ذاته // sizes الكائن الفرعي clone و user يتشارك الكائنان user.sizes.width++; // تغيير خاصية من مكان ما alert(clone.sizes.width); // 51, يعرض النتيجة من مكان آخر لإصلاح هذا، يجب استخدام حلقة الاستنساخ التي تفحص كل قيمة في user[key]، وإن كان كائنًا نستبدل الهيكل الخاص به أيضًا. هذه الطريقة تُسمى "استنساخ عميق" (deep cloning). يوجد خوارزمية عامة للاستنساخ العميق تنفِّذ الحالة السابقة بشكل صحيح، بالإضافة إلى حالات أكثر تعقيدًا. تُدعى هذه الخوارزمية خوارزمية الاستنساخ المُهيكلة. حتى لا نُعيد اختراع العجلة مجدَّدًا، يمكننا استخدام تنفيذ جاهز للحالة من مكتبة JavaScript lodash، تُدعى الدالة _.cloneDeep(obj). الخلاصة الكائنات عبارة عن مصفوفات ترابطية بميزات خاصة عديدة. تُخزن الكائنات خاصيات (أزواج مفتاح-قيمة)، بشرط أنه: يجب أن تكون مفاتيح الخاصيات نصوصًا أو رموزًا (غالبًا نصوص). يمكن أن تكون القيم من أي نوع. يمكننا استخدام ما يلي للوصول إلى خاصية: الصيغة النُقَطِيَّة: obj.property. صيغة الأقواس المعقوفة obj["property"]. تتيح لنا الأقواس المعقوفة أخذ مفتاح من متغير، مثل obj[varWithKey]. عمليات أخرى: لِحذف خاصية: delete obj.prop. لِفحص تواجد خاصية بمفتاح معين: "key" in obj. للتنقل خلال كائن: الحلقة for (let key in obj). تُخَزَّن الكائنات وتُنسخ باستخدام المرجع. بمعنىً آخر، لا يُخزن المتغير قيمة الكائن (object value) لكنه يُخزن مرجعًا (reference) يمثِّل موقع قيمة الكائن في الذاكرة. لذا فإن نسخ هذا المتغير أو تمريره إلى دالة سَينسخ هذا المرجع وليس الكائن ككُل. جميع العمليات (مثل إضافة أو حذف خاصيات) المُنفَّذة على مرجع منسوخ تُنفَّذ على الكائن نفسه. لعمل نسخة حقيقية (الاستنساخ) يمكننا استخدام Object.assign أو _.cloneDeep(obj). يُسمى ما درسناه في هذا الفصل "كائن بسيط" أو كائن فقط. يوجد العديد من أنواع الكائنات الأخرى في JavaScript: الكائن Array (مصفوفة): لتخزين مجموعة البيانات المرتبة، الكائن Date (تاريخ): لتخزين معلومات عن الوقت والتاريخ، الكائن Error (خطأ): لتخزين معلومات عن خطأ ما. وغيرها من أنواع الكائنات. لدى هذه الأنواع ميزاتها الخاصة التي سيتم دراستها لاحقًا. يقول بعض الأشخاص أحيانًا شيئًا مثل "نوع مصفوفة" أو "نوع تاريخ" (الاسم الذي وضعته بين قوسين بجانب نوع الكائن)، لكن هذه الأنواع ليست أنواعًا مستقلة بحد ذاتها، إنما تنتمي إلى نوع البيانات Object (كائن) وتتفرع عنه بأشكال مختلفة. تُعد الكائنات في JavaScript قوية جدًا. درسنا في هذا الفصل جزءًا بسيطًا من موضوع هائل جدًا. سنتعامل مع الكائنات لاحقًا بصورة أقرب وسَنتعلم أكثر عنها في فصول أخرى. تمارين مرحبًا، بالكائن الأهمية: 5 اكتب الشيفرة البرمجية، سطر لكل متطلب: أنشئ كائنًا فارغًا باسم user. أضف الخاصية name بالقيمة John. أضف الخاصية surname بالقيمة Smith. غير قيمة الخاصية name إلى Pete. احذف الخاصية name من الكائن. الحل let user = {}; user.name = "John"; user.surname = "Smith"; user.name = "Pete"; delete user.name; التحقق من الفراغ اكتب الدالة isEmpty(obj) التي تُرجع القيمة true إن كان الكائن فارغًا، وتُرجِع القيمة false في الحالات الأخرى. يجب أن تعمل كالتالي: let schedule = {}; alert( isEmpty(schedule) ); // true schedule["8:30"] = "get up"; alert( isEmpty(schedule) ); // false إليك تجربة حية للمثال. الحل قم بالمرور خلال الكائن ونفذ الأمر return false مباشرة إن عثرت على أي خاصية: function isEmpty(obj) { for (let key in obj) { // إن بدأت الحلقة بالعمل، فهناك خاصية في الكائن return false; } return true; } كائنات ثابتة؟ الأهمية: 5 هل من الممكن تغيير كائن صُرِّح عنه بالكلمة المفتاحية const؟ ما رأيك؟ const user = { name: "John" }; // هل تعمل؟ user.name = "Pete"; الحل بالفعل ستعمل بدون مشاكل. تحمي الكلمة المفتاحية const المتغير نفسه من التغيير فقط. بمعنى آخر، يخزن user مرجعًا للكائن ولا يمكن تغييره مع وجود التصريح عنه بالكلمة المفتاحية const لكن يمكن تغيير محتوى الكائن. const user = { name: "John" }; // تعمل user.name = "Pete"; // خطأ user = 123; جمع خاصيات الكائن لدينا كائن يُخزن رواتب الفريق: let salaries = { John: 100, Ann: 160, Pete: 130 } اكتب الشيفرة التي تجمع الرواتب وتُخزنها في المتغير sum. يجب أن يكون مجموع المثال أعلاه 390. إن كان salaries فارغًا، فإن الناتج سيكون 0. الحل let salaries = { John: 100, Ann: 160, Pete: 130 }; let sum = 0; for (let key in salaries) { sum += salaries[key]; } alert(sum); // 390 ضرب الخاصيات العددية بالقيمة 2 الأهمية: 3 أنشئ دالةً باسم multiplyNumeric(obj) تضرب جميع الخاصيات العددية في الكائن obj في العدد 2. مثلا: // قبل الاستدعاء let menu = { width: 200, height: 300, title: "My menu" }; multiplyNumeric(menu); // بعد الاستدعاء menu = { width: 400, height: 600, title: "My menu" }; لاحظ أنَّ الدالة multiplyNumeric لا يجب أن تُرجِع أي شيء. يجب أن تُعدِّل القيم بداخل الكائن. ملاحظة: استخدام typeof لفحص الأعداد. إليك تجربة حية للتمرين. الحل function multiplyNumeric(obj) { for (let key in obj) { if (typeof obj[key] == 'number') { obj[key] *= 2; } } } ترجمة -وبتصرف- للفصل Objects من كتاب The JavaScript Language .task__importance { color: #999; margin-left: 30px; } .task__answer { border: 3px solid #f7f6ea; margin: 20px 0 14px; position: relative; display: block; padding: 25px 30px; } code { background-color: rgb(250, 250, 250); border-radius: 3px; } اقرأ أيضًا المقال التالي: كنس البيانات المهملة المقال السابق: تعويض نقص دعم المتصفحات لجافاسكربت1 نقطة
-
هذا المقال تمت كتابته بتعاون بين Joe Richardson و Robin Rendle ومجموعة من فريق موقع CSS-Tricks. أراد Joe إضافة مقال بخصوص BEM التي أحببناها وكان لدى كل منا أفكارًا وآراءً حولها، لذلك قررنا أن نتعاون سويًا لكتابة هذا المقال. تُعدُّ منهجية BEM (وهي اختصار لهذه الكلمات معًا Block و Element و Modifier) مصطلح تسمية شائع يستخدم من أجل الأصناف في HTML و CSS تم تطويره بواسطة فريق في Yandex؛ هدفها هو مساعدة المطورين في فهم العلاقة بين HTML و CSS في مشروع معين بشكل أفضل. فيما يلي مثال حول ما يكتبه مطور CSS في نمط BEM: /* مكون كتلة */ .btn {} /* عنصر يعتمد على كتلة */ .btn__price {} /* مُعدِّل يعدِّل تنسيق الكتلة */ .btn--orange {} .btn--big {} في شيفرة CSS السابقة، الكتلة (Block) عبارة عن عنصر جديد في أعلى مستوى من التجريد (top-level abstraction) على سبيل المثال زر: .btn{}. يجب اعتبار الجملة البرمجية هذه بمثابة أحد الأبوين، إذ يمكن وضع العناصر الأبناء (elements) في الداخل ويتم الإشارة إليها بواسطة شرطتين سفليتين يتبع اسم الكتلة أو الحاوية البرمجية تلك مثل .btn__price{}. أخيرًا، يمكن للمُعدلات (modifiers) معالجة الحاوية البرمجية (Block) بحيث يمكننا تمييز أو تصميم ذلك المكون المحدد دون إحداث تغييرات على بقية الحاويات البرمجية الأخرى يتم ذلك عن طريق إلحاق شرطتين باسم الحاوية البرمجية Block تمامًا مثل btn--orange. تبدو الشيفرة كما في المثال التالي: <a class="btn btn--big btn--orange" href="http://css-tricks.com"> <span class="btn__price">$9.99</span> <span class="btn__text">Subscribe</span> </a> لو كتب مطور آخر هذه الشيفرة ولم يكن لدينا خبرة في CSS، فيجب أن نأخذ فكرة جيدة عن أي الأصناف ومسؤوليتها وكيف تعتمد على بعضها بعضًا. يستطيع المطورون آنذاك بناء مكوناتهم الخاصة وتعديل الحاويات (الكتل) الموجودة إلى المحتوى الذي يريدونه. يحتمل بدون كتاب مثل شيفرة CSS هذه أن يتمكن المطورون من كتابة عدة مجموعات مختلفة من الأزرار ببساطة عبر تعيير صنف في الشيفرة: في البداية، قد تبدو هذه الصياغة أبطأ من بناء صنف جديد لكل نوع من أنواع الأزرار، ولكن هذا غير صحيح لعدة أسباب سنذكرها. لماذا علينا أخذ BEM في الحسبان؟ إذا كنا نريد إنشاء تصميم جديد لعنصر معين، يمكننا أولًا النظر في الحاويات البرمجية لمعرفة أي المُعدِّلات، والعناصرالفرعية موجودة بالفعل. ربما ندرك أننا لسنا بحاجة لكتابة أي تعبير CSS برمجي آخر لأن هناك معدّل موجود مسبقًا يفي بالغرض. إذا كنا نقرأ الوسوم بدلاً من تعابير CSS البرمجية، يجب أن نكون قادرين على الحصول بسرعة على فكرة حول العنصر الذي يعتمد على عنصر آخر؛ في المثال السابق، يمكننا رؤية أن العنصر .btn__price يعتمد على العنصر .btn، حتى لو لم نكن على دراية بعمل أي من تلك العناصر. يمكن للمصممين والمطورين تسمية العناصر لتسهيل الاتصال بين فريق المطورين بمعنى آخر، يوفر BEM لكل مطور في المشروع تسمية صيغة إعلانية للعناصر يمكنه مشاركتها بحيث تكون في نفس الصفحة. حدد Harry Roberts فائدة رئيسية أخرى لاستخدام صيغة برمجية مثل BEM حين كتب التالي عن تحسين ثقة المطور: "هذا هو السبب الرئيسي الذي يجعلنا ننتهي من قواعد الشفرة المتضخمة المليئة بشيفرات برمجية قديمة وغير قابلة للتعديل من CSS. نحن نفتقر إلى الثقة في أن نكون قادرين على العمل مع الأنماط الحالية وتعديلها لأننا نخشى عواقب أن تعمل CSS عالميًا، إذ هي مشهورة بحد ذاتها. تتلخص كل المشكلات تقريبًا المتعلقة بـ CSS على نطاق واسع في الثقة (أو عدم وجودها)، لذلك لا يقومون بإجراء تغييرات لأنهم لا يعرفون ما هي الآثار لهذه التغييرات على المدى البعيد." وبالمثل، يناقش Philip Walton حل لهذه المشكلة وهو إلتزام عدد كاف من المطورين بمبادئ BEM: "على الرغم من أن الشيفرة القابلة للتنبؤ بنسبة 100٪ غير متوافرة، من المهم فهم المفاضلات التي تجريها مع الطريقة المتبعة في كتابة التعابير البرمجية التي تختارها. إذا كنت تتبع طريقة BEM الصارمة، فستكون قادرًا على تحديث تعابير CSS وإضافة الشيفرات البرمجية الخاصة بك إليها في المستقبل بثقة تامة بأن هذه التغيرات لن يكون لها أي آثار جانبية." لذلك، إذا كان بإمكان المطورين العمل في مشروع بطريقة أكثر ثقة، فإنهم على يقين من اتخاذ قرارات أكثر ذكاءً حول كيفية استخدام هذه العناصر المرئية. قد لا تكون هذه الطريقة حلًا مثاليًا لجميع هذه المشاكل، لكنها بالتأكيد تمنح المطورين معيارًا لكتابة جمل برمجية أفضل وأكثر قابلية للصيانة في المستقبل. هناك جانب آخر جيد في منهجية BEM، وهو أن كل مجموعة جمل برمجية خاصة بعنصر معين توجد داخل حاوية خاصة به ولا يوجد شيء متداخل مما يجعل خصوصية CSS سلسة جدًا ومنخفضة وهذا هو المطلوب. أي أنك لن تجهد نفسك فيما يتعلق بخصوصية تعابير CSS البرمجية. دعونا نلقي نظرة على بعض المشاكل مع BEM. مشاكل مع BEM CSS لن يقوم أحد بمعاقبتك بالطبع إذا خرجت عن قواعد BEM. لا يزال بإمكانك كتابة محدد CSS كالتالي: .nav .nav__listItem .btn--orange { background-color: green; } يبدو أن المثال السابق يحتوي أجزاء من منهجية BEM، لكنه ليس كذلك فهو يحتوي على محددات متداخلة، ولا يصف المُعدِّل (modifier) بدقة ما يحصل في هذه الجمل البرمجية. إذا فعلنا ذلك، فسنكون فشلنا في تحقيق مبدأ الخصوصية وهذا مفيد جدا لمنهجية BEM. يجب ألا تَبطل كتلةٌ (مثل .nav) عمل كتلة أخرى أو معدل آخر (مثل .btn --orange) وإلا فإن هذا سيجعل من المستحيل تقريبًا قراءة ملف HTML وفهم عمل هذا العنصر. في هذه العملية، نحن ملزمون الى حد كبير أن نكون أهلًا لثقة مطور آخر في البرنامج، هذا ينطبق على HTML أيضًا. ماذا تتوقع إذا رأيت الشيفرة التالية؟ <a class="btn" href="http://css-tricks.com"> <div class="nav__listItem">Item one</div> <div class="nav__listItem">Item two</div> </a> ربما ما يحدث في المثال السابق، هو أن عنصرًا في الحاوية البرمجية block يشتمل على الشيفرة التي يحتاجها المطور، لكن العناصر الأبناء لا تحتاج إلى الصنف .nav على أنه عنصر أب. هذه المشكلة تجعل البرنامج استثنائيًّا وغير متسق ومتناقض مع نفسه وينبغي تجنبها بأي ثمن. لذلك، يمكننا تلخيص هذه المشكلات في: عدم استخدام المُعدلات في الحاويات البرمجية block غير المترابطة. تجنب صنع عناصر رئيسية غير ضرورية عندما يكون العنصر الفرعي موجودًا بشكل جيد بدون أي مشاكل. المزيد من الأمثلة العملية على BEM قائمة قابلة للطي: في هذا المثال، هناك حاوية برمجية block واحدة وعنصران ومُعدِّل واحد. يمكننا هنا إنشاء مُعدِّل مثل .accordion__copy-open يتيح لنا معرفة أنه يجب ألا نستخدمه في حاوية برمجية block أو عنصر آخر. قائمة تنقل: يحتوي هذا المثال على كتلة block واحدة و 6 عناصر ومُعدِّل واحد. من الجيد تمامًا إنشاء حاويات برمجية blocks بدون معدلات على الإطلاق. يمكن للمطور في مرحلة ما في المستقبل أن يربط هذه الحاوية بمعدلات جديدة طالما بقيت ثابتة. عيوب BEM ربما لا تحب استخدام شرطة مزدوجة. حسنًا، استخدم شيئًا آخر فريدًا تستخدمه باستمرار. هنا رأي آخر: هذه الثلاثة محددات الأخيرة جميعها لها مستويات خصوصية مختلفة. تحتمل أن يكون لها عنصر رئيسي أو لا بدون أي قواعد معمول بها، هل من الممكن أن يكون هذا المثال الصغير جيد بالنسبة لك؟ ربما. ولكن كلما زاد عدد تعابير CSS البرمجية في المشروع، زاد عدد الأشياء الصغيرة مثل هذه، وبالتالي زادت مشاكل الخصوصية والتعقيد. ليس بالضرورة اختيار رأي صموئيل هنا، ولكن آراءه شاركها الكثير من الناس لذلك فهو مثال جيد. بخصوص من يرفضون BEM تمامًا فلا بأس بذلك، لكنني أعتقد أنه سيكون من الصعب القول بأن وجود مجموعة من القواعد التي تساعد في الفهم والحفاظ على CSS قابل للتعديل هو فكرة سيئة. في منهجية SMACSS، من المحتمل أن تجد اسم صنف CSS متكون من ثلاثة أحرف. تتبع المُعدِّلات بعدئذٍ اسم الوحدة النمطية باستخدام شرطة: /* مثال عن وحدة */ .btn { } /* btn معدِّل الصنف */ .btn-primary { } /* مع حالة Btn الوحدة */ .btn.is-collapsed { } هذه مجرد طريقة تسمية مختلفة لنفس المشكلة. إنه مشابه إلى حد ما، لكن تكون أكثر تحديدًا بشأن التبعيات والحفاظ على خصوصية التفاصيل. في OOCSS، الحاويات البرمجية هي عامة بالغالب. /* مثال عن وحدة */ .mod { } /* جزء من الوحدة */ .inner { } /* Talk الوحدة */ .talk { } /* تغيير جزء داخل الوحدة */ .talk .inner { } لذلك، يمكنك استخدام أصناف متعددة في HTML للحالات المختلفة. لم يتم تسمية الجزء الداخلي مثل التابع له، لذلك فهو أقل وضوحًا ولكنه قابل لإعادة استخدامه. ستقوم BEM بعمل .mod__inner و .mod--talk و mod-talk__inner.. هذه مجرد اختلافات في المنهجية. تذكر أن لا أحد يجبرك على استخدامها، فهذه قواعد مفروضة ذاتيًا حيث تأتي القيمة من متابعتها. BEM و Sass لأولئك الذين يستخدمون Sass ويستمتعون بتشعيب (nesting) العناصر كوسيلة لتحديد النطاقات لتنسيقات، لازال بإمكانك أن تكون المسؤول عن الصيغة المتشعبة ولكن يمكنك الحصول على CSS غير متشعب، باستخدام @at-root: ملف Scss: .block { @at-root #{&}__element { } @at-root #{&}--modifier { } } يولد ملف CSS التالي: .block { } .block__element { } .block--modifier { } ويمكنك الحصول على ملخص كما تريد! تحقق من منشئ BEM الذي يخص Danield Guillan أو Anders Schmidt Hansen من أجل BEM التعبيرية. الخلاصة أعتقد أنه من الإنصاف القول إنه على الرغم من أن BEM لن يحل جميع مشاكلنا، إلا أنه مفيد بشكل كبير في بناء واجهات قابلة للتطوير والصيانة حيث يجب أن يكون لدى كل فرد في الفريق فكرة واضحة عن كيفية تطوير تلك الأشياء. ذلك لأن الكثير من التطوير في الواجهة الأمامية لا يتعلق فقط بالخدع اللطيفة التي تحل مشكلة صغيرة واحدة على المدى القصير؛ نحتاج إلى اتفاقات وعقود ملزمة بين المطورين بحيث يمكن لبرنامجنا التكيف مع مرور الوقت وعلى المدى البعيد. بشكل عام، أود أن أفكر في BEM كإجابة على سؤال نيكولاس غالاغر: ترجمة -وبتصرف- للمقال BEM 101 لصاحبها Robin Rendle.1 نقطة