ور

وراثت در JavaScript

1 تمرین

درباره‌ی وراثت

«ارث‌بری» روشی است برای ساختن رابطه‌ی والد و فرزند بین کلاس‌ها. کلاس فرزند (که گاهی «زیرکلاس» نامیده می‌شود) به رفتار و داده‌ای که کلاس والد (که گاهی «اَبَرکلاس» نامیده می‌شود) تعریف کرده دسترسی دارد.

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

  introduce() {
    console.log(`This is my pet, ${this.name}.`);
  }
}

class Dog extends Pet {}

const dog = new Dog('Otis');
dog.introduce();
// => This is my pet, Otis.

کلیدواژه‌ی extends در اعلان کلاس فرزند، از طریق زنجیره‌ی پروتوتایپ رابطه‌ای با کلاس والد برقرار می‌کند.

شیءهایی که سازنده‌ی فرزند می‌سازد، پروتوتایپ کلاس والد را در زنجیره‌ی پروتوتایپ خود دارند و از این راه به هر متد یا داده‌ای که والد تعریف کرده دسترسی پیدا می‌کنند.

const dog = new Dog('Otis');

Dog.prototype.isPrototypeOf(dog); // => true
Pet.prototype.isPrototypeOf(dog); // => true
Pet.prototype.isPrototypeOf(Dog.prototype); // => true

Pet.prototype.hasOwnProperty('introduce'); // => true
Dog.prototype.hasOwnProperty('introduce'); // => false
dog.hasOwnProperty('introduce'); // => false

سازنده

اگر کلاس فرزند هیچ تابع سازنده‌ای تعریف نکند، از تابع سازنده‌ی والد استفاده می‌شود. اما اگر کلاس فرزند تابع سازنده‌ی خودش را تعریف کند، باید سازنده‌ی والد را به‌صراحت فراخوانی کرد. برای فراخوانی سازنده‌ی والد از داخل حوزه‌ی سازنده‌ی فرزند، از کلیدواژه‌ی super استفاده می‌شود.

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

class Dog extends Pet {
  constructor(name, breed) {
    super(name);
    this.breed = breed;
  }
}

const dog = new Dog('Otis', 'Pug');

از آنجا که سازنده‌ی والد مسئول مقداردهی اولیه‌ی شیء جدید و تخصیص آن به this است، باید پیش از آن که سازنده‌ی فرزند از this استفاده کند فراخوانی شود.

class Dog extends Pet {
  constructor(name, breed) {
    // using 'this' before calling the parent constructor with 'super'
    this.breed = breed;
    super(name);
  }
}

const dog = new Dog('Otis', 'Pug');
// => ReferenceError: Must call super constructor in derived class before accessing 'this'...

تعریف متدها روی کلاس فرزند

کلاس فرزند می‌تواند علاوه بر رفتار به‌ارث‌رسیده از والد، رفتار خودش را هم تعریف کند.

این یکی از دلایل اصلی استفاده از ارث‌بری است؛ داشتن کلاس‌های فرزند تخصصی که داده‌ها و متدهای منحصربه‌فرد خودشان را دارند و از طریق متدها و داده‌های مشترکی که کلاس والد فراهم می‌کند به هم مربوط می‌شوند.

class Dog extends Pet {
  constructor(name, breed) {
    super(name);
    this.breed = breed;
  }

  describe() {
    console.log(`${this.name} is a ${this.breed}.`);
  }
}

const dog = new Dog('Otis', 'Pug');
dog.introduce();
dog.describe();
// => 'This is my pet, Otis.'
// => 'Otis is a Pug.'

بازنویسی متدهای به‌ارث‌رسیده از کلاس والد

کلاس فرزند می‌تواند رفتار متدی را که والد تعریف کرده هم بازنویسی کند و آن را با رفتاری که خودش تعریف می‌کند جایگزین کند یا گسترش دهد.

class Cat extends Pet {
  // replacing parent class behavior
  introduce() {
    console.log(`This is my cat, ${this.name}.`);
  }
}

class Dog extends Pet {
  constructor(name, breed) {
    super(name);
    this.breed = breed;
  }

  describe() {
    console.log(`${this.name} is a ${this.breed}.`);
  }

  // extending parent class behavior
  introduce() {
    super.introduce();
    this.describe();
  }
}

const cat = new Cat('Milo');
cat.introduce();
// => 'This is my cat, Milo.'

const dog = new Dog('Otis', 'Pug');
dog.introduce();
// => This is my pet, Otis.
// => Otis is a Pug.

برای فراخوانی متدی که روی کلاس والد تعریف شده از داخل بدنه‌ی متدی با همان اسم روی کلاس فرزند، باید از کلیدواژه‌ی super برای اشاره به والد استفاده کرد.

ویرایش از طریق GitHub این پیوند در پنجره یا زبانه‌ی جدیدی باز می‌شود

وراثت را یاد بگیرید