◐ Shell
reader mode source ↗
AR
نريد أن نتيح هذا المشروع المفتوح المصدر إلى كل الناس حول العالم. من فضلك ساعدنا على ترجمة محتوى هذه السلسله للغة التى تعرفها.
بحث
بحث
١٥ ديسمبر ٢٠٢١

الصيغة الأساسية للClass

في البرمجة الموجهة للكائنات ، * الفئة * عبارة عن قالب رمز برنامج قابل للتوسيع لإنشاء الكائنات ، ويوفر القيم الأولية للحالة (متغيرات الأعضاء) وتنفيذ السلوك (وظائف الأعضاء أو الأساليب).

Wikipedia

من الناحية العملية ، نحتاج غالبًا إلى إنشاء العديد من العناصر من نفس النوع ، مثل المستخدمين أو السلع أو أيا كان.

كما نعلم بالفعل من الفصل <info: buildor-new> ، يمكن أن تساعد الوظيفة الجديدة في ذلك.

ولكن في جافا سكريبت الحديثة ، هناك بنية “صنف” أكثر تقدمًا ، تقدم ميزات جديدة رائعة مفيدة للبرمجة الموجهة للكائنات.

الصيغة الأساسية للClass

الصيغة الأساسية هي:

class MyClass {
  // class methods
  constructor() { ... }
  method1() { ... }
  method2() { ... }
  method3() { ... }
  ...
}

ثم استخدم New MyClass () لإنشاء كائن جديد بكل الطرق المدرجة.

يتم استدعاء طريقة constructor () تلقائيًا بواسطة new ، حتى نتمكن من تهيئة الكائن هناك.

فمثلا:

class User {
  constructor(name) {
    this.name = name;
  }

  sayHi() {
    alert(this.name);
  }
}

// Usage:
let user = new User('John');
user.sayHi();

When new User("John") is called:

  1. A new object is created.
  2. The constructor runs with the given argument and assigns it to this.name.

… ثم يمكننا استدعاء طرق الكائن ، مثل user.sayHi ().

لا فاصلة بين طرق الفصل

من المشاكل الشائعة للمطورين المبتدئين وضع فاصلة بين طرق الفصل ، مما يؤدي إلى خطأ في بناء الجملة.

لا يجب الخلط بين التدوين هنا وحرف الشيء. داخل الفصل الدراسي ، لا يلزم وجود فواصل.

ما هي الclass ؟

إذن ، ما هي “الطبقة” بالضبط؟ هذا ليس كيانًا جديدًا تمامًا على مستوى اللغة ، كما قد يعتقد المرء.

دعونا نكشف عن أي سحر ونرى ما هو الصف حقا. سيساعد ذلك في فهم العديد من الجوانب المعقدة.

في JavaScript ، الفئة هي نوع من الوظائف.

هنا ، ألق نظرة:

class User {
  constructor(name) { this.name = name; }
  sayHi() { alert(this.name); }
}

// proof: User is a function
alert(typeof User); // function

ما يفعله بناء مستخدم الفئة {... } حقًا هو:

  1. إنشاء وظيفة باسم “المستخدم” ، والتي تصبح نتيجة لإعلان الفئة. يتم أخذ رمز الوظيفة من طريقة المنشئ (يفترض أنها فارغة إذا لم نكتب مثل هذه الطريقة).
  2. يخزن طرق الفصل ، مثل sayHi ، فيUser.prototype.

بعد إنشاء كائن “مستخدم جديد” ، عندما نسمي أسلوبه ، يتم أخذه من النموذج الأولي ، تمامًا كما هو موضح في الفصل <info: function-prototype>. لذا فإن الكائن لديه حق الوصول إلى أساليب الفصل.

يمكننا توضيح نتيجة إعلان “مستخدم الفئة” كما يلي:

إليك كود لتتفكر فيه:

class User {
  constructor(name) {
    this.name = name;
  }
  sayHi() {
    alert(this.name);
  }
}

// class is a function
alert(typeof User); // function

// ...or, more precisely, the constructor method
alert(User === User.prototype.constructor); // true

// The methods are in User.prototype, e.g:
alert(User.prototype.sayHi); // alert(this.name);

// there are exactly two methods in the prototype
alert(Object.getOwnPropertyNames(User.prototype)); // constructor, sayHi

ليس مجرد سكر نحوي

أحيانًا يقول الناس أن “class” عبارة عن “سكر نحوي” (بنية تم تصميمها لتسهيل قراءة الأشياء ، ولكن لا تقدم أي شيء جديد) ، لأنه يمكننا في الواقع أن نعلن الشيء نفسه بدون كلمة “class” على الإطلاق:

// rewriting class User in pure functions

// 1. Create constructor function
function User(name) {
  this.name = name;
}
// a function prototype has "constructor" property by default,
// so we don't need to create it

// 2. Add the method to prototype
User.prototype.sayHi = function () {
  alert(this.name);
};

// Usage:
let user = new User('John');
user.sayHi();

نتيجة هذا التعريف هي نفسها. لذا ، هناك بالفعل أسباب تجعل من الممكن اعتبار “class” سكرًا نحويًا لتحديد المنشئ مع طرق النموذج الأولي.

لا تزال هناك اختلافات مهمة.

  1. أولاً ، يتم تصنيف دالة تم إنشاؤها بواسطة “class” بواسطة خاصية داخلية خاصة [[FunctionKind]]:" classConstructor ". لذلك فهي ليست تمامًا مثل إنشائها يدويًا.

تقوم اللغة بالتحقق من هذه الخاصية في أماكن متنوعة. على سبيل المثال ، على عكس الوظيفة العادية ، يجب أن يتم استدعاؤها بـ new:

```js run
class User {
  constructor() {}
}

alert(typeof User); // function
User(); // Error: Class constructor User cannot be invoked without 'new'
```

أيضًا ، يبدأ تمثيل السلسلة لمنشئ فئة في معظم محركات JavaScript بـ “class …”

```js run
class User {
  constructor() {}
}

alert(User); // class User { ... }
```

هناك اختلافات أخرى ، سنراها قريبًا.

  1. دوال الclass لا تعد ولا تحصى.      يقوم تعريف الفئة بتعيين علامة enumerable إلىfalse لجميع الطرق في “” prototype "`.

هذا أمر جيد ، لأنه إذا كنا 'for…in` فوق كائن ما ، فإننا عادة لا نريد طرقه الطبقية.

  1. الclasses دائمًا “استخدام صارم”.      تكون جميع التعليمات البرمجية داخل بنية الفصل تلقائيًا في وضع صارم.

بالإضافة إلى ذلك ، فإن بناء جملة class يجلب العديد من الميزات الأخرى التي سنستكشفها لاحقًا.

تعبير الclass

تمامًا مثل functions ، يمكن تعريف الفئات داخل تعبير آخر ، وتمريرها ، وإعادتها ، وتعيينها ، وما إلى ذلك.

إليك مثالاً على تعبير class:

let User = class {
  sayHi() {
    alert('Hello');
  }
};

على غرار تعبيرات الوظائف المسماة ، قد يكون لتعبيرات الفئة اسم.

إذا كان تعبير فئة له اسم ، فإنه يكون مرئيًا داخل الفصل فقط:

// "Named Class Expression"
// (no such term in the spec, but that's similar to Named Function Expression)
let User = class MyClass {
  sayHi() {
    alert(MyClass); // MyClass name is visible only inside the class
  }
};

new User().sayHi(); // works, shows MyClass definition

alert(MyClass); // error, MyClass name isn't visible outside of the class

يمكننا أيضًا أن نجعل classes ديناميكيًا “حسب الطلب” ، مثل هذا:

function makeClass(phrase) {
  // declare a class and return it
  return class {
    sayHi() {
      alert(phrase);
    }
  };
}

// Create a new class
let User = makeClass('Hello');

new User().sayHi(); // Hello

Getters/setters

تمامًا مثل الأشياء الحرفية ، قد تتضمن الفئات الحروف / المستوطنين والخصائص المحسوبة وما إلى ذلك.

في ما يلي مثال لـ user.name تم تنفيذه باستخدامget / set:

class User {

  constructor(name) {
    // invokes the setter
    this.name = name;
  }

  get name() {
    return this._name;
  }

  set name(value) {
    if (value.length < 4) {
      alert("Name is too short.");
      return;
    }
    this._name = value;
  }

}

let user = new User("John");
alert(user.name); // John

user = new User(""); // Name is too short.

من الناحية الفنية ، يعمل إعلان الفئة هذا عن طريق إنشاء الحروف والمستوطنين في User.prototype.

الأسماء المحسوبة […]

في ما يلي مثال باسم الطريقة المحسوبة باستخدام الأقواس [...]:

class User {

  ['say' + 'Hi']() {
    alert("Hello");
  }

}

new User().sayHi();

من السهل تذكر هذه الميزات ، لأنها تشبه تلك الموجودة في الأشياء الحرفية.

