Series: JavaScript Basics Lesson 27

Class (Syntactic Sugar)

Class tagida nima yotadi — constructor function + prototype. newsiz chaqirilmasligi, enumerable:false metodlar, TDZ va avtomatik strict mode kabi haqiqiy farqlar.

27-dars

Class (Syntactic Sugar)

1. "Syntactic sugar" nima degani?

class Person {
  constructor(name) {
    this.name = name;
  }
  greet() {
    return `Salom, men ${this.name}`;
  }
}

"Syntactic sugar" — degani: bu yangi mexanizm emas, faqat eski mexanizmni (constructor function + prototype) yozishning yangi, chiroyliroq sintaksisi. V8 ichida class uchun alohida engine yo'q — u parse bosqichida deyarli xuddi constructor function + prototype.method shakliga aylantiriladi.

Buni isbotlash mumkin:

console.log(typeof Person); // 'function' — CLASS ASLIDA FUNKSIYA!

class Person {} yozganingizda ham, typeof Person — 'function' qaytaradi, obyekt yoki boshqa maxsus tur emas. Bu — class'ning tagida hali ham constructor function yotganining isboti.


2. Class qanday "ochiladi" (desugar qilinsa)

class Person {
  constructor(name) {
    this.name = name;
  }
  greet() {
    return `Salom, men ${this.name}`;
  }
}

Bu — deyarli aynan quyidagiga teng:

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

Person.prototype.greet = function() {
  return `Salom, men ${this.name}`;
};

const p1 = new Person('Ali');
console.log(p1.greet()); // ikkalasida ham bir xil natija
console.log(p1.__proto__ === Person.prototype); // true — ikkalasida ham

Class ichidagi metodlar (greet()) — avtomatik ravishda Person.prototypega qo'shiladi, xuddi qo'lda Person.prototype.greet = ... yozgandagidek. Bu — xotira tejash (bitta metod, barcha instance bo'lishadi) hali ham class'da ham amal qiladi, chunki tagida bir xil mexanizm ishlaydi.


3. Farqlar — class va constructor function butunlay bir xil emas!

Garchi tagida bir xil mexanizm yotsa ham, class bir nechta qo'shimcha, qat'iy qoidalar kiritadi. Bular — haqiqiy farqlar, faqat "sintaksis" emas:

Farq 1 — newsiz chaqirib bo'lmaydi

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

Person('Ali'); // TypeError: Class constructor Person cannot be invoked without 'new'

Oddiy constructor function esa newsiz ham (noto'g'ri, lekin) chaqirilib ketaveradi. Class bu xatoni V8 darajasida oldindan bloklaydi — bu ataylab kiritilgan xavfsizlik.

Farq 2 — Class metodlari enumerable: false

function Person(name) { this.name = name; }
Person.prototype.greet = function() {};

console.log(Object.getOwnPropertyDescriptor(Person.prototype, 'greet').enumerable); // true

class PersonClass {
  greet() {}
}

console.log(Object.getOwnPropertyDescriptor(PersonClass.prototype, 'greet').enumerable); // false!

enumerable: false degani for...in va Object.keys()da ko'rinmaydi degani edi. Class metodlari avtomatik shunday qilib yaratiladi — chunki built-in metodlar (masalan Array.prototype.push) ham xuddi shunday, enumerable: false. Bu — class'ning constructor function'dan haqiqiy, texnik farqi, shunchaki sintaksis emas.

for (let key in new PersonClass()) {
  console.log(key); // greet HECH QACHON ko'rinmaydi
}

Farq 3 — Temporal Dead Zone (TDZ), Hoisting yo'q

const p1 = new Person('Ali'); // ✅ ishlaydi, chunki function hoisting bor
function Person(name) { this.name = name; }
const p2 = new PersonClass('Ali'); // ❌ ReferenceError: Cannot access before initialization
class PersonClass { constructor(name) { this.name = name; } }

Class hoisting bo'ladi (nomi biladi), lekin let/const kabi TDZ (Temporal Dead Zone) ga ega — e'lon qatoridan oldin foydalanib bo'lmaydi.

Farq 4 — Class tanasi avtomatik strict mode da ishlaydi

class Person {
  constructor(name) {
    this.name = name;
    undeclaredVar = 5; // ReferenceError — strict mode avtomatik yoqilgan!
  }
}

Oddiy funksiyada 'use strict' yozilmasa, bunday xato sokin o'tib ketardi (global o'zgaruvchi yaratilardi). Class ichida bu har doim, avtomatik strict mode — dasturchi buni o'zgartira olmaydi.


4. Getter/Setter class ichida — Property Descriptor bilan bog'lanish

class Person {
  constructor(firstName, lastName) {
    this.firstName = firstName;
    this.lastName = lastName;
  }

  get fullName() {
    return `${this.firstName} ${this.lastName}`;
  }

  set fullName(value) {
    [this.firstName, this.lastName] = value.split(' ');
  }
}

Bu — Accessor Descriptorning class ichidagi yozilishi. Ichkarida bu xuddi Object.defineProperty(Person.prototype, 'fullName', { get, set }) ga teng — faqat qulayroq sintaksis bilan.


5. constructor() — bu maxsus, ajratilgan metod

class Person {
  constructor(name) {
    this.name = name; // BU YERDA property qo'shiladi (Hidden Class C1 {name})
  }
}

constructor — class ichida maxsus nom sifatida ishlaydi. Uning ichidagi kod — parse qilinganda, aynan funksiya tanasiga aylanadi (Person.call(newObj, ...) bosqichida bajariladigan kod). Class ichidagi boshqa metodlar (greet()) esa — prototypega qo'shiladigan alohida funksiyalar.

class Person {
  constructor(name) { ... }   →  function Person(name) { ... }
  greet() { ... }             →  Person.prototype.greet = function() { ... }
}

Agar constructor yozilmasa, V8 bo'sh constructorni avtomatik yaratadi:

class Empty {}
// avtomatik ravishda:
// function Empty() {}

6. Xulosa jadval — Class vs Constructor Function

Xususiyat Constructor Function Class
Tagida nima yotadi Funksiya + prototype Xuddi shu (ichkarida)
typeof natija 'function' 'function'
newsiz chaqirish Ruxsat (xavfli, sokin xato) TypeError — bloklanadi
Metodlar enumerable true (default) false (avtomatik)
Hoisting To'liq (butun funksiya) Faqat nom, TDZ bor
Strict mode Qo'lda yoqish kerak Avtomatik, majburiy
Getter/setter defineProperty orqali qo'lda get/set sintaksis bilan qulay