Private va Public Field'lar
Class field deklaratsiyasi, # private field va metodlar, V8 ichida maxsus private slot, brand check (#field in obj) va closure orqali "private" bilan solishtirish.
30-dars
Private va Public Field'lar (class fields)
1. Public Field — nima va V8 da qanday ishlaydi
class Person {
name = 'noma\'lum'; // PUBLIC FIELD — class tanasida, constructor'dan tashqarida
constructor(name) {
this.name = name;
}
}
Bu — class field declaration (ES2022 standart qismi). U constructor ichidagi this.name = ...dan oldin, avtomatik bajariladi:
class Person {
name = 'default';
age = 0;
constructor(name, age) {
console.log(this.name); // 'default' — field allaqachon o'rnatilgan!
this.name = name;
this.age = age;
}
}
2. Private Field (#) — haqiqiy, til darajasidagi maxfiylik
class Person {
#age; // PRIVATE FIELD — # bilan boshlanadi
constructor(name, age) {
this.name = name;
this.#age = age;
}
getAge() {
return this.#age; // faqat class ICHIDAN kirish mumkin
}
}
const p1 = new Person('Ali', 25);
console.log(p1.getAge()); // 25 — ishlaydi
console.log(p1.#age); // ❌ SyntaxError: Private field '#age' must be declared in an enclosing class
console.log(p1.age); // undefined — bu boshqa property, #age bilan aloqasi yo'q
Muhim farq — closure pattern bilan solishtiring:
// Closure orqali (eski usul):
function createPerson(name, age) {
let _age = age; // "private" — konvensiya orqali, haqiqiy emas
return {
getAge() { return _age; }
};
}
Closure'dagi _age — haqiqatan tashqaridan ko'rinmaydi (chunki u funksiya scope'ida, faqat closure orqali ushlanadi). Lekin bu — har bir instance uchun alohida funksiya yaratishni talab qiladi ("yomon pattern"). #age esa — class field, u prototype darajasida ishlaydigan metodlar (getAge) bilan birga, xotira jihatdan samarali ishlaydi.
3. V8 ichida #field qayerda saqlanadi?
Bu — eng qiziq qism. #age — Hidden Classning oddiy property qismida saqlanmaydi. V8 unga alohida, maxsus ichki slot ajratadi:
p1 (instance):
┌─────────────────────┐
│ Hidden Class ptr │──→ C1 { name: offset0 } ← FAQAT public "name" ko'rinadi
├─────────────────────┤
│ properties[0] │──→ 'Ali'
├─────────────────────┤
│ [[PrivateFieldSlot]] │──→ #age → 25 ← ALOHIDA, maxfiy slot
└─────────────────────┘
Bu shuni anglatadiki — #age hech qachon Object.keys(), JSON.stringify(), for...in, hatto Object.getOwnPropertyNames() orqali hech qanday holatda ko'rinmaydi — bu enumerable: falsedan ham kuchliroq, chunki bu til darajasidagi (syntax-level) himoya, descriptor darajasidagi emas:
console.log(Object.keys(p1)); // ['name'] — #age YO'Q
console.log(Object.getOwnPropertyNames(p1)); // ['name'] — #age BU YERDA HAM YO'Q!
console.log(JSON.stringify(p1)); // '{"name":"Ali"}' — #age YO'Q
enumerable: false bo'lsa ham, Object.getOwnPropertyNames orqali property hali ham ko'rinardi edi. #age esa — bundan butunlay boshqa, kuchliroq himoya darajasi: u grammatika (syntax) darajasida yashiringan, descriptor flag orqali emas.
4. Private field'ga tashqaridan kirishga urinish — nima uchun undefined emas, SyntaxError?
class Person {
#age = 25;
}
const p1 = new Person();
console.log(p1.someRandomProp); // undefined — oddiy, mavjud bo'lmagan property
console.log(p1.#age); // ❌ SyntaxError — BUTUNLAY BOSHQA XATO TURI!
Nega undefined emas, balki SyntaxError? Chunki #age — faqat shu #ageni e'lon qilgan class ichida grammatik jihatdan tan olinadigan token. Agar siz class'dan tashqarida .#age yozsangiz, JS parser buni umuman tushunmaydi — bu hatto runtime xatosi ham emas, parse vaqtidagi xato (kod ishga tushishidan oldin aniqlanadi).
5. Private metodlar ham bo'ladi (nafaqat field)
class Person {
#age;
constructor(age) {
this.#age = age;
}
#validateAge() { // PRIVATE METOD
return this.#age > 0 && this.#age < 150;
}
getAge() {
if (!this.#validateAge()) {
throw new Error('Noto\'g\'ri yosh');
}
return this.#age;
}
}
#validateAge — bu private metod, u Person.prototypega qo'shiladi, lekin maxsus, ko'rinmas nom bilan — tashqaridan p1.#validateAge() deb chaqirib bo'lmaydi (xuddi private field kabi, SyntaxError beradi).
6. in operatori — private field mavjudligini xavfsiz tekshirish
class Person {
#age;
constructor(age) { this.#age = age; }
static isPerson(obj) {
return #age in obj; // maxsus sintaksis — mavjudligini xato bermay tekshirish
}
}
const p1 = new Person(25);
console.log(Person.isPerson(p1)); // true
console.log(Person.isPerson({})); // false — xato bermaydi!
Bu — "brand check" deb ataladi, ko'pincha obyekt "chindan shu class instance'imi" ekanini tekshirish uchun ishlatiladi (instanceofdan ham ishonchliroq, chunki instanceof prototype almashtirilsa aldanishi mumkin edi).
7. Static bilan birlashtirilgan holat (Singleton misoli)
class Config {
static #instance; // STATIC + PRIVATE — Config'ning o'zida, tashqaridan ko'rinmas
static get instance() {
if (!Config.#instance) {
Config.#instance = new Config();
}
return Config.#instance;
}
#settings = {};
setSetting(key, value) {
this.#settings[key] = value;
}
}
const c1 = Config.instance;
const c2 = Config.instance;
console.log(c1 === c2); // true — bitta instance, private static field orqali saqlangan
console.log(Config.#instance); // ❌ SyntaxError — tashqaridan ko'rinmaydi
8. Xulosa — jadval bilan
| Public field | Private field (#) |
Closure orqali "private" | |
|---|---|---|---|
| Yozilishi | name = val |
#name = val |
let _name (funksiya ichida) |
| Tashqaridan ko'rinishi | ✅ To'liq ochiq | ❌ SyntaxError (parse darajasida) | ❌ Ko'rinmaydi (lekin _name konvensiyasi — chin ma'noda ochiq) |
Object.keys/JSON.stringify |
Ko'rinadi | Hech qachon ko'rinmaydi | Ko'rinmaydi (chunki closure scope'ida, obyektning o'zida emas) |
| Xotira | Hidden Class orqali (instance) | Maxsus private slot (instance) | Har instance uchun alohida funksiya (agar metod ham closure ichida bo'lsa) |
| Metodlar bilan bo'lishish | prototype orqali | prototype orqali (private metodlar ham) | Ko'pincha bo'lishilmaydi (har instance o'z nusxasi) |