OOP & Prototypes (ES5)

"JavaScript-ը պրոտոտիպային լեզու է։ Հասկանալով պրոտոտիպերը՝ դուք տիրապետում եք լեզվի հիմքին։"

JavaScript-ում ամեն օբյեկտ ունի հատուկ թաքնված հատկություն՝ [[Prototype]], որը կա՛մ null է, կա՛մ հղում է մեկ այլ օբյեկտի։ Այդ օբյեկտը կոչվում է "prototype" (նախատիպ)։

Գաղափարը

Եթե մենք փորձում ենք կարդալ մի հատկություն (property), որը չկա օբյեկտի մեջ, JS-ը ավտոմատ այն փնտրում է իր prototype-ի մեջ։

Երբ մենք ստեղծում ենք օբյեկտ կոնստրուկտոր ֆունկցիայի միջոցով new F(), նոր ստեղծված օբյեկտի [[Prototype]]-ը դառնում է այն օբյեկտը, որին հղվում է F.prototype-ը։

Օրինակ

function Animal(name) {
  this.name = name;
}

// Ավելացնում ենք մեթոդ նախատիպի մեջ
Animal.prototype.eat = function() {
  console.log(this.name + " is eating...");
};

let rabbit = new Animal("White Rabbit");
rabbit.eat(); // "White Rabbit is eating..."

Կարևոր է

Animal.prototype-ը սովորական օբյեկտ է, որն ունի մեկ նախնական հատկություն՝ constructor, որը հղվում է հենց ֆունկցիայի վրա։

Պատկերացրեք՝ ունեք 1000 հատ Animal օբյեկտ։

❌ Առանց Prototype

Եթե մեթոդը սահմանեք this.eat = function... կոնստրուկտորի ներսում, ամեն մի օբյեկտի հետ կստեղծվի նոր ֆունկցիա (հիշողության վատնում)։

✅ Prototype-ով

Ֆունկցիան ստեղծվում է մեկ անգամ Animal.prototype-ում, և բոլոր 1000 օբյեկտները հղվում են նույն ֆունկցիայի վրա։

Ժառանգությունը JavaScript-ում հիմնված է Prototypal Delegation-ի (Նախատիպային պատվիրակման) վրա: Օբյեկտը «պատվիրակում» է իր հարցումները ծնողին, երբ իր մոտ չի գտնում համապատասխան հատկությունը:

1. Կոնստրուկտորի կանչ (Constructor Stealing)

function Animal(name) {
  this.name = name;
}

function Rabbit(name, speed) {
  // Կանչում ենք ծնողին այս (this) օբյեկտի համար
  Animal.call(this, name);
  this.speed = speed;
}

2. Prototype Chain-ի կառուցում

// Ստեղծում ենք կապ Animal.prototype-ի հետ
Rabbit.prototype = Object.create(Animal.prototype);

// Վերականգնում ենք constructor-ը
Rabbit.prototype.constructor = Rabbit;

3. Մեթոդների ավելացում և Override

Animal.prototype.move = function() {
  console.log(this.name + " is moving.");
};

// Override (Գերազանցում)
Rabbit.prototype.move = function() {
  console.log(this.name + " hops fast!");
};

Երբ դուք կանչում եք bugsy.move(), տեղի է ունենում հետևյալը.

  • 1. Search in bugsy: Կա՞ արդյոք move մեթոդը հենց օբյեկտի մեջ: (Ոչ):
  • 2. Search in Rabbit.prototype: JS-ը բարձրանում է վերև: Կա՞ այստեղ move: (Այո):
  • 3. Search in Animal.prototype: Եթե նախորդում չլիներ, նա կբարձրանար այստեղ:
  • 4. Object.prototype: Վերջին կանգառը:
graph TD bugsy["Object: bugsy"] e1@--> RabbitProto["Rabbit.prototype"] e2@--> AnimalProto["Animal.prototype"] e3@--> ObjectProto["Object.prototype"] e4@--> Null["null"] e1@{ animate: true } e2@{ animate: true } e3@{ animate: true } e4@{ animate: true } style bugsy fill:#f7df1e,color:#000 style RabbitProto fill:#61afef,color:#000 style AnimalProto fill:#98c379,color:#000

⚠️ Հաճախակի տրվող հարց

Ինչու՞ չօգտագործել Rabbit.prototype = new Animal():

Սա վատ պրակտիկա է, որովհետև new Animal()-ը ստեղծում է Animal-ի հատկություններ (օրինակ՝ name: undefined) հենց Prototype-ի մեջ: Object.create-ը ստեղծում է «մաքուր» կապ առանց ծնողի կոնստրուկտորը կանչելու:

__proto__ vs prototype

prototype-ը հատկություն է, որը կա միայն ֆունկցիաների մեջ։

[[Prototype]]-ը (կամ __proto__-ն) հղում է, որը կա բոլոր օբյեկտների մեջ։

Performance & Native

Բոլոր ներկառուցված օբյեկտները (Array, Date) ունեն իրենց նախատիպերը։

Օգտագործեք obj.hasOwnProperty(key) միայն սեփական հատկությունները ստուգելու համար։