Skip to content

Latest commit

 

History

History
1281 lines (1006 loc) · 36.5 KB

File metadata and controls

1281 lines (1006 loc) · 36.5 KB

OOP — Interview Savollari

OOP 4 ustuni (Encapsulation, Abstraction, Inheritance, Polymorphism), SOLID printsiplari, prototypal vs classical OOP, composition vs inheritance, va duck typing haqida interview savollari.


Nazariy savollar

1. OOP ning 4 ta asosiy ustunini (pillar) ayting va har birini JS misolida tushuntiring [Junior+]

Javob

OOP 4 ta ustun (pillar) ga qurilgan:

  1. Encapsulation — ma'lumotni yashirish va himoyalash
  2. Abstraction — murakkablikni yashirish, faqat kerakli qismini ko'rsatish
  3. Inheritance — bir class'dan boshqasiga meros olish (code reuse)
  4. Polymorphism — bir xil nomli method turli class'larda turli xil ishlashi
// 1. Encapsulation — private field bilan himoya:
class BankAccount {
  #balance = 0;
  deposit(amount) {
    if (amount > 0) this.#balance += amount;
  }
  get balance() { return this.#balance; }
}

// 2. Abstraction — foydalanuvchi faqat play() ni biladi, ichki dekoder mexanizmi yashirin:
class AudioDecoder {
  decode(file) { /* ... murakkab decoding logic ... */ return "decoded audio"; }
}
class MediaPlayer {
  #decoder = new AudioDecoder();
  play(file) { return this.#decoder.decode(file); }
  // Foydalanuvchi #decoder, decode() haqida bilmasligi kerak — faqat play()
}

// 3. Inheritance — meros:
class Animal {
  speak() { return "..."; }
}
class Dog extends Animal {
  speak() { return "Hav!"; }
}

// 4. Polymorphism — turli xil xatti-harakat:
const animals = [new Animal(), new Dog()];
animals.forEach(a => console.log(a.speak()));
// "..." , "Hav!" — bir xil method, turli natija

2. Encapsulation nima va JS da qanday qilinadi? [Junior+]

Javob

Encapsulation — ob'ektning ichki state'ini tashqi koddan berkitish va faqat belgilangan method'lar orqali kirish imkonini berish.

JS da encapsulation qilishning asosiy usullari:

// 1. Private fields # (zamonaviy, eng yaxshi usul):
class User {
  #password;
  constructor(name, pass) {
    this.name = name;
    this.#password = pass;
  }
  checkPassword(pass) { return pass === this.#password; }
}
const u = new User("Ali", "secret");
// u.#password; // ❌ SyntaxError — tashqi kirish taqiq

// 2. Closures (ES6 dan oldingi pattern):
function createCounter() {
  let count = 0; // private — tashqaridan ko'rinmaydi
  return {
    increment() { count++; },
    getCount() { return count; }
  };
}

// 3. WeakMap:
const _data = new WeakMap();
class Wallet {
  constructor(balance) { _data.set(this, { balance }); }
  getBalance() { return _data.get(this).balance; }
}

Muhim: _ prefix (underscore convention) encapsulation emas — bu faqat kelishuv, hech qanday himoya bermaydi. # private fields esa til darajasidagi enforcement.

3. Abstraction va Encapsulation farqi nima? [Middle]

Javob

Ko'pchilik bu ikkisini aralashtirib yuboradi. Farqi:

Encapsulation Abstraction
Maqsad Ma'lumotni himoyalash Murakkablikni yashirish
Savol "Kim kirishi mumkin?" "Nimani ko'rsatish kerak?"
Vosita Private fields, closures Public API, facade
Yo'nalish Ichki state ni berkitish Tashqi interface ni soddalashtirish
class Car {
  // Encapsulation — ichki state yashirin:
  #engine;
  #fuel = 100;

  constructor(engine) {
    this.#engine = engine;
  }

  // Abstraction — foydalanuvchi faqat shu method'larni biladi:
  start() {
    this.#checkFuel();       // murakkab ichki logika
    this.#engine.ignite();   // tashqi dependency
    this.#warmUp();          // ichki jarayon
    return "Mashina yurdi";  // sodda natija
  }

  // Ichki murakkablik — yashirin:
  #checkFuel() {
    if (this.#fuel <= 0) throw new Error("Yonilg'i yo'q");
  }

