Bizga maâlumki, JavaScript-dagi funktsiyalar qiymatlardir.
JavaScript-dagi har qanday qiymat turga ega. Funktsiya qaysi turga kiradi?
JavaScript-da funktsiyalar obyektlardir.
Funktsiyalarni tasavvur qilishning yaxshi usuli â bu chaqiriladigan âharakat obyektlariâ. Biz ularni nafaqat chaqira olamiz, balki ularni obyektlar sifatida koârib chiqishimiz mumkin: xususiyatlarni qoâshish/olib tashlash, maâlumotnoma orqali oâtish va hk.
ânameâ xususiyati
Funktsiya obyektlari bir nechta ishlatilishi mumkin boâlgan xususiyatlarni oâz ichiga oladi.
Masalan, funktsiya nomiga ânameâ xususiyati sifatida kirish mumkin:
function sayHi() {
alert("Salom");
}
alert(sayHi.name); // sayHi
Qizigâi shundaki, nom berish mantiqi juda aqlli. Shuningdek, tayinlashda ishlatiladigan funktsiyalarga toâgâri nom beriladi:
let sayHi = function() {
alert("Salom");
}
alert(sayHi.name); // sayHi (ishlaydi!)
Agar tayinlash oldingan berilgan qiymat orqali bajarilgan boâlsa, u ham ishlaydi:
function f(sayHi = function() {}) {
alert(sayHi.name); // sayHi (ishlaydi!)
}
f();
Spetsifikatsiyada ushbu xususiyat âkontekstual ismâ deb nomlanadi. Agar funktsiya uni taâminlamasa, topshiriqda u kontekstdan aniqlanadi.
Obyekt usullari ham nomlarga ega:
let user = {
sayHi() {
// ...
},
sayBye: function() {
// ...
}
}
alert(user.sayHi.name); // sayHi
alert(user.sayBye.name); // sayBye
Bunda hech sehr yoâq. Toâgâri ismni aniqlashning imkoni boâlmagan holatlar ham mavjud. Bunday holda, ism xususiyati boâsh, masalan:
// massiv ichida yaratilgan funktsiya
let arr = [function() {}];
alert( arr[0].name ); // <bo'sh matn>
// interpretatorda to'g'ri nomni o'rnatish imkoniyati yo'q, shuning uchun bo'sh
Amalda esa aksariyat funktsiyalarning nomi bor.
âlengthâ xususiyati
Funktsiya parametrlari sonini qaytaradigan yana bir âlengthâ xususiyati mavjud, masalan:
function f1(a) {}
function f2(a, b) {}
function many(a, b, ...more) {}
alert(f1.length); // 1
alert(f2.length); // 2
alert(many.length); // 2
Bu erda dam qoldiq parametrlari hisoblanmaganligini koârishimiz mumkin.
length xususiyati baâzan boshqa funktsiyalarda ishlaydigan funktsiyalarni introspektsiya qilish uchun ishlatiladi.
Masalan, quyida joylashgan kodda ask funktsiyasi soâraladigan savolni va chaqiruv qilish uchun tasodifiy sonlarni qabul qiladi.
Agar foydalanuvchi oâz javobini taqdim qilsa, funktsiya ishlovchilarni chaqiradi. Ikki xil ishlov beruvchidan oâtishimiz mumkin:
- Nol-argumentli funktsiya, bu faqat foydalanuvchi ijobiy javob berganida chaqiriladi.
- Ikkala holatda ham chaqiriladigan va javobni qaytaradigan argumentlarga ega funktsiya.
Ushbu gâoya shundaki, bizda ijobiy holatlar uchun argumentlarsiz ishlov beruvchi sintaksis mavjud (eng tez-tez uchraydigan variant), lekin universal ishlovchilarni ham taqdim etishga qodir.
handlers ni toâgâri chaqirish uchun biz length xususiyatini koârib chiqamiz:
function ask(question, ...handlers) {
let isYes = confirm(question);
for(let handler of handlers) {
if (handler.length == 0) {
if (isYes) handler();
} else {
handler(isYes);
}
}
}
// ijobiy javob uchun ikkala ishlovchilar ham chaqiriladi
// salbiy javob uchun faqat ikkinchisi
ask("Savol?", () => alert('Siz ha dedingiz'), result => alert(result));
Bu polimorfizm deb nomlangan alohida holat â argumentlarni turiga qarab turlicha muomala qilish, yoki bizda length ga qarab. Ushbu gâoya JavaScript-ni kutubxonalarida ishlatilishi mumkin.
Maxsus xususiyatlar
Biz oâzimizga xos xususiyatlarni ham qoâshishimiz mumkin.
Bu erda biz chaqiruvlarning umumiy sonini kuzatib borish uchun counter xususiyatini qoâshamiz:
function sayHi() {
alert("Hi");
// keling, necha marta bajarilganini hisoblaymiz
sayHi.counter++;
}
sayHi.counter = 0; // boshlang'ich qiymati
sayHi(); // Hi
sayHi(); // Hi
alert( `Called ${sayHi.counter} times` ); // 2 marta chaqirildi
sayHi.counter = 0 kabi funktsiyaga tayinlangan xususiyat ichida counter mahalliy oâzgaruvchanni aniqlamaydi. Boshqacha qilib aytganda, xususiyat counter va let counter â bu oâzaro bogâliq boâlmagan ikkita narsa.
Biz funktsiyani obyekt sifatida koârib chiqishimiz, unda xususiyatlarni saqlashimiz mumkin, ammo bu uning bajarilishiga taâsir qilmaydi. Oâzgaruvchanlar hech qachon funktsiya xususiyatlaridan foydalanmaydi va aksincha. Bu shunchaki parallel olamlar.
Funktsiya xususiyatlari baâzida yopilishni oâzgartirishi mumkin. Masalan, funktsiya xususiyatidan foydalanish uchun hisoblagich funktsiyasi misolini O'zgaruvchi doirasi, yopilish (closure) bobidan qayta yozishimiz mumkin:
function makeCounter() {
// instead of:
// let count = 0
function counter() {
return counter.count++;
};
counter.count = 0;
return counter;
}
let counter = makeCounter();
alert( counter() ); // 0
alert( counter() ); // 1
Endi counter tashqi leksik muhitida emas, balki toâgâridan-toâgâri funktsiyalarda saqlanadi.
Yopishni ishlatishdan koâra yaxshiroqmi yoki yomonmi?
Asosiy farq shundaki, agar count qiymati tashqi oâzgaruvchanda yashasa, tashqi kod unga kira olmaydi. Faqatgina ichki funktsiyalar uni oâzgartirishi mumkin. Agar u funktsiyaga bogâliq boâlsa, unda bunday narsa boâlishi mumkin:
function makeCounter() {
function counter() {
return counter.count++;
};
counter.count = 0;
return counter;
}
let counter = makeCounter();
counter.count = 10;
alert( counter() ); // 10
Shunday qilib, amalga oshirishni tanlash bizning maqsadlarimizga bogâliq.
Named Function Expression
Named Function Expression, yoki NFE, nomi boâlgan Funktsiya ifodalari uchun atama.
Masalan, oddiy funktsiya ifodasini olaylik:
let sayHi = function(who) {
alert(`Salom, ${who}`);
};
Va unga ism qoâshaylik:
let sayHi = function func(who) {
alert(`Salom, ${who}`);
};
Bu yerda biror narsaga erishdikmi? Ushbu qoâshimcha "func" nomining maqsadi nima?
Birinchidan, bizda hali ham funktsiya ifodasi borligini taâkidlaymiz. function dan keyin "func" ismining qoâshilishi uni funktsiya deklaratsiyasiga aylantirmadi, chunki u hali ham tayinlash ifodasining bir qismi sifatida yaratilgan.
Bunday nomni qoâshish hech narsani buzmadi.
Funktsiya hanuzgacha sayHi() sifatida mavjud:
let sayHi = function func(who) {
alert(`Salom, ${who}`);
};
sayHi("John"); // Salom, John
func nomi haqida ikkita alohida narsa mavjud:
- Bu funktsiya oâziga havola qilishiga imkon beradi.
- Bu funksiya tashqaridan koârinmaydi.
Masalan, quyida joylashgan sayHi funktsiyasi yana oâzini "Mehmon" bilan chaqiradi, agar chaqirmasa, who taqdim etiladi:
let sayHi = function func(who) {
if (who) {
alert(`Salom, ${who}`);
} else {
func("Mehmon"); // o'zini qayta chaqirish uchun func-dan foydalaning
}
};
sayHi(); // Salom, Guest
// Ammo bu ishlamaydi:
func(); // Error, func is not defined (funktsiya tashqaridan ko'rinmaydi)
Nima uchun func dan foydalandik? Ehtimol, ichki chaqiruv uchun sayHi dan foydalanamiz?
Darhaqiqat, aksariyat hollarda biz:
let sayHi = function(who) {
if (who) {
alert(`Salom, ${who}`);
} else {
sayHi("Mehmon");
}
};
Ushbu kod bilan bogâliq muammo shundaki, sayHi qiymati oâzgarishi mumkin. Funktsiya boshqa oâzgaruvchanga oâtishi mumkin va kod xatolarga yoâl qoâyishni boshlaydi:
let sayHi = function(who) {
if (who) {
alert(`Hello, ${who}`);
} else {
sayHi("Mehmon"); // Error: sayHi is not a function
}
};
let welcome = sayHi;
sayHi = null;
welcome(); // Error, the nested sayHi call doesn't work any more!
Buning sababi shundaki, funktsiya sayHi ni tashqi leksik muhitdan oladi. Mahalliy sayHi yoâq, shuning uchun tashqi oâzgaruvchandan foydalaniladi. Chaqiruv paytida tashqi sayHi null.
Funktsiya ifodasiga kiritishimiz mumkin boâlgan ixtiyoriy nom aynan shu turdagi muammolarni hal qilish uchun moâljallangan.
Kodimizni tuzatish uchun undan foydalanamiz:
let sayHi = function func(who) {
if (who) {
alert(`Salom, ${who}`);
} else {
func("Mehmon"); // Endi barchasi yaxshi
}
};
let welcome = sayHi;
sayHi = null;
welcome(); // Salom, Mehmon (ichki chaqiruv ishlamoqda)
Endi u ishlaydi, chunki "func" nomi funktsional-lokal hisoblanadi. U tashqaridan olinmaydi (va u yerda koârinmaydi). Spetsifikatsiya har doim mavjud funktsiyaga murojaat qilishiga kafolat beradi.
Tashqi kod hali ham sayHi yoki welcome oâzgaruvchanga ega. Va func â bu âichki funktsiya nomiâ, funktsiya oâzini oâzi ichkaridan chaqira oladi.
Bu yerda tasvirlangan âichki ismâ xususiyati faqat funktsiya ifodalari uchun mavjud, funktsiya deklaratsiyalari uchun emas. Funktsiya deklaratsiyalari uchun yana bitta âichkiâ ism qoâshish uchun sintaksis imkoniyati mavjud emas.
Baâzan, bizga ishonchli ichki nom kerak boâlganda, funktsiya deklaratsiyasini nomlangan funktsiya ifodasi shakliga qayta yozish uchun sabab boâladi.
Xulosa
Funktsiyalar obyektlardir.
Bu yerda biz ularning xususiyatlarini koârib chiqdik:
nameâ funktsiya nomi. Nafaqat funktsiya taârifida berilganida, balki tayinlashlar va obyekt xususiyatlari uchun ham mavjud.lengthâ funktsiya taârifidagi argumentlar soni. Qoldiq parametrlari hisoblanmaydi.
Agar funktsiya funktsiya ifodasi deb eâlon qilingan boâlsa (asosiy kod oqimida emas) va u nomni olib yuradigan boâlsa, u holda Named Function Expression deyiladi. Ism ichida rekursiv chaqiruvlar yoki shunga oâxshash maâlumotlarga murojaat qilish uchun ishlatilishi mumkin.
Shuningdek, funktsiyalar qoâshimcha xususiyatlarga ega boâlishi mumkin. Koâpgina taniqli JavaScript kutubxonalari ushbu xususiyatdan juda yaxshi foydalanadi.
Ular âasosiyâ funktsiyani yaratadilar va unga boshqa koâplab âyordamchiâ funktsiyalarni biriktiradilar. Masalan, jquery kutubxonasi $ nomli funktsiyani yaratadi. Lodash kutubxonasi _ funktsiyasini yaratadi. Va keyin ularga _.clone, _.keyBy va boshqa xususiyatlarni qoâshib qoâyadilar (ular haqida koâproq maâlumotga ega boâlishni xohlagan vaqtingizda docs ga qarang). Aslida, ular buni global makonning ifloslanishini kamaytirish uchun qilishadi, shuning uchun bitta kutubxona faqat bitta global oâzgaruvchanni beradi. Bu nizolarni nomlash imkoniyatini kamaytiradi.
Shunday qilib, funktsiya nafaqat oâz-oâzidan biror narsa qila oladi, balki uning xususiyatlari orqali foydali funksiyalarni ham taâminlaydi.
Izohlar
<code>yorlig'ini ishlating, bir nechta satrlar uchun - ularni<pre>yorlig'i bilan o'rab qo'ying, 10 satrdan ortiq bo'lsa - sandbox (plnkr, jsbin, codepenâ¦)