Constructor Function
Constructor function nima, .prototype qanday avtomatik biriktiriladi, new siz chaqirilsa nima bo'ladi, instanceof, constructor property va new.target.
25-dars
Constructor Function
1. Constructor function nima o'zi?
function Person(name, age) {
this.name = name;
this.age = age;
}
const p1 = new Person('Ali', 25);
Constructor function — bu shunchaki oddiy funksiya, faqat u new kalit so'zi bilan chaqirilishi uchun mo'ljallab yozilgan. JS da alohida "constructor" degan sintaksis yo'q (class paydo bo'lgunga qadar) — har qanday oddiy funksiya constructor bo'la oladi, agar siz uni new bilan chaqirsangiz.
Konventsiya (majburiy emas, lekin qat'iy odat): constructor function lar nomi Katta harf bilan boshlanadi (Person, Array, Date), oddiy funksiyalar esa kichik harf bilan (getName, calculate). Bu — faqat o'qish qulayligi uchun, JS engine buni tekshirmaydi.
2. Har bir funksiya avtomatik .prototypega ega bo'ladi
function Person(name) {
this.name = name;
}
console.log(Person.prototype); // {} — allaqachon mavjud!
console.log(Person.prototype.constructor === Person); // true
Funksiya e'lon qilingan paytdayoq, V8 avtomatik ravishda unga ikkita narsani biriktiradi:
Person.prototype— bo'sh obyekt (keyinchalik metodlar shu yerga qo'shiladi)Person.prototype.constructor— bu prototype obyekti ichida, orqaga,Personning o'ziga ishora qiluvchi pointer
HEAP:
Person (function object)
│
│ .prototype
▼
Person.prototype (object) { constructor: → Person }
│
│ [[Prototype]]
▼
Object.prototype
Muhim: bu .prototype — arrow function larda yo'q:
const Foo = () => {};
console.log(Foo.prototype); // undefined
Shu sababli arrow function new bilan chaqirilmaydi — new Foo() xato beradi, chunki newga kerakli .prototype mavjud emas.
3. Constructor function chaqirilmasa nima bo'ladi (oddiy funksiya kabi)
function Person(name) {
this.name = name;
}
const result = Person('Ali'); // 'new' YO'Q!
console.log(result); // undefined
console.log(window.name); // 'Ali' — GLOBAL OBYEKTGA yozildi! (non-strict mode)
new yo'q holda chaqirilsa, Person — oddiy funksiya sifatida ishlaydi. Ichidagi this — global obyektga (brauzerda window, Node'da global, yoki strict mode'da undefined) ishora qiladi. Bu — eng xavfli, ko'p uchraydigan xatolardan biri:
'use strict';
function Person(name) {
this.name = name; // TypeError: Cannot set property 'name' of undefined
}
Person('Ali'); // strict mode'da darhol xato beradi (yaxshi tomoni!)
Strict mode'da this — undefined bo'ladi (global obyektga tushmaydi), shuning uchun xato darhol ko'rinadi, aks holda global obyektni "iflos qilib" (pollute) sokin xatoga olib boradi.
4. newsiz ishlashni oldini olish (himoya patterni)
function Person(name) {
if (!(this instanceof Person)) {
return new Person(name); // avtomatik to'g'irlaydi
}
this.name = name;
}
const p1 = Person('Ali'); // 'new' yozmasangiz ham, ichkarida to'g'irlanadi
console.log(p1.name); // 'Ali'
Bu — eski, ES5 davridagi himoya patterni. Zamonaviy JS da bunga o'rniga new.target ishlatiladi (keyinroq ko'ramiz) yoki oddiygina class ishlatiladi, chunki class hech qachon newsiz chaqirilishga ruxsat bermaydi (TypeError beradi).
5. Bir nechta instance — har biri mustaqil this, lekin bitta umumiy prototype
function Person(name, age) {
this.name = name; // HAR BIR instance uchun ALOHIDA
this.age = age; // HAR BIR instance uchun ALOHIDA
}
Person.prototype.greet = function() { // BARCHA instance uchun BITTA, umumiy
return `Salom, men ${this.name}`;
};
const p1 = new Person('Ali', 25);
const p2 = new Person('Vali', 30);
HEAP:
p1 { name: 'Ali', age: 25, [[Prototype]] ──┐
p2 { name: 'Vali', age: 30, [[Prototype]] ──┼──→ Person.prototype { greet }
Qoida oddiy: this.xxx = ... orqali yozilgan narsa — har bir instance uchun mustaqil (o'z Hidden Class'ida saqlanadi). Person.prototype.xxx = ... orqali yozilgan narsa — barcha instance lar uchun bitta, umumiy (bo'lishiladi).
Amaliy qoida: metodlarni har doim prototypega qo'shing, this ichiga emas — aks holda har bir instance o'zining alohida funksiya nusxasini yaratadi, xotira behuda sarflanadi:
// ❌ YOMON — har instance uchun YANGI funksiya yaratiladi
function Person(name) {
this.name = name;
this.greet = function() { return `Salom, ${this.name}`; }; // har safar yangi function object!
}
// ✅ YAXSHI — bitta funksiya, barcha instance bo'lishadi
function Person(name) {
this.name = name;
}
Person.prototype.greet = function() { return `Salom, ${this.name}`; };
Birinchi variantda, 1000 ta Person yaratilsa — 1000 ta alohida greet funksiya obyekti yaratiladi (heap'da behuda joy egallaydi). Ikkinchi variantda — bitta greet funksiya, 1000 ta instance uni bo'lishadi.
6. instanceof — qanday ishlaydi?
console.log(p1 instanceof Person); // true
instanceof — bu obyektning Hidden Class'ini yoki nomini tekshirmaydi. U prototype chain bo'ylab yuradi va Person.prototypeni qidiradi:
p1.[[Prototype]] === Person.prototype ? → HA bo'lsa, true
console.log(p1 instanceof Object); // true — chunki Person.prototype → Object.prototype zanjirida bor
Nozik nuqta: agar siz Person.prototypeni butunlay almashtirsangiz, eski instance lar "eskirib qoladi":
function Person(name) { this.name = name; }
const p1 = new Person('Ali');
Person.prototype = { greet() { return 'yangi'; } }; // PROTOTYPE ALMASHTIRILDI
console.log(p1 instanceof Person); // FALSE! — p1 hali ESKI prototype'ga ishora qiladi
p1 yaratilganda [[Prototype]] o'sha paytdagi Person.prototypega ulangan edi (pointer sifatida saqlangan). Keyin Person.prototypeni butunlay yangi obyektga almashtirsangiz, p1ning pointeri eski obyektga ishora qilib qolaveradi — u yangilanmaydi. Shuning uchun p1 instanceof Person — endi false, chunki Person.prototype (yangi) va p1.[[Prototype]] (eski) — endi ikki xil obyekt.
7. constructor property — nega u muhim va qachon "buziladi"
function Person(name) { this.name = name; }
const p1 = new Person('Ali');
console.log(p1.constructor === Person); // true
p1ning o'zida constructor yo'q — u prototype chain orqali topiladi (Person.prototype.constructor). Bu — "men qaysi funksiya orqali yaratilganman" degan ma'lumot sifatida foydali:
function createSame(obj) {
return new obj.constructor();
}
Lekin diqqat — agar siz prototypeni to'liq almashtirsangiz, constructor yo'qoladi:
Person.prototype = { greet() {} }; // constructor YO'Q endi bu yangi obyektda!
const p2 = new Person('Vali');
console.log(p2.constructor === Person); // false!
console.log(p2.constructor === Object); // true — Object.prototype'dagi umumiy constructor topildi
To'g'ri yo'l — prototypeni almashtirish o'rniga, unga qo'shish:
Person.prototype.greet = function() {}; // constructor buzilmaydi
// yoki, agar butunlay almashtirsangiz, constructor'ni qo'lda tiklang:
Person.prototype = {
constructor: Person, // qo'lda tiklandi
greet() {}
};
8. new operatori — funksiyaga qanday "ishora" beradi (new.target)
function Person(name) {
console.log(new.target); // Person yoki undefined
this.name = name;
}
new Person('Ali'); // new.target → Person (funksiya)
Person('Vali'); // new.target → undefined
new.target — zamonaviy, aniq yo'l bilan "men new orqali chaqirildimmi yoki yo'qmi"ni tekshirish uchun:
function Person(name) {
if (!new.target) {
throw new Error("Person faqat 'new' bilan chaqirilishi kerak!");
}
this.name = name;
}
Bu — eski this instanceof Person patternidan ishonchliroq, chunki instanceof tekshiruvi ba'zi murakkab holatlarda (masalan meros zanjirida) noto'g'ri natija berishi mumkin, new.target esa to'g'ridan-to'g'ri, aniq javob beradi.
Xulosa
Constructor function — bu oddiy funksiya, faqat new bilan chaqirilishga mo'ljallangan; har bir funksiya avtomatik .prototype obyektiga ega bo'ladi (u yerda constructor orqaga pointer bilan turadi); this.xxx — har instance uchun mustaqil (Hidden Class'da), prototype.xxx — barcha instance bo'lishadigan, umumiy; instanceof — prototype chain bo'ylab tekshiradi, prototype almashtirilsa bu tekshiruv "buziladi".