  #warmUp() {
    // 50 qatorlik murakkab logika...
  }
}

// Foydalanuvchi faqat start() ni biladi — ichki 3 ta qadam haqida bilmasligi kerak
// Bu ABSTRACTION
// #engine, #fuel ga kira olmasligi — bu ENCAPSULATION

4. Polymorphism JS da qanday ishlaydi? Turlarini ayting. [Middle]

Javob

Polymorphism — "ko'p shakllilik". JS da 3 turi bor:

1. Method Overriding — child class parent method ni qayta yozadi:

class Shape {
  area() { return 0; }
}

class Circle extends Shape {
  constructor(r) { super(); this.r = r; }
  area() { return Math.PI * this.r ** 2; } // override
}

class Square extends Shape {
  constructor(s) { super(); this.s = s; }
  area() { return this.s ** 2; } // override
}

// Polymorphism — bir xil method chaqiruv, turli natija:
[new Circle(5), new Square(3)].forEach(s => console.log(s.area()));
// 78.54, 9

2. Duck Typing — type emas, behavior tekshiriladi:

// Bu ob'ektlar hech qanday umumiy class'dan meros olmagan
const file   = { read() { return "file content"; } };
const stream = { read() { return "stream data"; } };

function processInput(source) {
  return source.read(); // read() bor — yetarli
}

processInput(file);   // ishlaydi
processInput(stream); // ishlaydi

3. Method Overloading (emulatsiya) — JS da native yo'q:

class Formatter {
  format(value) {
    if (typeof value === "number") return value.toFixed(2);
    if (typeof value === "string") return value.trim();
    if (Array.isArray(value)) return value.join(", ");
    return String(value);
  }
}

5. SOLID printsiplaridan "S" va "O" ni tushuntiring [Middle]

Javob

S — Single Responsibility Principle (SRP): Har bir class bitta vazifa uchun javobgar. O'zgartirish uchun bitta sabab.

// ❌ SRP buzilgan:
class UserService {
  createUser(data) { /* user yaratish */ }
  sendWelcomeEmail(user) { /* email */ }
  generateAvatar(user) { /* rasm */ }
  logAction(action) { /* log */ }
}

// ✅ SRP to'g'ri:
class UserService { createUser(data) { /* ... */ } }
class EmailService { send(to, template) { /* ... */ } }
class AvatarService { generate(user) { /* ... */ } }
class Logger { log(action) { /* ... */ } }

O — Open/Closed Principle (OCP): Kengaytirishga ochiq, o'zgartirishga yopiq.

// ❌ OCP buzilgan — har yangi tur uchun if qo'shish kerak:
class Discount {
  calculate(type, price) {
    if (type === "student") return price * 0.8;
    if (type === "veteran") return price * 0.7;
    // yangi tur = kodni o'zgartirish ⚠️
  }
}

// ✅ OCP to'g'ri — yangi strategy qo'shish yetarli:
class StudentDiscount {
  calculate(price) { return price * 0.8; }
}
class VeteranDiscount {
  calculate(price) { return price * 0.7; }
}

class PriceCalculator {
  constructor(discountStrategy) {
    this.discount = discountStrategy;
  }
  getPrice(price) {
    return this.discount.calculate(price);
  }
}

// Yangi tur — faqat yangi class, mavjud kod o'zgarmaydi:
class EmployeeDiscount {
  calculate(price) { return price * 0.75; }
}

6. Liskov Substitution Principle (LSP) nima? Buzilish misolini ko'rsating. [Middle+]

Javob

LSP: child class parent o'rnida ishlatilganda dastur buzilmasligi kerak. Ya'ni parent bilan ishlaydigan har qanday kod child bilan ham to'g'ri ishlashi shart.

// ❌ LSP buzilgan — klassik Rectangle/Square muammosi:
class Rectangle {
  constructor(w, h) {
    this.width = w;
    this.height = h;
  }

  setWidth(w) { this.width = w; }
  setHeight(h) { this.height = h; }