حقول class

قد تحتاج المتصفحات القديمة إلى ملف متعدد

تعد حقول الصف إضافة حديثة للغة.

في السابق ، كانت فصولنا تمتلك طرقًا فقط.

“حقول class” هي بنية تسمح بإضافة أي خصائص.

على سبيل المثال ، دعنا نضيف خاصية name إلىclass User:

class User {
  name = "John";

  sayHi() {
    alert(`Hello, ${this.name}!`);
  }
}

new User().sayHi(); // Hello, John!

لذلك ، نكتب فقط “<اسم الخاصية> = <قيمة>” في الإعلان ، وهذا كل شيء.

الاختلاف المهم في حقول الصف هو أنه يتم تعيينها على كائنات فردية ، وليس User.prototype:

class User {
  name = "John";
}

let user = new User();
alert(user.name); // John
alert(User.prototype.name); // undefined

من الناحية الفنية ، تتم معالجتها بعد أن يقوم المنشئ بعمله ، ويمكننا استخدامه بالنسبة لهم التعبيرات المعقدة واستدعاءات الوظائف:

class User {
  name = prompt("Name, please?", "John");
}

let user = new User();
alert(user.name); // John

عمل طرق مرتبطة بحقول class

كما هو موضح في الفصل <info: bind> ، فإن وظائف JavaScript لها ديناميكية this. يعتمد ذلك على سياق المكالمة.

لذلك إذا تم تمرير طريقة كائن واستدعاؤها في سياق آخر ، فلن يكون هذا مرجعاً إلى كائنه بعد الآن.

على سبيل المثال ، سيُظهر هذا الرمز undefined:

class Button {
  constructor(value) {
    this.value = value;
  }

  click() {
    alert(this.value);
  }
}

let button = new Button("hello");

setTimeout(button.click, 1000); // undefined

تسمى المشكلة “فقدان” this “”.

هناك طريقتان لإصلاحها ، كما هو موضح في الفصل <info: bind>:

  1. قم بتمرير دالة مجمعة ، مثل setTimeout (() => button.click ()، 1000).
  2. ربط طريقة الاعتراض ، على سبيل المثال في المنشئ:

توفر حقول class بنية أكثر أناقة للحل الأخير:

class Button {
  constructor(value) {
    this.value = value;
  }
  click = () => {
    alert(this.value);
  }
}

let button = new Button("hello");

setTimeout(button.click, 1000); // hello

ينشئ حقل الفئة click = () => {...} وظيفة مستقلة على كل كائن Button ، معthis مرتبطًا بالكائن. ثم يمكننا تمرير “button.click” في أي مكان ، وسيتم استدعاؤها باستخدام this الصحيح.

هذا مفيد بشكل خاص في بيئة المتصفح ، عندما نحتاج إلى إعداد طريقة كمستمع للأحداث.

ملخص

تبدو بنية الصف الأساسية كما يلي:

class MyClass {
  prop = value; // property

  constructor(...) { // constructor
    // ...
  }

  method(...) {} // method

  get something(...) {} // getter method
  set something(...) {} // setter method

  [Symbol.iterator]() {} // method with computed name (symbol here)
  // ...
}

MyClass هي وظيفة من الناحية الفنية (تلك التي نقدمها على أنها مُنشئ) ، بينما تتم كتابة الأساليب والرسومات والمستوطنين علىMyClass.prototype.

في الفصول التالية سنتعلم المزيد عن الفصول ، بما في ذلك الميراث والميزات الأخرى.

مهمه

The Clock class (see the sandbox) is written in functional style. Rewrite it in the “class” syntax.

ملاحظة. تدق الساعة في وحدة التحكم ، افتحها لترى.

افتح sandbox للمهمه.

الحل
class Clock {
  constructor({ template }) {
    this.template = template;
  }

  render() {
    let date = new Date();

    let hours = date.getHours();
    if (hours < 10) hours = '0' + hours;

    let mins = date.getMinutes();
    if (mins < 10) mins = '0' + mins;

    let secs = date.getSeconds();
    if (secs < 10) secs = '0' + secs;

    let output = this.template
      .replace('h', hours)
      .replace('m', mins)
      .replace('s', secs);

    console.log(output);
  }

  stop() {
    clearInterval(this.timer);
  }

  start() {
    this.render();
    this.timer = setInterval(() => this.render(), 1000);
  }
}


let clock = new Clock({template: 'h:m:s'});
clock.start();

افتح الحل في sandbox.

خريطة الدورة التعليمية