في البرمجة كائنية التوجه، الفئة عبارة عن قالب كود برنامج قابل للتوسيع لإنشاء كائنات، وتوفير القيم الأولية للحالة (متغيرات الأعضاء) وتنفيذ السلوك (وظائف الأعضاء أو أساليبهم).
من الناحية العملية، غالبًا ما نحتاج إلى إنشاء العديد من الكائنات من نفس النوع، مثل المستخدمين أو البضائع أو أي شيء آخر.
كما نعلم بالفعل من الفصل المُنشئ، يمكن new function
للمشغل أن تساعد في ذلك.
ولكن في JavaScript الحديثة، هناك بنية "فئة" أكثر تقدمًا، والتي تقدم ميزات جديدة رائعة مفيدة للبرمجة الموجهة للكائنات.
بناء الجملة الأساسي هو:
فئة ماي كلاس { // طرق الفصل منشئ () { ... } الطريقة 1 () { ... } الطريقة 2 () { ... } الطريقة 3 () { ... } ... }
ثم استخدم new MyClass()
لإنشاء كائن جديد بجميع الطرق المذكورة.
يتم استدعاء الأسلوب constructor()
تلقائيًا بواسطة new
، حتى نتمكن من تهيئة الكائن هناك.
على سبيل المثال:
فئة المستخدم { منشئ (الاسم) { this.name = name; } قل مرحبا () { تنبيه(هذا.اسم); } } // الاستخدام: اسمح للمستخدم = مستخدم جديد("جون"); user.sayHi();
عندما يتم استدعاء new User("John")
:
يتم إنشاء كائن جديد.
يعمل constructor
مع الوسيطة المحددة ويعينها إلى this.name
.
…ثم يمكننا استدعاء أساليب الكائن، مثل user.sayHi()
.
لا توجد فاصلة بين أساليب الطبقة
من الأخطاء الشائعة للمطورين المبتدئين وضع فاصلة بين أساليب الفصل، مما قد يؤدي إلى خطأ في بناء الجملة.
لا ينبغي الخلط بين التدوين هنا وبين الكائنات الحرفية. داخل الفصل، لا توجد فواصل مطلوبة.
إذًا، ما هو class
بالضبط؟ وهذا ليس كيانًا جديدًا تمامًا على مستوى اللغة، كما قد يظن المرء.
دعونا نكشف النقاب عن أي سحر ونرى ما هو الفصل حقًا. سيساعد ذلك في فهم العديد من الجوانب المعقدة.
في JavaScript، الفصل هو نوع من الوظائف.
هنا، ألق نظرة:
فئة المستخدم { مُنشئ (اسم) { this.name = name; } sayHi() { تنبيه(this.name); } } // إثبات: المستخدم هو وظيفة تنبيه (نوع المستخدم)؛ // وظيفة
ما يفعله إنشاء class User {...}
حقًا هو:
ينشئ دالة باسم User
، والتي تصبح نتيجة إعلان الفئة. رمز الوظيفة مأخوذ من طريقة constructor
(يُفترض أنه فارغ إذا لم نكتب مثل هذه الطريقة).
يخزن توابع الصنف، مثل sayHi
، في User.prototype
.
بعد إنشاء كائن new User
، عندما نستدعي أسلوبه، فإنه مأخوذ من النموذج الأولي، تمامًا كما هو موضح في الفصل F.prototype. لذا فإن الكائن لديه حق الوصول إلى أساليب الفصل.
يمكننا توضيح نتيجة إعلان class User
على النحو التالي:
إليك الكود لتحليله:
فئة المستخدم { مُنشئ (اسم) { this.name = name; } sayHi() { تنبيه(this.name); } } // الفئة هي وظيفة تنبيه (نوع المستخدم)؛ // وظيفة // ... أو بشكل أكثر دقة، طريقة البناء تنبيه (المستخدم === User.prototype.constructor); // حقيقي // الطرق موجودة في User.prototype، على سبيل المثال: تنبيه (User.prototype.sayHi)؛ // كود طريقة sayHi // هناك طريقتان بالضبط في النموذج الأولي تنبيه(Object.getOwnPropertyNames(User.prototype)); // منشئ، قل مرحبا
في بعض الأحيان يقول الناس أن class
هو "سكر نحوي" (بناء الجملة مصمم لتسهيل قراءة الأشياء، لكنه لا يقدم أي شيء جديد)، لأنه يمكننا في الواقع الإعلان عن نفس الشيء دون استخدام الكلمة الأساسية class
على الإطلاق:
// إعادة كتابة مستخدم الفصل في وظائف خالصة // 1. إنشاء وظيفة المنشئ وظيفة المستخدم (الاسم) { this.name = name; } // يحتوي النموذج الأولي للدالة على خاصية "المنشئ" افتراضيًا، // لذلك لا نحتاج إلى إنشائه // 2. أضف الطريقة إلى النموذج الأولي User.prototype.sayHi = function() { تنبيه(هذا.اسم); }; // الاستخدام: اسمح للمستخدم = مستخدم جديد("جون"); user.sayHi();
ونتيجة هذا التعريف هي نفسها تقريبا. لذلك، هناك بالفعل أسباب تجعل من الممكن اعتبار class
سكرًا نحويًا لتعريف المُنشئ مع طرق النموذج الأولي الخاصة به.
ومع ذلك، لا تزال هناك اختلافات مهمة.
أولاً، يتم تسمية الوظيفة التي تم إنشاؤها بواسطة class
بواسطة خاصية داخلية خاصة [[IsClassConstructor]]: true
. لذلك لا يختلف الأمر تمامًا عن إنشائه يدويًا.
تتحقق اللغة من تلك الخاصية في مجموعة متنوعة من الأماكن. على سبيل المثال، على عكس الدالة العادية، يجب استدعاؤها باستخدام new
:
فئة المستخدم { منشئ () {} } تنبيه (نوع المستخدم)؛ // وظيفة مستخدم()؛ // خطأ: لا يمكن استدعاء مستخدم مُنشئ الفئة بدون "جديد"
أيضًا، يبدأ تمثيل السلسلة لمنشئ الفئة في معظم محركات JavaScript بـ "class..."
فئة المستخدم { منشئ () {} } تنبيه (المستخدم)؛ // فئة المستخدم {...}
وهناك اختلافات أخرى سنراها قريبا.
أساليب الطبقة غير قابلة للتعداد. يقوم تعريف الفئة بتعيين علامة enumerable
على false
لجميع الأساليب في "prototype"
.
هذا أمر جيد، لأننا إذا أردنا for..in
مع كائن ما، فإننا عادةً لا نريد أساليب فئته.
use strict
. كل التعليمات البرمجية الموجودة داخل بنية الفصل تكون تلقائيًا في الوضع الصارم.
بالإضافة إلى ذلك، يوفر بناء جملة class
العديد من الميزات الأخرى التي سنستكشفها لاحقًا.
تمامًا مثل الوظائف، يمكن تعريف الفئات داخل تعبير آخر، أو تمريرها، أو إرجاعها، أو تعيينها، وما إلى ذلك.
فيما يلي مثال على تعبير الفصل:
دع المستخدم = الفئة { قل مرحبا () { تنبيه("مرحبا"); } };
على غرار تعبيرات الدالة المسماة، قد يكون لتعبيرات الفئة اسم.
إذا كان لتعبير الفصل اسم، فسيكون مرئيًا داخل الفصل فقط:
// "تعبير الفئة المسماة" // (لا يوجد مثل هذا المصطلح في المواصفات، ولكنه مشابه لتعبير الوظيفة المسماة) دع المستخدم = فئة MyClass { قل مرحبا () { تنبيه(MyClass); // اسم MyClass مرئي فقط داخل الفصل } }; مستخدم جديد().sayHi(); // يعمل، ويظهر تعريف MyClass تنبيه(MyClass); // خطأ، اسم MyClass غير مرئي خارج الفصل
يمكننا أيضًا إنشاء الفصول الدراسية ديناميكيًا "حسب الطلب"، مثل هذا:
وظيفة makeClass(عبارة) { // قم بإعلان الفصل وإعادته فئة العودة { قل مرحبا () { تنبيه(عبارة); } }; } // إنشاء فئة جديدة Let User = makeClass("Hello"); مستخدم جديد().sayHi(); // مرحبًا
تمامًا مثل الكائنات الحرفية، قد تتضمن الفئات الحروف/المحددات والخصائص المحسوبة وما إلى ذلك.
فيما يلي مثال على user.name
الذي تم تنفيذه باستخدام get/set
:
فئة المستخدم { منشئ (الاسم) { // يستدعي الواضع this.name = name; } الحصول على الاسم () { إرجاع هذا._name; } تعيين الاسم (القيمة) { إذا (القيمة.الطول <4) { تنبيه("الاسم قصير جدًا."); يعود؛ } this._name = value; } } اسمح للمستخدم = مستخدم جديد("جون"); تنبيه (اسم المستخدم) ؛ // جون المستخدم = مستخدم جديد(""); // الاسم قصير جدًا.
من الناحية الفنية، يعمل إعلان الفئة هذا عن طريق إنشاء حروف ومحددات في User.prototype
.
فيما يلي مثال لاسم الطريقة المحسوبة باستخدام الأقواس [...]
:
فئة المستخدم { ["قل" + "مرحبًا"]() { تنبيه("مرحبا"); } } مستخدم جديد().sayHi();
من السهل تذكر هذه الميزات، لأنها تشبه تلك الموجودة في الكائنات الحرفية.
قد تحتاج المتصفحات القديمة إلى ملف polyfill
تعد حقول الفصل إضافة حديثة إلى اللغة.
في السابق، كانت فصولنا تحتوي على طرق فقط.
"حقول الفئة" هي صيغة تسمح بإضافة أي خصائص.
على سبيل المثال، لنضيف خاصية name
إلى class User
:
فئة المستخدم { اسم = "جون"؛ قل مرحبا () { تنبيه("مرحبا، ${this.name}!`); } } مستخدم جديد().sayHi(); // مرحبا جون!
لذلك نكتب فقط "
الفرق المهم بين حقول الفئة هو أنها تم تعيينها على كائنات فردية، وليس على User.prototype
:
فئة المستخدم { اسم = "جون"؛ } السماح للمستخدم = مستخدم جديد ()؛ تنبيه (اسم المستخدم) ؛ // جون تنبيه (User.prototype.name)؛ // غير محدد
يمكننا أيضًا تعيين قيم باستخدام تعبيرات أكثر تعقيدًا واستدعاءات دوال:
فئة المستخدم { name = موجه("الاسم من فضلك؟", "جون"); } السماح للمستخدم = مستخدم جديد ()؛ تنبيه (اسم المستخدم) ؛ // جون
كما هو موضح في الفصل، تتمتع وظائف ربط الوظائف في JavaScript this
. ذلك يعتمد على سياق المكالمة.
لذا، إذا تم تمرير أسلوب كائن واستدعائه في سياق آخر، فلن يكون this
مرجعًا لكائنه بعد الآن.
على سبيل المثال، سيظهر هذا الرمز undefined
:
زر الفئة { منشئ (القيمة) { this.value = value; } انقر () { تنبيه (هذه القيمة)؛ } } زر السماح = زر جديد("مرحبا"); setTimeout(button.click, 1000); // غير محدد
المشكلة تسمى "خسارة this
".
هناك طريقتان لإصلاحه، كما تمت مناقشته في فصل ربط الوظيفة:
قم بتمرير دالة مجمعة، مثل setTimeout(() => button.click(), 1000)
.
قم بربط الطريقة بالكائن، على سبيل المثال في المنشئ.
توفر حقول الفئة صيغة أخرى أنيقة للغاية:
زر الفئة { منشئ (القيمة) { this.value = value; } انقر = () => { تنبيه (هذه القيمة)؛ } } زر السماح = زر جديد("مرحبا"); setTimeout(button.click, 1000); // مرحبًا
يتم إنشاء حقل الفئة click = () => {...}
على أساس كل كائن، وهناك وظيفة منفصلة لكل كائن Button
، مع وجود this
بداخله يشير إلى هذا الكائن. يمكننا تمرير button.click
في أي مكان، وستكون this
صحيحة دائمًا.
وهذا مفيد بشكل خاص في بيئة المتصفح لمستمعي الأحداث.
يبدو بناء جملة الفئة الأساسية كما يلي:
فئة ماي كلاس { الدعامة = القيمة؛ // ملكية منشئ (...) {// منشئ // ... } الطريقة (...) {} // الطريقة احصل على شيء ما (...) {} // طريقة getter اضبط شيئًا (...) {} // طريقة الضبط [Symbol.iterator]() {} // طريقة ذات اسم محسوب (الرمز هنا) // ... }
MyClass
هي دالة من الناحية الفنية (التي نقدمها constructor
)، بينما تتم كتابة الأساليب والحروف والمحددات في MyClass.prototype
.
في الفصول القادمة سوف نتعلم المزيد عن الطبقات، بما في ذلك الميراث وغيرها من الميزات.
الأهمية: 5
تمت كتابة فئة Clock
(انظر وضع الحماية) بأسلوب وظيفي. أعد كتابتها في بناء جملة "الفئة".
ملاحظة: تدق الساعة في وحدة التحكم، افتحها لترى.
افتح صندوق الحماية للمهمة.
ساعة الصف { منشئ ({قالب }) { this.template = template; } يجعل() { اسمحوا التاريخ = تاريخ جديد ()؛ دع الساعات = date.getHours(); إذا (ساعات < 10) ساعات = '0' + ساعات؛ Let mins = date.getMinutes(); إذا (دقيقة < 10) دقيقة = '0' + دقيقة؛ Let secs = date.getSeconds(); إذا (ثانية < 10) ثانية = '0' + ثانية؛ دع الإخراج = this.template .استبدال ('h'، ساعات) .استبدال ('م'، دقيقة) .replace('s', sec); console.log(output); } قف() { ClearInterval(this.timer); } يبدأ() { this.render(); this.timer = setInterval(() => this.render(), 1000); } } Let Clock = new Clock({template: 'h:m:s'}); Clock.start();
افتح الحل في رمل.