Object — key-value juftliklardan iborat ma'lumot tuzilmasi. Bu bo'limda property descriptors, immutability, shallow/deep copy, enumeration va zamonaviy object metodlari haqida savollar.
Javob
JavaScript da object yaratishning to'rtta asosiy usuli:
// 1. Object Literal — eng ko'p ishlatiladigan
const user1 = { name: "Alice", age: 25 };
// 2. Constructor Function — new bilan
function User(name) { this.name = name; }
const user2 = new User("Alice");
// 3. Object.create() — berilgan prototype bilan
const proto = { greet() { return "Hi"; } };
const user3 = Object.create(proto);
// 4. Class (ES6) — constructor function'ning syntactic sugar'i
class UserClass {
constructor(name) { this.name = name; }
}
const user4 = new UserClass("Alice");| Usul | Prototype | Use case |
|---|---|---|
Literal {} |
Object.prototype |
Oddiy object'lar |
| Constructor | Constructor.prototype |
ES5 da OOP |
Object.create(proto) |
berilgan proto | Custom prototype chain |
| Class | Class.prototype |
Zamonaviy OOP |
Javob
Har bir object property'si meta-ma'lumotlarga ega — bu property descriptor. Uch xil flag bor:
writable— qiymat o'zgartirilishi mumkinmienumerable—for...in,Object.keys()da ko'rinadimiconfigurable— descriptor qayta sozlanishi yoki property o'chirilishi mumkinmi
const obj = { name: "Alice" };
// Default descriptor (literal bilan yaratilganda):
console.log(Object.getOwnPropertyDescriptor(obj, "name"));
// { value: "Alice", writable: true, enumerable: true, configurable: true }
// Custom descriptor:
Object.defineProperty(obj, "id", {
value: 1,
writable: false, // o'zgarmas
enumerable: false, // ko'rinmas
configurable: false // qaytib bo'lmas
});
obj.id = 999; // ❌ silent fail (strict: TypeError)
console.log(Object.keys(obj)); // ["name"] — id ko'rinmaydi
delete obj.id; // ❌ o'chirilmaydiObject.defineProperty bilan yaratilgan property'larda default qiymatlar false — oddiy assign bilan yaratilganda esa true.
Javob
| Yangi property | O'zgartirish | O'chirish | |
|---|---|---|---|
preventExtensions |
❌ | ✅ | ✅ |
seal |
❌ | ✅ | ❌ |
freeze |
❌ | ❌ | ❌ |
// freeze — eng kuchli
const obj = Object.freeze({ a: 1, nested: { b: 2 } });
obj.a = 99; // ❌ o'zgarmaydi
obj.nested.b = 99; // ✅ ISHLAYDI — shallow freeze!Barchasi shallow — nested object'larga ta'sir qilmaydi. Deep freeze uchun recursive funksiya kerak:
function deepFreeze(obj) {
Object.freeze(obj);
Object.getOwnPropertyNames(obj).forEach(prop => {
const val = obj[prop];
if (val && typeof val === "object" && !Object.isFrozen(val)) {
deepFreeze(val);
}
});
return obj;
}Javob
Shallow copy — birinchi daraja copy, nested object'lar reference bo'lib qoladi:
const a = { x: 1, inner: { y: 2 } };
const b = { ...a }; // shallow
b.inner.y = 99;
console.log(a.inner.y); // 99 — ❌ original buzildiDeep copy — barcha darajalar to'liq copy:
const c = structuredClone(a); // deep
c.inner.y = 99;
console.log(a.inner.y); // 2 — ✅ original saqlanadi| Usul | Tur | Circular ref | Date/Map/Set | Function |
|---|---|---|---|---|
{ ...obj } / Object.assign |
Shallow | ❌ | ❌ ref | ❌ ref |
JSON.parse(JSON.stringify()) |
Deep | ❌ Error | ❌ yo'qoladi | ❌ yo'qoladi |
structuredClone() (ES2022) |
Deep | ✅ | ✅ | ❌ Error |
| Recursive function | Deep | ✅ (WeakMap) | ✅ |
Zamonaviy kod'da structuredClone eng yaxshi tanlov — function copy kerak bo'lmasa.
Javob
| Metod | Own | Inherited | Non-enumerable | Symbol |
|---|---|---|---|---|
for...in |
✅ | ✅ | ❌ | ❌ |
Object.keys() |
✅ | ❌ | ❌ | ❌ |
Reflect.ownKeys() |
✅ | ❌ | ✅ | ✅ |
const parent = { inherited: true };
const obj = Object.create(parent);
obj.visible = 1;
Object.defineProperty(obj, "hidden", { value: 2, enumerable: false });
obj[Symbol("id")] = 3;
// for...in: "visible", "inherited" (inherited ham!)
// Object.keys: ["visible"]
// Reflect.ownKeys: ["visible", "hidden", Symbol(id)]Zamonaviy kod'da for...in o'rniga Object.keys() / Object.entries() ishlatish tavsiya etiladi — inherited property'lardan kutilmagan xulq bo'lmaydi.
Javob
Object.hasOwn() (ES2022) — property object'ning o'ziga tegishli ekanini tekshiradi. hasOwnProperty ning xavfsiz versiyasi:
// hasOwnProperty muammolari:
const obj = Object.create(null); // prototype yo'q
// obj.hasOwnProperty("key"); // ❌ TypeError — metod mavjud emas
const tricky = { hasOwnProperty: () => false }; // override
tricky.hasOwnProperty("hasOwnProperty"); // false — noto'g'ri!
// Object.hasOwn — har doim ishlaydi:
Object.hasOwn(obj, "key"); // ✅ ishlaydi — statik metod
Object.hasOwn(tricky, "hasOwnProperty"); // true — to'g'riZamonaviy kod'da doim Object.hasOwn() ishlatish kerak.
Javob
Getter/Setter — property o'qilganda/yozilganda avtomatik chaqiriladigan funksiyalar. Tashqaridan oddiy property, ichida logika:
const user = {
_age: 25,
get age() {
return this._age;
},
set age(value) {
if (value < 0 || value > 150) throw new Error("Invalid age");
this._age = value;
}
};
console.log(user.age); // 25 — getter chaqirildi (funksiya kabi emas!)
user.age = 30; // ✅ setter — validation o'tdi
// user.age = -5; // ❌ Error: Invalid ageUse cases:
- Validation — qiymat yozilganda tekshirish
- Computed properties —
fullName=firstName+lastName - Lazy initialization — birinchi o'qilganda hisoblash, keyingilarida cache
- Backward compatibility — eski property nomini saqlab, ichida yangi logika
Javob
Optional chaining — chuqur nested property'ga xavfsiz murojaat. Agar zanjirdagi biror qism null yoki undefined bo'lsa — TypeError o'rniga undefined qaytaradi:
const user = {
profile: {
address: { city: "NYC" }
}
};
// Optional chaining yo'q — manual tekshirish:
const city1 = user && user.profile && user.profile.address && user.profile.address.city;
// Optional chaining bilan:
const city2 = user?.profile?.address?.city; // "NYC"
const zip = user?.profile?.address?.zip; // undefined (xato emas)
const phone = user?.contact?.phone; // undefined (xato emas)
// Method chaqirish:
user?.profile?.toString?.(); // "[object Object]"
user?.missing?.method?.(); // undefined
// Index access:
const first = user?.items?.[0]; // undefinedQachon ishlatish kerak: tashqi API'dan kelgan data, optional config, nullable type'lar bilan ishlashda.
Qachon ishlatMASLIK kerak: doim mavjud bo'lishi kerak bo'lgan property'lar uchun — ?. xatoni yashiradi, debugging qiyinlashadi.
9. V8 Hidden Class nima va performance'ga qanday ta'sir qiladi? [Senior]
Javob
V8 har bir object uchun Hidden Class (ichki nomi "Map") yaratadi — bu object'ning "shakli" (shape). Bir xil tartibda bir xil property'lar qo'shilgan object'lar bitta Hidden Class'ni share qiladi.
Hidden Class'ning maqsadi — inline caching. Agar V8 bilsa ki ikkita object bir xil shape'da — birinchi object'da property access optimizatsiya qilinsa, ikkinchisida ham shu optimizatsiya ishlaydi.
// ✅ Yaxshi — bir xil tartibda property qo'shish
function createPoint(x, y) {
const p = {};
p.x = x; // Hidden Class: C0 → C1 (x qo'shildi)
p.y = y; // Hidden Class: C1 → C2 (y qo'shildi)
return p;
}
const p1 = createPoint(1, 2); // Shape: C2
const p2 = createPoint(3, 4); // Shape: C2 — SHARE ✅
// ❌ Yomon — turli tartibda property qo'shish
const a = {}; a.x = 1; a.y = 2; // Shape: A
const b = {}; b.y = 2; b.x = 1; // Shape: B — BOSHQA shape!
// V8 inline caching foyda bermaydiPerformance qoidalari:
- Object'larga property'larni bir xil tartibda qo'shing
- Object yaratgandan keyin yangi property qo'shMANG (constructor'da hammasi bo'lsin)
- Property o'chirMANG (
delete) — shape buziladi - Property type'ini o'zgartirMANG (number → string) — deoptimization
Deep Dive:
V8 ichida Hidden Class Map deb ataladi (JavaScript Map data structure bilan aralashtirilmasin — bu butunlay boshqa tushuncha, faqat nomi bir xil). Har bir Map DescriptorArray ga ega — unda property nomlari va ularning xotiradagi offset'lari saqlanadi. delete operator ishlatilganda object Map dan Dictionary Mode (hash table) ga o'tadi — inline cache ishlamay qoladi va hot path'da sezilarli sekinlashuv beradi (aniq farq V8 versiyasi, workload va property count'ga bog'liq). V8 %HasFastProperties(obj) internal function orqali object hali Map-based ekanini tekshirish mumkin (faqat --allow-natives-syntax flag bilan).
Javob
Object.groupBy() (ES2024) — array elementlarini callback natijasi bo'yicha guruhlaydi:
const users = [
{ name: "Alice", role: "admin" },
{ name: "Bob", role: "user" },
{ name: "Charlie", role: "admin" },
{ name: "Diana", role: "user" }
];
const byRole = Object.groupBy(users, user => user.role);
// {
// admin: [{ name: "Alice", role: "admin" }, { name: "Charlie", role: "admin" }],
// user: [{ name: "Bob", role: "user" }, { name: "Diana", role: "user" }]
// }ES2024 dan oldin bu reduce bilan qo'lda qilinardi:
const byRole = users.reduce((groups, user) => {
const key = user.role;
(groups[key] ??= []).push(user);
return groups;
}, {});Map.groupBy() ham bor — key sifatida non-string qiymat kerak bo'lsa (masalan object).
Javob
structuredClone() (ES2022) — browser'ning Structured Clone Algorithm'i asosida deep copy yaratadi:
| Xususiyat | JSON.parse(JSON.stringify()) |
structuredClone() |
|---|---|---|
| Date | ❌ → string | ✅ Date saqlanadi |
| Map/Set | ❌ → {} | ✅ saqlanadi |
| RegExp | ❌ → {} | ✅ saqlanadi |
| undefined | ❌ yo'qoladi | ✅ saqlanadi |
| NaN/Infinity | ❌ → null | ✅ saqlanadi |
| Circular ref | ❌ TypeError | ✅ ishlaydi |
| Function | ❌ yo'qoladi | ❌ DataCloneError |
| Symbol keys | ❌ yo'qoladi | ❌ copy qilmaydi |
| DOM Node | ❌ Error | ❌ Error |
const obj = {
date: new Date(),
set: new Set([1, 2, 3]),
undef: undefined,
nan: NaN
};
obj.self = obj; // circular
const clone = structuredClone(obj);
console.log(clone.date instanceof Date); // true
console.log(clone.set instanceof Set); // true
console.log(clone.self === clone); // true — circular resolvedconst a = { x: 1, y: { z: 2 } };
const b = Object.assign({}, a);
const c = { ...a };
b.x = 10;
c.y.z = 30;
console.log(a.x); // ?
console.log(a.y.z); // ?
console.log(b.y.z); // ?Javob
1
30
30
b.x = 10→a.xo'zgarmaydi (birinchi daraja copy)c.y.z = 30→a.y.zHAM o'zgaradi (shallow —yreference bo'lib qolgan)b.y.z = 30→ b ham c ham a bilan bir xilyobject'ga reference saqlaydi- Barchasi bitta
yobject:a.y === b.y === c.y
const obj = { a: 1 };
Object.defineProperty(obj, "b", {
value: 2,
enumerable: false,
writable: true
});
obj[Symbol("c")] = 3;
console.log(Object.keys(obj)); // ?
console.log(Object.getOwnPropertyNames(obj)); // ?
console.log(Reflect.ownKeys(obj)); // ?
console.log(JSON.stringify(obj)); // ?Javob
Object.keys(obj); // ["a"]
// ✅ Faqat own + enumerable string keys
Object.getOwnPropertyNames(obj); // ["a", "b"]
// ✅ Own string keys (enumerable + non-enumerable), Symbol yo'q
Reflect.ownKeys(obj); // ["a", "b", Symbol(c)]
// ✅ BARCHA own keys: string + Symbol, enumerable + non-enumerable
JSON.stringify(obj); // '{"a":1}'
// ✅ Faqat enumerable string keys, Symbol keys va non-enumerable yo'qJavob
function deepClone(obj, seen = new WeakMap()) {
// Primitive va null
if (obj === null || typeof obj !== "object") return obj;
// Circular reference himoya
if (seen.has(obj)) return seen.get(obj);
// Special types
if (obj instanceof Date) return new Date(obj);
if (obj instanceof RegExp) return new RegExp(obj.source, obj.flags);
if (obj instanceof Map) {
const map = new Map();
seen.set(obj, map);
obj.forEach((v, k) => map.set(deepClone(k, seen), deepClone(v, seen)));
return map;
}
if (obj instanceof Set) {
const set = new Set();
seen.set(obj, set);
obj.forEach(v => set.add(deepClone(v, seen)));
return set;
}
// Array va Object
const clone = Array.isArray(obj) ? [] : {};
seen.set(obj, clone);
for (const key of Reflect.ownKeys(obj)) {
clone[key] = deepClone(obj[key], seen);
}
return clone;
}
// Test:
const original = {
date: new Date(),
nested: { a: [1, 2, { b: 3 }] },
map: new Map([["x", 1]]),
regex: /test/gi
};
original.circular = original; // circular reference
const cloned = deepClone(original);
console.log(cloned.nested.a[2].b); // 3
console.log(cloned.date instanceof Date); // true
console.log(cloned.circular === cloned); // true (circular resolved)
console.log(cloned !== original); // true (alohida object)WeakMap circular reference'ni handle qiladi: object birinchi ko'rilganda seen ga yoziladi, qayta uchrasa — saqlangan clone qaytariladi.
- Prototype chain yo'qoladi —
clonehar doimObject.prototypeyokiArray.prototypebilan yaratiladi. Agaroriginal instanceof CustomClassbo'lgan bo'lsa,cloned instanceof CustomClassfalse bo'ladi. Tuzatish:Object.create(Object.getPrototypeOf(obj))bilan yaratish. - Property descriptor'lar yo'qoladi —
clone[key] = ...oddiy assignment, shuning uchun getter/setter, non-enumerable, non-writable flag'lar yo'qoladi. Tuzatish:Object.definePropertybilan descriptor'larni ham copy qilish. - Function'lar reference bo'lib qoladi — funksiyalar primitive emas, lekin kod ularni shallow copy qiladi. Real clone imkonsiz (closure scope yo'qoladi).
- Built-in'lar —
Error,URL,TypedArray,ArrayBuffer,DataView,Promisekabi ko'p tiplar maxsus handle qilinmagan.
Deep Dive:
structuredClone() ichida brauzerning Structured Clone Algorithm (HTML spec) ishlatiladi — bu postMessage, IndexedDB, va Cache API da ham ishlatiladigan bir xil algoritm. U [[Transfer]] va [[Clone]] internal method'larini chaqiradi. Custom deepClone dan farqi — structuredClone ArrayBuffer transferable object'larni, TypedArray'larni, Error, Blob, File, ImageData kabi ko'p built-in tiplarni ham qo'llab-quvvatlaydi. Lekin structuredClone ham prototype chain'ni saqlamaydi va funksiyalar uchun DataCloneError throw qiladi.
Javob
function deepEqual(a, b) {
if (a === b) return true;
if (a === null || b === null) return false;
if (typeof a !== "object" || typeof b !== "object") return false;
if (a.constructor !== b.constructor) return false;
if (Array.isArray(a)) {
if (a.length !== b.length) return false;
return a.every((item, i) => deepEqual(item, b[i]));
}
if (a instanceof Date) return a.getTime() === b.getTime();
if (a instanceof RegExp) return a.source === b.source && a.flags === b.flags;
if (a instanceof Map) {
if (a.size !== b.size) return false;
for (const [key, val] of a) {
if (!b.has(key) || !deepEqual(val, b.get(key))) return false;
}
return true;
}
if (a instanceof Set) {
if (a.size !== b.size) return false;
for (const val of a) {
if (!b.has(val)) return false;
}
return true;
}
const keysA = Object.keys(a);
const keysB = Object.keys(b);
if (keysA.length !== keysB.length) return false;
return keysA.every(key =>
Object.hasOwn(b, key) && deepEqual(a[key], b[key])
);
}
// Test:
deepEqual({ a: [1, { b: 2 }] }, { a: [1, { b: 2 }] }); // true
deepEqual({ a: 1 }, { a: 2 }); // false
deepEqual(new Date(0), new Date(0)); // true
deepEqual([1, 2], [1, 2, 3]); // falseDeep Dive:
ECMAScript spec'da uchta alohida equality algoritmi bor — ularni aralashtirmaslik muhim:
| Algoritm | Ishlatilish joyi | -0 vs +0 |
NaN vs NaN |
|---|---|---|---|
| IsStrictlyEqual | ===, !== operator'lari |
true |
false |
| SameValue | Object.is, property descriptor invariants |
false |
true |
| SameValueZero | Array.includes, Map.has, Set.has |
true |
true |
Primitive'lar uchun === value equality tekshiradi (not reference), object'lar uchun esa reference identity. Deep equality uchun spec'da alohida operation yo'q — bu userland implementatsiya. Node.js'ning assert.deepStrictEqual ichida innerDeepEqual funksiyasi Object.keys, Object.getOwnPropertySymbols, va getPrototypeOf kombinatsiyasini ishlatib recursive taqqoslash qiladi — va u SameValue semantikasini ishlatadi (NaN === NaN true).
Shuning uchun custom deepEqual yozayotganda primitive taqqoslash uchun odatda Object.is (SameValue) ishlatiladi — NaN va -0/+0 to'g'ri taqqoslanishi uchun.