libs:
- lodash
Hozirga qadar biz faqat this
ni bog'lash haqida gaplashib kelmoqdamiz. Keling, yana bir qadam tashlaymiz.
Biz nafaqat this
ni, balki argumentlarni ham bog'lashimiz mumkin. Bu kamdan-kam hollarda amalga oshiriladi, lekin ba'zida qulay bo'lishi mumkin.
bind
ning to'liq sintaksisi:
let bound = func.bind(context, arg1, arg2, ...);
Bu kontekstni this
va funktsiyalarning boshlang'ich argumentlari sifatida bog'lashga imkon beradi.
Masalan, bizda mul(a, b)
ko'paytma funktsiyasi mavjud:
function mul(a, b) {
return a * b;
}
Uning asosida double
funktsiyasini yaratish uchun bind
dan foydalanamiz:
function mul(a, b) {
return a * b;
}
*!*
let double = mul.bind(null, 2);
*/!*
alert( double(3) ); // = mul(2, 3) = 6
alert( double(4) ); // = mul(2, 4) = 8
alert( double(5) ); // = mul(2, 5) = 10
mul.bind(null, 2)
ga chaqiruv mul
ga o'tuvchi yangi double
funktsiyasini yaratadi, kontekst sifatida null
ni va 2
ni birinchi argument sifatida belgilaydi. Boshqa argumentlar "boricha" o'tkaziladi.
Bu qisman funktsional dastur deb nomlanadi -- biz mavjud bo'lgan parametrlarning bir qismini tuzatish orqali yangi funktsiya yaratamiz.
Iltimos, e'tibor bering, biz bu erda this
ni ishlatmaymiz. Ammo bind
buni talab qiladi, shuning uchun biz null
ga o'xshash narsalarni qo'yishimiz kerak.
Quyidagi koddagi triple
funktsiyasi bu qiymatni uch baravar oshiradi:
function mul(a, b) {
return a * b;
}
*!*
let triple = mul.bind(null, 3);
*/!*
alert( triple(3) ); // = mul(3, 3) = 9
alert( triple(4) ); // = mul(3, 4) = 12
alert( triple(5) ); // = mul(3, 5) = 15
Nima uchun biz odatda qisman funktsiyani yaratamiz?
Buning foydasi shundaki, biz o'qilishi mumkin bo'lgan nom bilan mustaqil funktsiyani yaratishimiz mumkin (double
, triple
). Biz uni ishlata olamiz va har safar birinchi argumentni keltirmaymiz, chunki u "bind" bilan o'rnatiladi.
Boshqa hollarda, qisman dastur juda umumiy funktsiyaga ega bo'lganimizda va qulayligi uchun unchalik universal bo'lmagan variantni xohlaganimizda foydalidir.
Masalan, bizda send(from, to, text)
funksiyasi mavjud. Keyin, user
obyekti ichida uning qisman variantini ishlatishni xohlashimiz mumkin: joriy foydalanuvchidan yuboradigan sendTo(to, text)
.
Agar biz ba'zi argumentlarni tuzatishni xohlasak, lekin this
ni bog'lamasak nima bo'ladi?
bind
bunga yo'l qo'ymaydi. Biz shunchaki kontekstni qoldirib, argumentlarga o'tishimiz mumkin emas.
Yaxshiyamki, faqat argumentlarni bog'lash uchun qisman
funktsiyani osongina amalga oshirish mumkin.
Shunga o'xshash:
*!*
function partial(func, ...argsBound) {
return function(...args) { // (*)
return func.call(this, ...argsBound, ...args);
}
}
*/!*
// Foydalanish:
let user = {
firstName: "John",
say(time, phrase) {
alert(`[${time}] ${this.firstName}: ${phrase}!`);
}
};
// birinchi argumentni tuzatish orqali hozir biron bir narsani aytadigan qisman usulni qo'shing
user.sayNow = partial(user.say, new Date().getHours() + ':' + new Date().getMinutes());
user.sayNow("Salom");
// Shunga o'xshash narsa:
// [10:00] John: Salom!
partial(func[, arg1, arg2...])
chaqiruvining natijasi func
ni quyidagicha chaqiradigan (*)
to'plamidir:
- Xuddi shu
this
mavjud (user.sayNow
uchunuser
ni chaqiring) - Keyin unga
...argsBound
beradi -qisman
chaqiruvdan argumentlar ("10:00"
) - Keyin uni qaytaradi
...args
- o'ramga(wrapper) berilgan argumentlar ("Salom"
)
Buni tarqatish operatori bilan bajarish juda oson, shunday emasmi?
Shuningdek, lodash kutubxonasidan tayyor _.partial dastur mavjud.
Ba'zan odamlar yuqorida aytib o'tilgan qisman funktsiyani "currying" deb nomlangan boshqa narsa bilan aralashtiradilar. Bu yerda funktsiyalar bilan ishlashning yana bir qiziqarli uslubi, biz bu yerda eslatib o'tamiz.
Currying - bu f(a, b, c)
deb chaqiriladigan funktsiyadan f(a)(b)(c)
ga aylantirish. JavaScript-da, biz asl funktsiyani saqlab qolish uchun odatda o'ramni yaratamiz.
Currying funktsiyani chaqirmaydi. Bu shunchaki uni o'zgartiradi.
Keling, ikkita argumentli f
uchun currying-ni bajaradigan yordamchi curry(f)
funktsiyasini yarataylik. Boshqacha qilib aytganda,curry(f)
ikki argumentli f(a, b)
f(a)(b)
ga aylantiradi
*!*
function curry(f) { // curry(f) currying o'zgartiradi
return function(a) {
return function(b) {
return f(a, b);
};
};
}
*/!*
// foydalanish
function sum(a, b) {
return a + b;
}
let carriedSum = curry(sum);
alert( carriedSum(1)(2) ); // 3
Ko'rib turganingizdek, amalga oshirish - bu bir qator o'ramalar.
curry(func)
natijasifunction(a)
o'ralmasini hosil qiladi.- U
sum(1)
kabi chaqirilganda, argument leksik muhitda saqlanadi va yangi o'rashfunction(b)
qaytariladi. - So'ngra
sum(1)(2)
nihoyat2
ni ta'minlaydiganfunction(b)
ni chaqiradi va u chaqiruvni asl ko'p argumentlisum
ga o'tkazadi.
Lodash kutubxonasidan _.curry kabi currying-ni yanada takomillashtirilgan dasturlari yanada murakkabroq narsani amalga oshiradi. Ular barcha argumentlar keltirilganda funktsiyani normal ravishda chaqirishga imkon beradigan o'ramni qaytaradi yoki aks holda qisman qaytaradi.
function curry(f) {
return function(...args) {
// agar args.length == f.length (f qancha argument bo'lsa),
// keyin chaqiruvni f ga o'tkazadi
// aks holda argumentlarni birinchi argument sifatida tuzatadigan qisman funktsiyani qaytaradi
};
}
Imtiyozlarni tushunish uchun, albatta, munosib hayotiy misol kerak.
Kengaytirilgan currying funktsiyani odatdagi va qisman chaqiruv qilish imkoniyatini beradi.
Masalan, biz ma'lumotni formatlaydigan va chiqaradigan log(date, importance, message)
logga yozish funktsiyasiga egamiz. Haqiqiy loyihalarda bunday funktsiyalar, shuningdek, tarmoq orqali loglarni yuborish kabi ko'plab boshqa foydali xususiyatlarga ega:
function log(date, importance, message) {
alert(`[${date.getHours()}:${date.getMinutes()}] [${importance}] ${message}`);
}
Keling, buni curry chiqaylik!
log = _.curry(log);
Shundan so'ng log
hali ham normal ishlaydi:
log(new Date(), "DEBUG", "some debug");
...Ammo, shuningdek, curry shaklida ham chaqirilishi mumkin:
log(new Date())("DEBUG")("some debug"); // log(a)(b)(c)
Bugungi logglar uchun qulaylik funktsiyasini olaylik:
// todayLog will be the partial of log with fixed first argument
let todayLog = log(new Date());
// use it
todayLog("INFO", "message"); // [HH:mm] INFO message
Va bugungi koddagi nosozliklarni tuzatish xabarlari uchun qulaylik vazifasi:
let todayDebug = todayLog("DEBUG");
todayDebug("message"); // [HH:mm] DEBUG message
Shunday qilib:
- Currying keyin biz hech narsani yo'qotmadik:
log
odatdagidek chaqiriladi. - Bugungi logglar kabi qisman funktsiyalarni yaratishga muvaffaq bo'ldik.
Agar tafsilotlarni bilmoqchi bo'lsangiz (majburiy emas!), Mana yuqorida ishlatishimiz mumkin bo'lgan "ilg'or" curry dasturini taklif qilamiz.
Bu juda qisqa:
function curry(func) {
return function curried(...args) {
if (args.length >= func.length) {
return func.apply(this, args);
} else {
return function(...args2) {
return curried.apply(this, args.concat(args2));
}
}
};
}
Foydalanish misollari:
function sum(a, b, c) {
return a + b + c;
}
let curriedSum = curry(sum);
alert( curriedSum(1, 2, 3) ); // 6, hali ham odatdagidek chaqiruv qilish mumkin
alert( curriedSum(1)(2,3) ); // 6, currying 1 chi argumentni
alert( curriedSum(1)(2)(3) ); // 6, to'la currying
Yangi curry
murakkab ko'rinishi mumkin, ammo aslida uni tushunish oson.
curry(func)
natijasi quyidagicha ko'rinadigan curried
o'ramidir:
// func konvertatsiya qilish funktsiyasi
function curried(...args) {
if (args.length >= func.length) { // (1)
return func.apply(this, args);
} else {
return function pass(...args2) { // (2)
return curried.apply(this, args.concat(args2));
}
}
};
Biz uni bajarganimizda, ikkita stsenariy mavjud:
- Hozir chaqiruv qiling: agar o'tgan
args
soni asl funktsiya (func.length
) funktsiyasida bir xil bo'lsa yoki undan ko'p bo'lsa, shunchaki chaqiruvni unga o'tkazing. - Qismanni oling: aks holda,
func
hali chaqirilmagan. Buning o'rniga, avvalgi argumentlarni yangilari bilan birgalikda taqdim etgancurried
ni qayta ishlatadigan yana birpass
qaytariladi. Keyin yana yangi chaqiruvda biz yana qisman (agar yetarli argument bo'lmasa) yoki natijada natijani olamiz.
Masalan, sum(a, b, c)
misolida nima bo'lishini ko'rib chiqamiz. Uchta argument, shuning uchun sum.length = 3
.
curried(1)(2)(3)
chaqiruvi uchun:
-
Birinchi chaqiruv
curried(1)
leksik muhitda1
ni eslaydi vapass
o'ramasini qaytaradi. -
pass
o'ramasi(2)
bilan chaqiriladi: u oldingi argumentlarni (1
) oladi, ularni(2)
bilan birlashtiradi vacurried(1, 2)
ni ular bilan birga chaqiradi.Argumentlar soni hali 3 dan kam bo'lganligi sababli,
curry
pass
ni qaytaradi. -
pass
o'ramasi yana(3)
bilan chaqiriladi, chunki keyingi chaqiruvpass(3)
oldingi argumentlarni (1
,2
) oladi va ularga3
qo'shib, chaqiruvnicurried(1, 2, 3)
qiladi - nihoyat3
argumentlari bor, ular asl funktsiyaga qaytarilgan.
Agar bu hali ham aniq bo'lmasa, chaqiruvlar ketma-ketligini yodda yoki qog'ozda kuzatib boring.
Currying funktsiyadan ma'lum bir aniq sonli argumentga ega bo'lishini talab qiladi.
Ta'rifga ko'ra, currying `sum(a, b, c)` ni `sum(a)(b)(c)` ga aylantirishi kerak.
Ammo JavaScript-dagi currying dasturlarining aksariyati ta'riflanganidek rivojlangan: ular funktsiyani ko'p argumentli variantda ham chaqirish mumkin.
-
Mavjud funktsiyani ba'zi argumentlarini tuzatsak, natijada (kamroq universal) funktsiya qisman deb nomlanadi. Qisman olish uchun
bind
dan foydalanishimiz mumkin, ammo boshqa usullar ham mavjud.Biz bir xil argumentni qayta-qayta takrorlashni xohlamasak, qismlar qulay. Agar bizda
send(from, to)
funktsiyasi mavjud bo'lsa vafrom
har doim bizning vazifamiz uchun bir xil bo'lishi kerak bo'lsa, biz qismanni olamiz va shu bilan davom etamiz. -
Currying - bu
f(a,b,c)
nif(a)(b)(c)
deb chaqiradigan konvertatsiya. JavaScript dasturlari odatda funktsiyani odatdagidek chaqiradi va agar argumentlar soni yetarli bo'lmasa, qisman qaytaradi.Currying biz oson qismlarni xohlaganimizda juda yaxshi. Log misolida ko'rganimizdek:
log(date)
yoki ikkita argumentlog(date, importance)
kabi bitta argument bilan chaqirilganda universallog(date, importance, message)
funktsiyasi bizga qisman beradi.