  area() { return this.width * this.height; }
}

class Square extends Rectangle {
  setWidth(w) {
    this.width = w;
    this.height = w; // ❌ height ham o'zgaradi — kutilmagan!
  }
  setHeight(h) {
    this.width = h;  // ❌ width ham o'zgaradi
    this.height = h;
  }
}

// Bu funksiya Rectangle bilan to'g'ri ishlaydi:
function testArea(rect) {
  rect.setWidth(5);
  rect.setHeight(4);
  console.log(rect.area()); // 20 kutamiz
}

testArea(new Rectangle(0, 0)); // 20 ✅
testArea(new Square(0, 0));    // 16 ❌ — LSP buzildi!
// Square berganда width = 4 (setHeight height ham width o'zgartirdi)


// ✅ LSP to'g'ri yechim:
class Shape {
  area() { throw new Error("implement qiling"); }
}

class Rectangle extends Shape {
  constructor(w, h) { super(); this.w = w; this.h = h; }
  area() { return this.w * this.h; }
}

class Square extends Shape {
  constructor(side) { super(); this.side = side; }
  area() { return this.side ** 2; }
}
// Endi ikkalasi ham Shape dan meros, bir-biriga bog'liq emas

7. Composition vs Inheritance — qachon qaysi biri? [Middle]

Javob
Inheritance Composition
Munosabat "is-a" (It is a Hayvon) "has-a" (Mashina has Motor)
Bog'liqlik Qattiq (tight coupling) Sust (loose coupling)
Flexibility Kam — hierarxiya qotib qoladi Yuqori — istalgan vaqtda o'zgaradi
Qachon Aniq "is-a" bo'lganda, 2-3 daraja Ko'pchilik holatda
// ❌ Inheritance xato ishlatilgan:
class Stack extends Array {
  peek() { return this.at(-1); }
}
const s = new Stack();
s.push(1, 2, 3);
s.shift();        // ❌ Stack da shift bo'lmasligi kerak!
s.splice(0, 1);   // ❌ Stack da splice bo'lmasligi kerak!
// Stack IS-A Array emas!

// ✅ Composition to'g'ri:
class Stack {
  #items = [];
  push(item)  { this.#items.push(item); }
  pop()       { return this.#items.pop(); }
  peek()      { return this.#items.at(-1); }
  get size()  { return this.#items.length; }
  // splice, shift, unshift — yo'q! Faqat stack operatsiyalari
}

Qoida: "Agar shubha bo'lsa — composition tanlang." Inheritance faqat aniq is-a munosabat bo'lganda va hierarxiya sayoz (2-3 daraja) bo'lganda.

8. Duck typing nima va JS da qanday ishlatiladi? [Middle]

Javob

Duck typing — "Agar o'rdakdek yursa va o'rdakdek qaqillasa — u o'rdak." Ya'ni ob'ektning type i emas, behavior i (qanday method'lari bor) muhim.

JS dinamik tipli til bo'lgani uchun duck typing natural ishlaydi:

// Iterator protocol — duck typing'ning klassik misoli:
// "Agar ob'ektda Symbol.iterator bor — u iterable"
const range = {
  from: 1,
  to: 5,
  [Symbol.iterator]() {
    let current = this.from;
    const last = this.to;
    return {
      next() {
        return current <= last
          ? { value: current++, done: false }
          : { done: true };
      }
    };
  }
};

for (const n of range) console.log(n); // 1, 2, 3, 4, 5
// range Array emas, Set emas — lekin iterable!

// Thenable — Promise.resolve duck typing ishlatadi:
const thenable = {
  then(resolve) { resolve(42); }
};
Promise.resolve(thenable).then(console.log); // 42
// Bu Promise emas, lekin then() bor — Promise dek ishlatildi!

Afzalligi: Interfeyslarsiz flexibility. Kamchiligi: Compile-time xato yo'q — runtime da sinadi. TypeScript bu muammoni yechadi.

9. JS da abstract class qanday yaratiladi? [Middle+]

Javob

JS da abstract keyword yo'q. Lekin new.target va Error throw qilish bilan emulatsiya qilish mumkin:

class DataStore {
  constructor() {
    if (new.target === DataStore) {
      throw new Error("DataStore abstract — to'g'ridan yaratib bo'lmaydi");
    }
  }

  // Abstract method'lar — subclass implement qilishi SHART:
  save(key, value) {
    throw new Error(`${this.constructor.name}.save() implement qilinmagan`);
  }

  get(key) {
    throw new Error(`${this.constructor.name}.get() implement qilinmagan`);
  }

  // Concrete method — barcha subclass'larga umumiy:
  has(key) {
    return this.get(key) !== undefined;
  }
}

class MemoryStore extends DataStore {
  #data = new Map();
  save(key, value) { this.#data.set(key, value); }
  get(key) { return this.#data.get(key); }
}

class LocalStore extends DataStore {
  save(key, value) { localStorage.setItem(key, JSON.stringify(value)); }
  get(key) { return JSON.parse(localStorage.getItem(key)); }
}

// const ds = new DataStore();    // ❌ Error: abstract
const mem = new MemoryStore();    // ✅
mem.save("name", "Ali");
console.log(mem.has("name"));     // true — concrete method ishlaydi

new.target — constructor qaysi class uchun chaqirilganini ko'rsatadi. Agar DataStore o'zi uchun chaqirilsa — xato beradi.

10. SOLID ning "D" — Dependency Inversion qanday ishlaydi? JS misolida ko'rsating. [Senior]

Javob

DIP: Yuqori darajadagi modul pastki darajadagiga bevosita bog'lanmasligi kerak. Ikkalasi ham abstraktsiyaga bog'lanishi kerak. JS da bu dependency injection orqali amalga oshiriladi:

// ❌ DIP buzilgan — PaymentService to'g'ridan-to'g'ri Stripe ga bog'liq:
class PaymentService {
  constructor() {
    this.provider = new StripeProvider(); // qattiq bog'liqlik
  }
  charge(amount) {
    return this.provider.charge(amount);
  }
}
// PayPal ga o'tkazmoqchi bo'lsak — PaymentService kodini o'zgartirish kerak

// ✅ DIP to'g'ri — dependency injection:
// "Interface" (duck typing):
// Har qanday provider: { charge(amount), refund(id) }

class StripeProvider {
  charge(amount) { return `Stripe: $${amount} charged`; }
  refund(id) { return `Stripe: ${id} refunded`; }
}

class PayPalProvider {
  charge(amount) { return `PayPal: $${amount} charged`; }
  refund(id) { return `PayPal: ${id} refunded`; }
}

class PaymentService {
  constructor(provider) { // tashqaridan beriladi
    this.provider = provider;
  }
  charge(amount) { return this.provider.charge(amount); }
  refund(id) { return this.provider.refund(id); }
}

// Istalgan provider bilan ishlaydi — kodni o'zgartirmasdan:
const stripe = new PaymentService(new StripeProvider());
const paypal = new PaymentService(new PayPalProvider());

// Test uchun mock:
const mock = new PaymentService({ charge: () => "mock", refund: () => "mock" });

DIP ning afzalligi:

  1. Testability — mock inject qilish oson
  2. Flexibility — provider almashtirinsh bir qator
  3. Maintainability — provider o'zgarganda PaymentService o'zgarmaydi

Deep Dive:

JavaScript da DIP static type system yo'qligi uchun duck typing orqali amalga oshiriladi — interface spec'da aniqlanmagan, lekin runtime'da provider.charge method mavjudligi tekshiriladi. TypeScript qo'shilganda interface PaymentProvider { charge(amount: number): string } kabi compile-time enforcement qo'shiladi. V8 nuqtai nazaridan, agar turli provider'lar bir xil method signature'ga ega bo'lsa — inline cache polymorphic holatda ishlaydi (2-4 ta shape), lekin monomorphic'dan sekinroq.

11. Prototypal va classical OOP farqi nima? [Middle+]

Javob
// Classical OOP (Java, C#):
// Class = blueprint (shablon). Instance = nusxa.
// Class yaratilgandan keyin o'zgarmaydi.

// Prototypal OOP (JavaScript):
// Object = prototype (tirik ob'ekt). Instance = prototype dan meros.
// Istalgan vaqtda o'zgartirish mumkin.

const animal = {
  speak() { return `${this.name} gapirdi`; }
};

// Object.create — ob'ektdan ob'ektga bevosita meros:
const dog = Object.create(animal);
dog.name = "Rex";
dog.bark = function() { return "Hav!"; };

console.log(dog.speak()); // "Rex gapirdi" — animal dan meros
console.log(dog.bark());  // "Hav!" — o'ziniki

// Runtime da prototype o'zgartirish:
animal.eat = function() { return `${this.name} yeyapti`; };
console.log(dog.eat()); // "Rex yeyapti" — darhol ishlaydi!
// Classical OOP da bu MUMKIN EMAS

// ES6 class — prototypal OOP ustiga syntactic sugar:
class Animal {
  constructor(name) { this.name = name; }
  speak() { return `${this.name} gapirdi`; }
}
// Ichida xuddi shu prototype mexanizmi ishlaydi
typeof Animal; // "function" — class emas!
Classical Prototypal (JS)
Blueprint Class (abstract shablon) Prototype (tirik ob'ekt)
Meros Class → class Object → object
Runtime o'zgartirish Yo'q Ha
Abstract/Interface Til darajasida Pattern bilan

12. Mixin nima va nima uchun JS da ishlatiladi? [Middle+]

Javob

Mixin — bir nechta manba'dan xatti-harakatlarni aralashtirib olish usuli. JS da multiple inheritance yo'q (extends faqat bitta class dan). Mixin bu muammoni yechadi:

// Mixin'lar — funksiya sifatida:
const Serializable = (Base) => class extends Base {
  toJSON() {
    return JSON.stringify(this);
  }
  static fromJSON(json) {
    return Object.assign(new this(), JSON.parse(json));
  }
};

const Validatable = (Base) => class extends Base {
  validate() {
    for (const [key, value] of Object.entries(this)) {
      if (value === null || value === undefined) {
        throw new Error(`${key} bo'sh bo'lmasligi kerak`);
      }
    }
    return true;
  }
};

const Timestamped = (Base) => class extends Base {
  constructor(...args) {
    super(...args);
    this.createdAt = new Date();
    this.updatedAt = new Date();
  }
  touch() { this.updatedAt = new Date(); }
};

// Kerakli mixin'larni "aralashtirib" olish:
class User extends Timestamped(Validatable(Serializable(Object))) {
  constructor(name, email) {
    super();
    this.name = name;
    this.email = email;
  }
}

const user = new User("Ali", "ali@mail.com");
user.validate();           // ✅ Validatable dan
console.log(user.toJSON()); // Serializable dan
user.touch();              // Timestamped dan

Prototype chain: user → User.prototype → Timestamped → Validatable → Serializable → Object.prototype

13. OOP va Functional Programming (FP) — qachon qaysi birini tanlaysiz? [Senior]

Javob

JS multi-paradigm — ikkalasini ham qo'llab-quvvatlaydi. Tanlash kontekstga bog'liq:

// OOP yaxshi ishlaydi — domain modeling:
class Order {
  #items = [];
  #status = "pending";

  addItem(item) { this.#items.push(item); }
  submit() {
    if (this.#items.length === 0) throw new Error("Bo'sh buyurtma");
    this.#status = "submitted";
  }
  get total() { return this.#items.reduce((s, i) => s + i.price, 0); }
}

// FP yaxshi ishlaydi — data transformation:
const processOrders = (orders) =>
  orders
    .filter(o => o.status === "submitted")
    .map(o => ({ ...o, tax: o.total * 0.12 }))
    .sort((a, b) => b.total - a.total);
Shart OOP FP
Murakkab domain (e-commerce, game)
State boshqarish (UI, session)
Data transformation, pipeline
Pure, testable logika
API/service layer
Utility funksiyalar

Amalda: React = FP (functional components) + OOP (class services). Node.js = OOP (controllers, models) + FP (middleware, data processing).

Deep Dive:

ECMAScript spec o'zi multi-paradigm dizayn qilingan: function'lar first-class object ([[Call]] internal method), class syntactic sugar, Object.create prototypal delegation. V8 engine ikki paradigmani ham bir xil samarali optimize qiladi — pure function'lar TurboFan'da inline bo'ladi, class method'lar prototype sharing orqali memory tejaydi. JavaScript'ning duck typing xususiyati FP va OOP ni erkin aralashtirishga imkon beradi — TypeScript'ning structural typing'i ham shu falsafani davom ettiradi.

14. Delegation pattern nima va inheritance dan qanday farqi bor? [Middle+]

Javob

Delegation — ob'ekt o'zi bajara olmaydigan ishni boshqa ob'ektga topshiradi (forward qiladi). Inheritance'dan farqi — bu "is-a" munosabat emas, balki "handles-by-forwarding".

// Inheritance — Dog IS an Animal:
class Animal {
  eat() { return "yeyapman"; }
}
class Dog extends Animal {
  bark() { return "hav!"; }
}
// Dog.prototype.__proto__ === Animal.prototype
// Method Dog ichida — prototype chain orqali topiladi

// Delegation — UserService DELEGATES to Logger:
class Logger {
  log(msg) { console.log(`[LOG] ${msg}`); }
}

class UserService {
  #logger;
  constructor(logger) { this.#logger = logger; }

  createUser(name) {
    // O'zi log qilishni bilmaydi — Logger'ga TOPSHIRADI:
    this.#logger.log(`User yaratildi: ${name}`);
    return { name };
  }
}
Inheritance Delegation
Munosabat is-a uses / forwards-to
Bog'liqlik Qattiq (compile-time) Sust (runtime o'zgaradi)
Almashtirishlik Qiyin Oson (dependency injection)
JS'da extends / prototype Forwarding / composition

OLOO (Kyle Simpson) — JS prototypal delegation:

const Validator = {
  validate() {
    return this.value != null && this.value !== "";
  }
};

const EmailField = Object.create(Validator);
EmailField.init = function(value) {
  this.value = value;
  return this;
};
EmailField.isValidEmail = function() {
  return this.validate() && this.value.includes("@");
};

const field = Object.create(EmailField).init("ali@mail.com");
console.log(field.isValidEmail()); // true
// field → EmailField → Validator — delegation chain

15. Law of Demeter nima? Quyidagi kodda qanday buzilgan? [Middle+]

Javob

Law of Demeter (LoD) — ob'ekt faqat yaqin do'stlari bilan gaplashishi kerak. "Begonalar" ning ichki tuzilishiga kirmaslik kerak. Yuqoridagi kodda order → customer → address → city — 3 daraja chuqur, bu "train wreck" deyiladi.

// ❌ Muammo: address strukturasi o'zgarsa — SHU KOD SINADI
// Masalan: address → location ga qayta nomlansa, yoki address nested bo'lsa

// ✅ Law of Demeter — har bir ob'ekt o'z ma'lumotini o'zi beradi:
class Address {
  #city;
  constructor(city) { this.#city = city; }
  getCity() { return this.#city; }
}

class Customer {
  #address;
  #membership;
  constructor(address, membership) {
    this.#address = address;
    this.#membership = membership;
  }
  getCity() { return this.#address.getCity(); }    // delegatsiya
  isGold() { return this.#membership.tier === "gold"; } // delegatsiya
}

class Order {
  #customer;
  #total;
  constructor(customer, total) {
    this.#customer = customer;
    this.#total = total;
  }

  getShippingCity() {
    return this.#customer.getCity(); // faqat 1 daraja — yaqin do'st
  }

  applyDiscount() {
    if (this.#customer.isGold()) { // faqat 1 daraja
      this.#total *= 0.9;
    }
  }
}

Qoida: Agar method ichida 2+ ta dot (.) zanjiri boshqa ob'ektlarga kirayotgan bo'lsa — LoD buzilayotgan bo'lishi mumkin. (builder.setA().setB() bundan mustasno — bu method chaining, bitta ob'ekt.)

16. Tell Don't Ask principle nima? Misol bilan tushuntiring. [Middle]

Javob

Tell Don't Ask — ob'ektdan ma'lumot so'ramang va uning o'rniga qaror qabul qilmang. Buning o'rniga ob'ektga nima qilish kerakligini ayting — u o'zi qaror qilsin.

// ❌ Ask — so'rab, keyin tashqarida qaror qilish (procedural):
function shipOrder(order) {
  const status = order.getStatus();
  const items = order.getItems();
  if (status === "paid" && items.length > 0) {
    const weight = items.reduce((s, i) => s + i.weight, 0);
    if (weight > 30) {
      order.setShipping("freight");
    } else {
      order.setShipping("standard");
    }
    order.setStatus("shipped");
  }
}
// Business logika TASHQARIDA — Order o'zi haqida hech narsa bilmaydi

// ✅ Tell — ob'ektga aytish:
class Order {
  #status;
  #items;
  #shipping;

  ship() {
    if (this.#status !== "paid" || this.#items.length === 0) {
      throw new Error("Buyurtma jo'natishga tayyor emas");
    }
    this.#shipping = this.#totalWeight > 30 ? "freight" : "standard";
    this.#status = "shipped";
  }

  // Private getter (ES2022+) — private field + private method bilan birga qabul qilingan
  get #totalWeight() {
    return this.#items.reduce((s, i) => s + i.weight, 0);
  }
}

order.ship(); // ✅ Tell — nima qilishni aytdik, qanday qilishni Order o'zi biladi

Afzalliklari:

  • Business logika bitta joyda — class ichida
  • Encapsulation saqlanadi — ichki state tashqariga chiqmaydi
  • Qaror logikasi takrorlanmaydi (DRY)

17. Coupling va Cohesion nima? Yaxshi dizayn qanday bo'ladi? [Middle]

Javob

Coupling — modullar o'rtasidagi bog'liqlik darajasi. Cohesion — modul ichidagi elementlarning bir-biriga aloqadorligi.

Yaxshi dizayn = Loose Coupling + High Cohesion.

// ❌ Tight Coupling + Low Cohesion:
class UserManager {
  createUser(name) {
    const id = db.query("INSERT INTO users..."); // ❌ DB ga qattiq bog'liq
    smtp.send(`user-${id}@app.com`, "Welcome!"); // ❌ SMTP ga qattiq bog'liq
    redis.set(`session:${id}`, "{}");             // ❌ Redis ga qattiq bog'liq
    // Low cohesion: user + email + session = 3 xil soha
  }
}

// ✅ Loose Coupling + High Cohesion:
class UserService {
  // High cohesion: faqat user operatsiyalari
  #repo; #events;

  constructor(repo, events) { // Loose coupling: dependency injection
    this.#repo = repo;
    this.#events = events;
  }

  createUser(name) {
    const user = this.#repo.save({ name });
    this.#events.emit("user:created", user); // qolganlari event handler qiladi
    return user;
  }
}
Yomon Yaxshi
Coupling Tight — to'g'ridan-to'g'ri new, global, ichki field Loose — interface, DI, events
Cohesion Low — aralash vazifalar bir class da High — bir soha, bir class

18. Symbol.hasInstance qanday ishlaydi? Custom instanceof yozing. [Senior]

Javob

instanceof operator ichida Symbol.hasInstance static method chaqiriladi. Uni override qilib, instanceof xatti-harakatini butunlay o'zgartirish mumkin:

// ✅ Interface tekshiruvchi — duck typing bilan birga:
class Stringable {
  static [Symbol.hasInstance](instance) {
    return instance != null && typeof instance.toString === "function"
      && instance.toString !== Object.prototype.toString;
  }
}

class User {
  constructor(name) { this.name = name; }
  toString() { return `User: ${this.name}`; }
}

console.log(new User("Ali") instanceof Stringable); // true — custom toString bor
console.log("hello" instanceof Stringable);          // true — String.toString
console.log({} instanceof Stringable);               // false — default toString
console.log(42 instanceof Stringable);               // true — Number.prototype.toString !== Object.prototype.toString

// ✅ Array-like tekshirish:
class ArrayLike {
  static [Symbol.hasInstance](instance) {
    return instance != null
      && typeof instance.length === "number"
      && Number.isInteger(instance.length)
      && instance.length >= 0;
  }
}

console.log([1, 2] instanceof ArrayLike);       // true
console.log("hello" instanceof ArrayLike);      // true (length = 5)
console.log({ length: 3 } instanceof ArrayLike); // true
console.log({ length: -1 } instanceof ArrayLike); // false

Under the hood: x instanceof Foo → engine Foo[Symbol.hasInstance](x) ni chaqiradi. Default implementatsiya prototype chain bo'ylab yuradi, lekin static method bilan butunlay o'zgartirishingiz mumkin.

Deep Dive:

Spec'da instanceof operatori InstanceofOperator(V, target) abstract operation'ni chaqiradi. Birinchi qadam: target[@@hasInstance] bor-yo'qligini tekshiradi (GetMethod). Agar bor — uni chaqiradi va natijani ToBoolean qiladi. Agar yo'q — OrdinaryHasInstance(target, V) chaqiriladi, bu prototype chain'ni iteratsiya qiladi. Function.prototype[@@hasInstance] default implementatsiya sifatida barcha funksiyalarda mavjud va OrdinaryHasInstance ni chaqiradi.

19. Proxy bilan OOP encapsulation/validation qanday qilinadi? [Senior]

Javob

Proxy ob'ektning fundamental operatsiyalarini (get, set, delete) tutib oladi. Bu OOP da runtime validation, access control va observable pattern'ni amalga oshiradi:

// ✅ Typed object — runtime type enforcement:
function createTyped(schema) {
  return new Proxy({}, {
    set(target, prop, value) {
      if (!(prop in schema)) {
        throw new Error(`Noma'lum property: ${prop}`);
      }
      const expectedType = schema[prop];
      if (typeof value !== expectedType) {
        throw new TypeError(
          `${prop}: ${expectedType} kutildi, ${typeof value} berildi`
        );
      }
      target[prop] = value;
      return true;
    }
  });
}

const user = createTyped({
  name: "string",
  age: "number",
  active: "boolean"
});

user.name = "Ali";       // ✅
user.age = 25;           // ✅
// user.age = "yigirma"; // ❌ TypeError
// user.role = "admin";  // ❌ Error: noma'lum property

Proxy afzalliklari OOP da:

  • Runtime validation — compile-time emas, amalda ishlaydi
  • Transparent — client kodi o'zgarmaydi, proxy "yashirin"
  • Flexible — istalgan vaqtda qoidalar o'zgaradi
  • Cross-cutting concerns — logging, caching, access control

Batafsil:23-proxy-reflect.md

Deep Dive:

Spec'da Proxy object 13 ta internal method'ni ([[Get]], [[Set]], [[Has]], [[Delete]] va boshqalar) intercept qilishi mumkin — bu MakeBasicObject o'rniga ProxyCreate orqali yaratiladi. Har bir trap chaqirilganda spec invariant'lar tekshiradi — masalan, agar target'da configurable: false, writable: false property bo'lsa, [[Get]] trap boshqa qiymat qaytarsa TypeError tashlaydi. Bu Proxy'ning fundamental object semantic'larini buzishini oldini oladi.


Amaliy savollar (Coding Challenges)

1. Quyidagi kodning output'ini ayting [Middle+]

Savol:

class Base {
  constructor() {
    this.init();
  }
  init() {
    this.type = "base";
  }
}

class Child extends Base {
  count = 0;

  init() {
    this.type = "child";
    this.count = 10;
  }

  getCount() {
    return this.count;
  }
}

const c = new Child();
console.log(c.type);
console.log(c.getCount());
Javob
"child"
0
// Qadam-baqadam:
// 1. new Child() → Child constructor yo'q, Base constructor chaqiriladi
// 2. Base constructor: this.init() → this = Child instance
//    → Child.init() chaqiriladi (polymorphism — override!)
// 3. Child.init(): this.type = "child", this.count = 10
// 4. LEKIN! — class fields (= 0) constructor body dan KEYIN EMAS
//    Aslida class fields Base constructor DAN KEYIN initialize bo'ladi
//    Ya'ni: super() → base constructor → KEYIN child fields initialize
//
// Tartib:
// a) Base constructor ishlaydi → this.init() → Child.init()
//    → this.type = "child", this.count = 10
// b) Child class fields initialize bo'ladi → count = 0 (qayta yozildi!)
//
// Natija: type = "child", count = 0

// Sabab: Spec algoritmi:
//   1. Default derived constructor: super(...args)
//   2. Base constructor ishlaydi (this.init() → Child.init() dispatch)
//   3. Base constructor return → InitializeInstanceElements(this, Child)
//      bu bosqichda class field initializer'lar source order'da ishlaydi:
//      this.count = 0  ← 10 ni OVERRIDE qildi!
//   4. Child constructor body qolgani (bizda yo'q)
// Natija: type="child" (Base/Child.init chaqirilgan), count=0 (class field keyin)

Xulosa: Class field initializer'lar super() dan keyin, lekin child constructor body'dan oldin InitializeInstanceElements spec operation orqali ishlaydi — bu ko'pchilikni yanglishtiradigan nuance. Qoida: parent constructor'da virtual method chaqirmang — agar chaqirsangiz, child'ning class field'lari hali initialize qilinmagan bo'lishi mumkin.

2. Quyidagi kodda muammo nima? Qanday tuzatasiz? [Middle+]

Savol:

class Bird {
  fly() {
    return `${this.name} uchyapti`;
  }
}

class Eagle extends Bird {
  constructor() { super(); this.name = "Eagle"; }
}

class Penguin extends Bird {
  constructor() { super(); this.name = "Penguin"; }
  fly() {
    throw new Error("Penguinlar ucha olmaydi");
  }
}

function releaseBirds(birds) {
  return birds.map(b => b.fly());
}

releaseBirds([new Eagle(), new Penguin()]);
Javob

Muammo — Liskov Substitution Principle (LSP) buzilgan. Penguin Bird o'rnida ishlatilganda fly() error tashlaydi — ya'ni parent shartnomasi buzildi.

// ✅ To'g'ri dizayn — hierarxiyani qayta qurish:
class Bird {
  constructor(name) { this.name = name; }
  move() {
    throw new Error(`${this.constructor.name}.move() implement qilinmagan`);
  }
}

class FlyingBird extends Bird {
  move() { return `${this.name} uchyapti`; }
  fly()  { return this.move(); }
}

class SwimmingBird extends Bird {
  move() { return `${this.name} suzyapti`; }
  swim() { return this.move(); }
}

class Eagle extends FlyingBird {
  constructor() { super("Eagle"); }
}

class Penguin extends SwimmingBird {
  constructor() { super("Penguin"); }
}

function releaseBirds(birds) {
  return birds.map(b => b.move()); // move() hammada bor — xavfsiz
}

releaseBirds([new Eagle(), new Penguin()]);
// ["Eagle uchyapti", "Penguin suzyapti"] ✅

3. EventEmitter'ni OOP printsiplari bilan implement qiling [Senior]

Savol: EventEmitter class yarating — on, off, emit, once method'lari bilan. Encapsulation va polymorphism ishlating.

Javob
class EventEmitter {
  #listeners = new Map();

  on(event, callback) {
    if (!this.#listeners.has(event)) {
      this.#listeners.set(event, []);
    }
    this.#listeners.get(event).push(callback);
    return this; // chaining uchun
  }

  off(event, callback) {
    const callbacks = this.#listeners.get(event);
    if (!callbacks) return this;
    const index = callbacks.indexOf(callback);
    if (index !== -1) callbacks.splice(index, 1);
    return this;
  }

  emit(event, ...args) {
    const callbacks = this.#listeners.get(event);
    if (!callbacks) return false;
    for (const cb of [...callbacks]) { // copy — once o'chirish xavfsiz
      cb.apply(this, args);
    }
    return true;
  }

  once(event, callback) {
    const wrapper = (...args) => {
      this.off(event, wrapper);
      callback.apply(this, args);
    };
    return this.on(event, wrapper);
  }
}

// Test:
const emitter = new EventEmitter();
const handler = (msg) => console.log(`Got: ${msg}`);

emitter.on("data", handler);
emitter.once("connect", () => console.log("Connected!"));

emitter.emit("connect"); // "Connected!"
emitter.emit("connect"); // (hech narsa — once!)
emitter.emit("data", "hello"); // "Got: hello"

emitter.off("data", handler);
emitter.emit("data", "world"); // (hech narsa — off qilindi)

OOP printsiplari:

  • Encapsulation: #listeners private — tashqaridan manipulate qilib bo'lmaydi
  • Polymorphism: once ichida on va off ni qayta ishlatadi
  • SRP: Faqat event boshqarish — boshqa vazifa yo'q

Deep Dive:

Node.js'ning built-in EventEmitter ichida _events property Object.create(null) bilan yaratiladi — prototype pollution'dan himoya. Listener'lar soni _maxListeners (default 10) bilan cheklanadi — oshsa MaxListenersExceededWarning beriladi (memory leak oldini olish). once implementatsiyasida Node.js onceWrapper funksiyasiga listener property qo'shadi — removeListener wrapper emas, original callback'ni topishi uchun.

4. Method Chaining (Fluent Interface) nima? Implement qiling. [Middle]

Javob

Method chaining — har bir method this ni qaytaradi, shunda bir necha method'ni zanjir shaklida chaqirish mumkin. Bu pattern jQuery, Lodash, Express, va ko'plab JS kutubxonalarida ishlatiladi.

class FormValidator {
  #rules = {};
  #errors = [];
  #data;

  setData(data) {
    this.#data = data;
    this.#errors = [];
    return this; // ← this qaytarish = chaining mumkin
  }

  required(field) {
    if (!this.#data[field]) {
      this.#errors.push(`${field} majburiy`);
    }
    return this;
  }

  minLength(field, min) {
    if (this.#data[field]?.length < min) {
      this.#errors.push(`${field} kamida ${min} ta belgi`);
    }
    return this;
  }

  isEmail(field) {
    if (this.#data[field] && !this.#data[field].includes("@")) {
      this.#errors.push(`${field} noto'g'ri email`);
    }
    return this;
  }

  validate() {
    return {
      isValid: this.#errors.length === 0,
      errors: [...this.#errors]
    };
  }
}

// Fluent interface — zanjir:
const result = new FormValidator()
  .setData({ name: "Al", email: "invalid", age: "" })
  .required("name")
  .minLength("name", 3)
  .required("email")
  .isEmail("email")
  .required("age")
  .validate();

console.log(result);
// { isValid: false, errors: ["name kamida 3 ta belgi", "email noto'g'ri email", "age majburiy"] }

Muhim: return this — mutable chaining (original o'zgaradi). Immutable chaining uchun return new ClassName(...) ishlatiladi (masalan, Array.map(), String.trim()).