Series: JavaScript Basics Lesson 17

this

this nima, V8 ichida qanday ishlaydi, 4 ta qoida (implicit, losing, explicit, new), arrow function ning this mexanizmi.

17-dars

this

Ko'pchilik this ni "o'zini o'zi ko'rsatadi" deb o'ylaydi. Bu noto'g'ri.

this — funksiya chaqirilgan paytdagi kontekst. U funksiya qayerda yozilganiga emas, qayerdan chaqirilganiga qarab belgilanadi.

Bu — Lexical Scope ning teskarisi:

Qayerga qaraydi Qachon belgilanadi
Lexical Scope Yozilgan joy Parse vaqtida
this Chaqirilgan joy Runtime da, har chaqiruvda

V8 ichida this qanday ishlaydi

Har bir Execution Context ichida ThisBinding degan slot bor:

Execution Context {
  LexicalEnvironment: { ... }
  ThisBinding: ???   ← this shu yerda saqlanadi
}

Funksiya chaqirilganda V8 qanday chaqirilganiga qarab ThisBinding ni belgilaydi. Bu qoidalar qat'iy — 4 ta asosiy holat bor.

4 ta qoida

Qoida 1 — Implicit Binding (metod sifatida chaqirilganda)

const user = {
  name: 'Ali',
  greet() {
    console.log(this.name);
  }
};

user.greet(); // 'Ali'

Qoida: Funksiya nuqta orqali chaqirilsa — nuqtadan chap tomondagi obyekt this bo'ladi.

user.greet()
↑
shu obyekt → this = user

V8 ichida:

Execution Context: greet
ThisBinding: user   ← nuqtadan chapni oldi

Qoida 2 — Implicit yo'qolishi (Losing this)

Bu eng ko'p uchraydigan xato:

const user = {
  name: 'Ali',
  greet() {
    console.log(this.name);
  }
};

const fn = user.greet; // metodni olib chiqdik
fn(); // undefined

Nima bo'ldi?

user.greet — funksiyaning o'zini oldik, lekin user bilan bog'liqlikni uzib oldik. Endi fn() chaqirilganda nuqtadan chap tomonda hech narsa yo'q:

fn()         → this = global (sloppy mode) / undefined (strict mode)
user.greet() → this = user

Yana bir klassik holat — callback:

const user = {
  name: 'Ali',
  greet() {
    setTimeout(function() {
      console.log(this.name); // undefined
    }, 100);
  }
};

user.greet();

setTimeout ga berilgan funksiya — oddiy funksiya sifatida chaqiriladi, Context yo'q, this yo'qoladi.


Qoida 3 — Explicit Binding (call, apply, bind)

this ni qo'lda belgilash:

function greet() {
  console.log(this.name);
}

const user = { name: 'Ali' };

greet.call(user);  // 'Ali' — darhol chaqiradi
greet.apply(user); // 'Ali' — darhol chaqiradi
const boundGreet = greet.bind(user);
boundGreet();      // 'Ali' — yangi funksiya qaytaradi

V8 ichida call/apply chaqirilganda:

Execution Context: greet
ThisBinding: user   ← birinchi argument dan olindi

Qoida 4 — new Binding

function Person(name) {
  this.name = name;
  console.log(this); // Person { name: 'Ali' }
}

const p = new Person('Ali');

new bilan chaqirilganda V8:

  1. Bo'sh obyekt yaratadi: {}
  2. ThisBinding = shu yangi obyekt
  3. Konstruktor ishlaydi — this.name = 'Ali' → yangi obyektga yoziladi
  4. Yangi obyektni qaytaradi

Qoidalar prioriteti

Bir vaqtda bir nechta qoida to'qnashsa — qaysi biri ustun?

new  >  bind/call/apply  >  nuqta (implicit)  >  global/undefined
function foo() { console.log(this.x); }
const obj = { x: 10 };
const bound = foo.bind(obj);

new bound(); // undefined — new, bind dan ustun
bound();     // 10 — bind ishlaydi

Arrow Function va this — butunlay boshqa mexanizm

Arrow function — o'zining this i yo'q.

Bu shunchaki "boshqacha this" emas — arrow function Execution Context yaratilganda ThisBinding umuman bo'lmaydi. U this ni o'qishga urinsa, Lexical Scope bo'ylab yuqoriga chiqadi va birinchi topgan this ni oladi.

const user = {
  name: 'Ali',
  greet() {
    // Bu oddiy funksiya — this = user

    const inner = () => {
      console.log(this.name); // 'Ali'
      // this yo'q, yuqoriga chiqdi → greet() ning this ini oldi → user
    };

    inner();
  }
};

user.greet(); // 'Ali'

Oddiy funksiya bo'lganda:

const user = {
  name: 'Ali',
  greet() {
    const inner = function() {
      console.log(this.name); // undefined
      // this bor, lekin inner() nuqtasiz chaqirildi → global/undefined
    };

    inner();
  }
};

user.greet(); // undefined

V8 ichida Arrow Function ning this si

Oddiy funksiya:

Execution Context: greet
ThisBinding: user   ← bor, qoida bo'yicha belgilandi
[[Environment]] → Global Execution Context (parent)

Arrow funksiya:

Execution Context: inner (arrow)
[[Environment]]: → greet ning LexicalEnvironment'iga pointer
ThisBinding: —      ← YO'Q, umuman yaratilmadi

inner ichida this o'qilganda V8:

  1. inner ning Execution Context da ThisBinding bormi? — yo'q
  2. [[Environment]] orqali yuqoriga: greet ning Execution Context da ThisBinding bormi? — bor, user
  3. this = user

Bu — this ning lexical ishlashi. Arrow funksiya this ni ham Lexical Scope orqali topadi.


Amaliy — qayerda arrow, qayerda oddiy funksiya

Arrow funksiya kerak — callback ichida this ni saqlash:

class Timer {
  constructor() {
    this.seconds = 0;
  }

  start() {
    setInterval(() => {
      this.seconds++; // this = Timer instance — to'g'ri
      console.log(this.seconds);
    }, 1000);
  }
}

const t = new Timer();
t.start(); // 1, 2, 3...

Oddiy funksiya bo'lganda this.seconds undefined bo'lardi.

Arrow funksiya KERAK EMAS — metod sifatida:

const user = {
  name: 'Ali',
  greet: () => {
    console.log(this.name); // undefined!
  }
};

user.greet(); // undefined

Nima bo'ldi? Arrow funksiya yaratilganda [[Environment]] ga qaraydi — bu yerda user obyektining ichida yozilyapti. Lekin obyekt {} yangi scope yaratmaydi — faqat funksiyalar va bloklar scope yaratadi. Shuning uchun arrow funksiya global scope ning this ini oladi.

user = { greet: arrow }
  Arrow yaratilganda atrofidagi scope → global scope
  this → global (browser da window, strict da undefined)

Umumiy qoida — qachon arrow, qachon oddiy

Holat Qaysi funksiya Sabab
Obyekt metodi Oddiy this = obyekt bo'lishi kerak
Callback (setTimeout, map, filter) Arrow Tashqi this ni saqlash
Constructor (new bilan) Oddiy Arrow new bilan ishlamaydi
Event handler (DOM) Oddiy yoki arrow — vaziyatga qarab Oddiy → this = element; Arrow → tashqi this
Class metodi ichidagi callback Arrow Class ning this ini saqlash