Funksiyalar bilan chuqur ishlash

Dekoratorlar va uzatish, call/apply

JavaScript funksiyalar bilan ishlashda ajoyib moslashuvchanlik beradi. Ularni uzatish, o'ram (wrapper) bilan qoplash mumkin. Ushbu darsda dekoratorlar va kontekstni uzatuvchi call/apply metodlarini o'rganamiz.

Dekorator nima?

Dekorator โ€” bu funksiyani argument sifatida oladigan va uning xatti-harakatini o'zgartiruvchi yangi funksiyani qaytaradigan funksiya. Asl funksiya o'zgarmaydi โ€” uning atrofiga qo'shimcha imkoniyat "o'raladi".

Masalan, sekin ishlaydigan funksiyaning natijalarini keshlab (eslab) qo'yadigan dekorator yozamiz:

kesh-dekorator.js
// natija shu yerda chiqadi
Dekoratorning go'zalligi โ€” asl sekinHisob funksiyasi o'zgarmadi. Biz uni tashqaridan "kesh" imkoniyati bilan qopladik. Bu โ€” vazifalarni ajratish (separation of concerns) tamoyilining yaxshi namunasi.

Kontekst muammosi

Yuqoridagi keshlovchi oddiy funksiyalar uchun ishlaydi. Ammo obyekt metodlari bilan muammo chiqadi. Metod ichida this ishlatilsa, dekorator uni func(x) deb chaqirganda this yo'qoladi:

let ishchi = {
  narx: 1000,
  sekinHisob(x) {
    return x * this.narx; // this KERAK
  }
};

// keshlovchi ichida func(x) deb chaqirilsa, this yo'qoladi
// va "Cannot read property 'narx' of undefined" xatoligi chiqadi

Buni hal qilish uchun bizga thisni to'g'ri uzatish yo'li kerak. Aynan shu yerda call yordamga keladi.

func.call โ€” kontekstni aniq uzatish

func.call(context, arg1, arg2, ...) funksiyani chaqiradi, lekin thisni birinchi argument sifatida aniq belgilaydi:

call-asosiy.js
// natija shu yerda chiqadi

func.call(obj) โ€” bu deyarli obj.func() bilan bir xil, lekin thisni aniq belgilaydi. Argumentlarni ham ketma-ket uzatish mumkin:

call-argumentlar.js
// natija shu yerda chiqadi

func.apply โ€” argumentlarni massiv bilan

func.apply call bilan deyarli bir xil, lekin argumentlarni massiv ko'rinishida qabul qiladi:

func.call(context, arg1, arg2, arg3);
func.apply(context, [arg1, arg2, arg3]); // xuddi shu natija
apply-massiv.js
// natija shu yerda chiqadi
Zamonaviy JavaScript'da spread sintaksisi (func.call(ctx, ...args)) applyga muqobil hisoblanadi. Ammo apply ba'zan bir oz tezroq (dvigatel uni yaxshi optimallashtiradi) va eski kodda ko'p uchraydi.

Chaqiruvni uzatish (call forwarding)

Dekoratorlarda ko'pincha barcha argumentlarni va kontekstni asl funksiyaga to'liq uzatish kerak bo'ladi. Buning uchun func.apply(this, arguments) naqshi ishlatiladi:

call-forwarding.js
// natija shu yerda chiqadi

func.apply(this, arguments) โ€” bu chaqiruvni uzatish (call forwarding). U tashqi o'ramning thisini va barcha argumentlarini asl funksiyaga to'liq "o'tkazib yuboradi", go'yo o'ram umuman bo'lmagandek.

arguments โ€” bu massivsimon obyekt (array-like), haqiqiy massiv emas. Uni applyga to'g'ridan-to'g'ri berish mumkin, lekin massiv metodlarini (masalan .map) ishlatish uchun Array.from(arguments) yoki [...arguments] orqali massivga o'girish kerak.

Metodni "qarzga olish" (method borrowing)

call/applyning yana bir foydali qo'llanishi โ€” bir obyektning metodini boshqa obyektda ishlatish. Masalan, arguments massivsimon obyektida massivning join metodini ishlatish:

method-borrowing.js
// natija shu yerda chiqadi

Xulosa