JavaScript funktsiyalari. Ekspressiv JavaScript: Funktsiyadan bir nechta qiymatlarni qaytaradigan Javascript funktsiyalari

Odamlar kompyuter fanini daholar uchun san'at deb o'ylashadi. Aslida esa buning teskarisi – ko‘pchilik bir-birining ustiga turgan narsalarni xuddi mayda toshlardan devor yasagandek yasaydi.

Donald Knut

Alert kabi funksiyalarga qoʻngʻiroqlarni allaqachon koʻrgansiz. Funktsiyalar JavaScript dasturlashning noni va sariyog'idir. Dasturning bir qismini o'rash va uni o'zgaruvchi sifatida chaqirish g'oyasi juda mashhur. Bu katta dasturlarni strukturalash, takrorlashni kamaytirish, kichik dasturlarni nomlash va kichik dasturlarni bir-biridan ajratish uchun vositadir.

Funktsiyalardan eng aniq foydalanish yangi lug'at yaratishdir. Oddiy inson nasri uchun so'z ixtiro qilish yomon shakldir. Bu dasturlash tilida zarur.

O'rtacha katta yoshli rus tilida so'zlashuvchi taxminan 10 000 so'zni biladi. Noyob dasturlash tilida 10 000 ta o'rnatilgan buyruqlar mavjud. Va dasturlash tilining lug'ati aniqroq aniqlangan, shuning uchun u inson tiliga qaraganda kamroq moslashuvchan. Shuning uchun, biz odatda keraksiz takrorlashdan qochish uchun o'z so'zlarimizni qo'shishimiz kerak.

Funktsiya ta'rifi Funktsiya ta'rifi oddiy o'zgaruvchi ta'rifi bo'lib, bu erda o'zgaruvchi qabul qiladigan qiymat funktsiyadir. Masalan, quyidagi kod berilgan sonning kvadratini hisoblaydigan funktsiyaga ishora qiluvchi o'zgaruvchan kvadratni belgilaydi:

Var kvadrat = funktsiya(x) (qaytish x * x; ); console.log(kvadrat(12)); // → 144

Funksiya function kalit so‘zi bilan boshlanadigan ifoda orqali yaratiladi. Funktsiyalar parametrlar to'plamiga (bu holda faqat x) va funktsiya chaqirilganda bajarilishi kerak bo'lgan ko'rsatmalarni o'z ichiga olgan tanaga ega. Funksiya tanasi bitta gapdan iborat bo'lsa ham, har doim jingalak qavslar ichiga olinadi.

Funktsiya bir nechta parametrlarga ega bo'lishi yoki umuman bo'lmasligi mumkin. Quyidagi misolda makeNoise parametrlar ro'yxatiga ega emas, lekin quvvat ikkitaga ega:

Var makeNoise = function() ( console.log("Shit!"); ); makeNoise(); // → Khrya! var power = funktsiya (asosiy, ko'rsatkich) ( var natija = 1; uchun (var soni = 0; hisoblash)< exponent; count++) result *= base; return result; }; console.log(power(2, 10)); // → 1024

Ba'zi funktsiyalar quvvat va kvadrat kabi qiymatni qaytaradi, boshqalari makeNoise kabi qaytarmaydi, bu faqat yon ta'sirni keltirib chiqaradi. Qaytish bayonoti funksiya tomonidan qaytarilgan qiymatni belgilaydi. Dasturni qayta ishlash ushbu ko'rsatmaga yetganda, u darhol funktsiyadan chiqadi va bu qiymatni koddagi funktsiya chaqirilgan joyga qaytaradi. ifoda holda qaytish aniqlanmagan qaytaradi.

Parametrlar va qamrov Funktsiya parametrlari bir xil o'zgaruvchilardir, lekin ularning boshlang'ich qiymatlari funksiya kodida emas, balki chaqirilganda o'rnatiladi.

Funksiyalarning muhim xususiyati shundaki, funktsiya ichida yaratilgan o'zgaruvchilar (shu jumladan parametrlar) ushbu funktsiya uchun lokaldir. Bu shuni anglatadiki, quvvat misolida natija o'zgaruvchisi funktsiya har safar chaqirilganda yaratiladi va uning bu individual mujassamlanishi bir-biriga hech qanday aloqasi bo'lmaydi.

Ushbu o'zgaruvchan joylashuv faqat funktsiyalar ichida yaratilgan parametrlar va o'zgaruvchilar uchun amal qiladi. Har qanday funktsiyadan tashqarida aniqlangan o'zgaruvchilar global deyiladi, chunki ular dastur davomida ko'rinadi. Agar bir xil nomdagi mahalliy o'zgaruvchini e'lon qilmasangiz, bunday o'zgaruvchilarga funksiya ichida kirishingiz mumkin.

Quyidagi kod buni ko'rsatadi. U x o'zgaruvchisiga qiymat beradigan ikkita funktsiyani belgilaydi va chaqiradi. Birinchisi uni mahalliy deb e'lon qiladi va shu bilan faqat mahalliy o'zgaruvchini o'zgartiradi. Ikkinchisi e'lon qilmaydi, shuning uchun funktsiya ichidagi x bilan ishlash misol boshida aniqlangan global o'zgaruvchi xga tegishlidir.

Var x = "tashqi"; var f1 = function() ( var x = "f1 ichida"; ); f1(); console.log(x); // → tashqarida var f2 = function() ( x = "f2 ichida"; ); f2(); console.log(x); // → f2 ichida

Bu xatti-harakatlar funktsiyalar o'rtasidagi tasodifiy shovqinlarni oldini olishga yordam beradi. Agar barcha o'zgaruvchilar dasturning istalgan joyida ishlatilsa, bitta o'zgaruvchidan turli maqsadlarda foydalanilmasligiga ishonch hosil qilish juda qiyin bo'lar edi. Va agar siz o'zgaruvchini qayta ishlatmoqchi bo'lsangiz, uchinchi tomon kodi o'zgaruvchingiz qiymatlarini buzganida g'alati effektlarga duch kelasiz. Funksiya-lokal o'zgaruvchilarga ular faqat funktsiya ichida mavjud bo'ladigan tarzda ishlov berish orqali til funksiyalar bilan go'yo ular alohida kichik koinotlardek ishlash imkonini beradi va butun kod haqida qayg'urmaslik imkonini beradi.

Joylashtirilgan qamrovli JavaScript global va mahalliy o'zgaruvchilardan ko'proq narsani ajratib turadi. Funktsiyalar funksiyalar ichida aniqlanishi mumkin, natijada mahalliylikning bir necha darajalari paydo bo'ladi.

Misol uchun, quyidagi juda ma'nosiz funktsiya yana ikkitasini o'z ichiga oladi:

Var landshaft = function() ( var natija = ""; var flat = funktsiya(hajm) ( uchun (var count = 0; count)< size; count++) result += "_"; }; var mountain = function(size) { result += "/"; for (var count = 0; count < size; count++) result += """; result += "\\"; }; flat(3); mountain(4); flat(6); mountain(1); flat(1); return result; }; console.log(landscape()); // → ___/""""\______/"\_

Yassi va tog 'funksiyalari natija o'zgaruvchisini ko'radi, chunki ular uni belgilaydigan funktsiya ichidadir. Lekin ular bir-birining count o'zgaruvchilarini ko'ra olmaydi, chunki bir funktsiyaning o'zgaruvchilari boshqasining doirasidan tashqarida. Landshaft funksiyasidan tashqaridagi muhit esa bu funksiya ichida belgilangan o‘zgaruvchilardan hech birini ko‘rmaydi.

Muxtasar qilib aytganda, har bir mahalliy doirada siz uni o'z ichiga olgan barcha doiralarni ko'rishingiz mumkin. Funktsiyada mavjud bo'lgan o'zgaruvchilar to'plami funktsiya dasturda e'lon qilingan joy bilan belgilanadi. Funktsiya ta'rifi atrofidagi bloklardagi barcha o'zgaruvchilar ko'rinadi, shu jumladan asosiy dasturning yuqori darajasida aniqlanganlar. Qo'llanish sohalariga bunday yondashuv leksik deb ataladi.

Boshqa dasturlash tillarini o'rgangan odamlar jingalak qavslar ichiga olingan har qanday blok o'zining mahalliy muhitini yaratadi deb o'ylashlari mumkin. Ammo JavaScript-da faqat funktsiyalar doirani yaratadi. Siz mustaqil bloklardan foydalanishingiz mumkin:

Var bir narsa = 1; ( var bir narsa = 2; // biror narsa o'zgaruvchisi bilan biror narsa qiling... ) // Blokdan chiqdi...

Lekin blok ichidagi biror narsa tashqaridagi kabi bir xil o'zgaruvchidir. Garchi bunday bloklarga ruxsat berilgan bo'lsa-da, ulardan faqat if iboralari va sikllardan foydalanish mantiqan to'g'ri keladi.

Agar bu sizga g'alati tuyulsa, siz shunday deb o'ylayotgan yagona odam emassiz. JavaScript 1.7 versiyasida paydo bo'ldi kalit so'z let, bu var kabi ishlaydi, lekin faqat funktsiya uchun emas, balki har qanday berilgan blok uchun mahalliy bo'lgan o'zgaruvchilarni yaratadi.

Funktsiyalar qiymat sifatida Funksiya nomlari odatda dasturning nomi sifatida ishlatiladi. Bunday o'zgaruvchi bir marta o'rnatiladi va o'zgarmaydi. Demak, funksiya va uning nomini chalkashtirib yuborish oson.

Ammo bu ikki xil narsa. Funktsiya chaqiruvi oddiy o'zgaruvchi kabi ishlatilishi mumkin - masalan, har qanday ifodada ishlatiladi. Funktsiya chaqiruvini yangi o'zgaruvchida saqlash, uni parametr sifatida boshqa funksiyaga o'tkazish va hokazo. Bundan tashqari, funktsiya chaqiruvini saqlaydigan o'zgaruvchi oddiy o'zgaruvchi bo'lib qoladi va uning qiymatini o'zgartirish mumkin:

Var launchMissiles = funktsiya(qiymat) (missileSystem.launch("yoki!"); ); agar (safeMode) launchMissiles = funktsiya (qiymat) (/* bekor qilish */);

5-bobda biz funksiya chaqiruvlarini boshqa funksiyalarga o‘tkazish orqali amalga oshirishingiz mumkin bo‘lgan ajoyib narsalarni muhokama qilamiz.

Funksiyalarni e'lon qilish "var kvadrat = funktsiya..." iborasining qisqaroq versiyasi mavjud. Funktsiya kalit so'zi bayonot boshida ishlatilishi mumkin:

Funktsiya kvadrati(x) (qaytish x * x; )

Bu funksiya deklaratsiyasi. Ko'rsatma kvadrat o'zgaruvchini belgilaydi va unga berilgan funktsiyani belgilaydi. Hozirgacha juda yaxshi. Bunday ta'rifda faqat bitta tuzoq bor.

Console.log("Kelajak aytadi:", kelajak()); function future() ( "Bizda hali ham uchadigan mashinalar yo'q."; ) qaytaring.

Ushbu kod funksiya uni ishlatadigan kod ostida e'lon qilingan bo'lsa ham ishlaydi. Buning sababi, funktsiya deklaratsiyasi oddiy yuqoridan pastga dastur bajarilishining bir qismi emas. Ular o'z doirasining yuqori qismiga "ko'chiriladi" va ushbu doiradagi istalgan kod bilan chaqirilishi mumkin. Ba'zan bu qulay, chunki siz yuqoridagi barcha funktsiyalarni ular qo'llanilgan joyda belgilash haqida tashvishlanmasdan kodni eng ma'noli tartibda yozishingiz mumkin.

Agar biz shartli blok yoki sikl ichiga funktsiya deklaratsiyasini joylashtirsak nima bo'ladi? Siz buni qilishingiz shart emas. Tarixiy jihatdan, turli xil JavaScript ishlaydigan platformalar bunday holatlarni boshqacha ko'rib chiqishgan va hozirgi til standarti buni taqiqlaydi. Agar siz dasturlaringiz ketma-ket ishlashini istasangiz, funksiya deklaratsiyasidan faqat boshqa funktsiyalar yoki asosiy dastur ichida foydalaning.

Funktsiya misoli() ( funksiya a() () // Oddiy bo'lsa (bir narsa) ( b() () // Ay-yay-yay! ) )

Qo'ng'iroqlar to'plami Funktsiyalar bilan bajarish tartibi qanday ishlashini batafsil ko'rib chiqish foydali bo'ladi. Bu yerga oddiy dastur bir nechta funksiya chaqiruvlari bilan:

Funktsiya greet(who) ( console.log("Salom, " + who); ) salomlash("Semyon"); console.log("Pokeda");

U shunday qayta ishlanadi: salomlashishni chaqirish o'tishni funktsiyaning boshiga o'tishga olib keladi. U o'rnatilgan console.log funktsiyasini chaqiradi, u boshqaruvni ushlab turadi, o'z ishini qiladi va boshqaruvni qaytaradi. Keyin salomlashishning oxiriga yetib, chaqirilgan joyga qaytadi. Keyingi qator yana console.log ni chaqiradi.

Buni sxematik tarzda quyidagicha ko'rsatish mumkin:

Yuqori salomlashma console.log yuqoriga salomlash

Funktsiya chaqirilgan joyga qaytishi kerakligi sababli, kompyuter funksiya chaqirilgan kontekstni eslab qolishi kerak. Bir holatda, console.log salomlashish uchun qaytib kelishi kerak. Boshqasida u dasturning oxiriga qaytadi.

Kompyuter kontekstni eslab turadigan joy stek deb ataladi. Har safar funktsiya chaqirilganda, joriy kontekst stekning yuqori qismiga suriladi. Funktsiya qaytib kelganda, u stekdan yuqori kontekstni chiqaradi va undan ishlashni davom ettirish uchun ishlatadi.

Stack xotirasi uchun xotira maydoni talab qilinadi. Stack juda kattalashganda, kompyuter ishlashni to'xtatadi va "stekning to'lib ketishi" yoki "juda ko'p rekursiya" kabi narsalarni aytadi. Quyidagi kod buni ko'rsatadi - u kompyuterga juda murakkab savol beradi, bu ikki funktsiya o'rtasida cheksiz sakrashga olib keladi. Aniqroq aytganda, agar kompyuterda cheksiz stek bo'lsa, bu cheksiz sakrashlar bo'lar edi. Aslida, stek to'lib toshib ketadi.

tovuq() funksiyasi ( tuxumni qaytarish(); ) funktsiyasi tuxum() ( tovuqni qaytarish(); ) console.log(chicken() + "birinchi keldi."); // → ??

Majburiy emas argumentlar Quyidagi kod butunlay qonuniy va muammosiz ishlaydi:

Ogohlantirish("Salom", "Xayrli kech", "Hammaga salom!");

Rasmiy ravishda, funktsiya bitta argumentni oladi. Biroq, bunday qiyinchilik tug'ilganda, u shikoyat qilmaydi. U qolgan dalillarni e'tiborsiz qoldirib, "Salom" ni ko'rsatadi.

JavaScript funksiyaga uzatiladigan argumentlar soni haqida juda aniq. Agar siz juda ko'p pul o'tkazsangiz, ortiqcha narsalar e'tiborga olinmaydi. Juda oz va etishmayotganlarga aniqlanmagan qiymat beriladi.

Ushbu yondashuvning salbiy tomoni shundaki, hech kim shikoyat qilmasdan funktsiyaga noto'g'ri argumentlarni yuborish mumkin va hatto ehtimol.

Afzalligi shundaki, siz ixtiyoriy argumentlarni oladigan funksiyalarni yaratishingiz mumkin. Misol uchun, quvvat funktsiyasining keyingi versiyasida uni ikkita yoki bitta argument bilan chaqirish mumkin - ikkinchi holda, ko'rsatkich ikkiga teng bo'ladi va funktsiya kvadrat kabi ishlaydi.

Funksiya quvvati(asos, koʻrsatkich) ( agar (koʻrsatkich == aniqlanmagan) koʻrsatkich = 2; var natija = 1; uchun (var count = 0; count)< exponent; count++) result *= base; return result; } console.log(power(4)); // → 16 console.log(power(4, 3)); // → 64

Keyingi bobda biz funktsiya tanasida unga berilgan argumentlarning aniq sonini qanday aniqlash mumkinligini ko'rib chiqamiz. Bu foydali, chunki ... istalgan sonli argumentlarni oladigan funksiya yaratish imkonini beradi. Masalan, console.log ushbu xususiyatdan foydalanadi va unga berilgan barcha argumentlarni chop etadi:

Console.log("R", 2, "D", 2); // → R 2 D 2

Yopish Funktsiya chaqiruvlarini o'zgaruvchilar sifatida ishlatish qobiliyati va har safar funktsiya chaqirilganda mahalliy o'zgaruvchilar yangidan yaratilishi bizni qiziqarli savolga olib keladi. Funktsiya ishlashni to'xtatganda mahalliy o'zgaruvchilar bilan nima sodir bo'ladi?

Quyidagi misol bu masalani ko'rsatadi. U mahalliy o'zgaruvchini yaratuvchi wrapValue funksiyasini e'lon qiladi. Keyin ushbu mahalliy o'zgaruvchini o'qiydigan va uning qiymatini qaytaradigan funktsiyani qaytaradi.

Function wrapValue(n) ( var localVariable = n; return function() ( return localVariable; ); ) var wrap1 = wrapValue(1); var wrap2 = wrapValue(2); console.log(wrap1()); // → 1 console.log(wrap2()); // → 2

Bu amal qiladi va kerak bo'lganda ishlaydi - o'zgaruvchiga kirish qoladi. Bundan tashqari, bir vaqtning o'zida bir xil o'zgaruvchining bir nechta nusxalari mavjud bo'lishi mumkin, bu esa har bir funktsiya chaqiruvi bilan mahalliy o'zgaruvchilar qayta yaratilishini tasdiqlaydi.

Mahalliy o'zgaruvchining namunasiga havola bilan ishlash qobiliyati yopish deb ataladi. Mahalliy o'zgaruvchilarni yopuvchi funksiya yopish deyiladi. Bu sizni nafaqat o'zgaruvchan umrlar haqida qayg'urishdan xalos qiladi, balki funksiyalardan ijodiy foydalanish imkonini ham beradi.

Bir oz o'zgartirish bilan biz misolimizni raqamlarni har qanday berilgan raqamga ko'paytiruvchi funktsiyaga aylantiramiz.

Funksiya multiplikatori(faktor) ( qaytaruvchi funksiya(raqam) ( qaytaruvchi raqam * omil; ); ) var two = multiplikator(2); console.log(ikki marta(5)); // → 10

wrapValue misolidagi localVariable kabi alohida o'zgaruvchilar endi kerak emas. Parametrning o'zi mahalliy o'zgaruvchi bo'lgani uchun.

Bunday fikrlashni boshlash uchun amaliyot kerak bo'ladi. Yaxshi variant aqliy model bu funksiya kodni o'z tanasida muzlatib qo'yishini va uni o'rashga o'rashini tasavvur qilishdir. Qaytish funksiyasini (...) (...) ko'rganingizda, uni keyinchalik foydalanish uchun muzlatilgan kodning boshqaruv paneli sifatida tasavvur qiling.

Bizning misolimizda multiplikator muzlatilgan kod qismini qaytaradi, biz uni ikki marta o'zgaruvchida saqlaymiz. Oxirgi satr o'zgaruvchidagi funksiyani chaqiradi, bu esa saqlangan kodni faollashtirishga olib keladi (qaytish raqami * omil;). U hali ham multiplikatorni chaqirganda aniqlangan omil o'zgaruvchisiga kirish huquqiga ega, shuningdek, raqamli parametr sifatida muzdan tushirish (5) paytida berilgan argumentga kirish huquqiga ega.

Rekursiya Agar funktsiya stekni to'ldirib yubormaslikka harakat qilsa, o'zini chaqirishi mumkin. Bu funksiya rekursiv deyiladi. Bu erda eksponentatsiyaning muqobil amalga oshirilishiga misol:

Funktsiya kuchi(asosiy, ko'rsatkich) ( agar (ko'rsatkich == 0) 1ni qaytaradi; aks holda bazani qaytaradi * quvvat(tayanch, ko'rsatkich - 1); ) console.log(power(2, 3)); // → 8

Matematiklar eksponentatsiyani taxminan shunday ta'riflaydilar va ehtimol bu kontseptsiyani tsikldan ko'ra nafisroq tasvirlaydi. Funktsiya bir nechta ko'paytirishga erishish uchun o'zini turli argumentlar bilan ko'p marta chaqiradi.

Biroq, ushbu amaliyotda muammo mavjud: normal muhit JavaScript loopli versiyadan 10 baravar sekinroq. Loop bo'ylab yurish funktsiyani chaqirishdan ko'ra arzonroqdir.

Tezlik va nafislik dilemmasi juda qiziq. Odamlar uchun qulaylik va mashinalar uchun qulaylik o'rtasida ma'lum bir farq bor. Har qanday dasturni kattaroq va murakkabroq qilish orqali tezlashtirish mumkin. Dasturchi tegishli muvozanatni topishi kerak.

Birinchi darajali ko'rsatkich bo'lsa, nomaqbul tsikl juda oddiy va tushunarli. Uni rekursiya bilan almashtirish mantiqiy emas. Biroq, ko'pincha dasturlar shunday murakkab tushunchalar bilan shug'ullanadiki, kishi o'qish qobiliyatini oshirish orqali samaradorlikni pasaytirmoqchi.

Bir necha marta takrorlangan va men to'liq rozi bo'lgan asosiy qoida, dastur sekinlashayotganiga to'liq ishonch hosil qilmaguningizcha, ishlash haqida tashvishlanmang. Agar shunday bo'lsa, eng uzoq davom etadigan qismlarni toping va u erda samaradorlik uchun nafislik bilan savdo qiling.

Albatta, biz darhol ishlashni butunlay e'tiborsiz qoldirmasligimiz kerak. Ko'p hollarda, eksponentatsiyada bo'lgani kabi, biz oqlangan echimlardan unchalik soddalikka erisha olmaymiz. Ba'zan tajribali dasturchi oddiy yondashuv hech qachon etarlicha tez bo'lmasligini darhol ko'radi.

Men buni ta'kidlayman, chunki juda ko'p tajribasiz dasturchilar hatto kichik narsalarda ham samaradorlik bilan shug'ullanishadi. Natija kattaroq, murakkabroq va ko'pincha xatosiz emas. Bunday dasturlarni yozish ko'proq vaqt talab etadi, lekin ular ko'pincha tezroq ishlamaydi.

Ammo rekursiya har doim ham looplarga nisbatan samaraliroq alternativa emas. Ayrim masalalarni rekursiya yordamida yechish osonroq. Ko'pincha bu daraxtning bir nechta shoxlarini kesib o'tishdir, ularning har biri shoxlanishi mumkin.

Bu yerda topishmoq: 1 raqamidan boshlab, so‘ngra 5 ni qo‘shish yoki 3 ga ko‘paytirish orqali cheksiz sonni olish mumkin. Raqam berilganda, qo‘shish va ko‘paytirish ketma-ketligini topishga harakat qiladigan funksiyani qanday yozamiz. bu berilgan raqamga olib keladimi? Misol uchun, 13 raqamini birinchi navbatda 1 ni 3 ga ko'paytirish va keyin 5 ni ikki marta qo'shish orqali olish mumkin. Va 15 raqamini bu tarzda umuman olish mumkin emas.

Rekursiv yechim:

findSolution(target) funksiyasi (funksiya find(start, history) ( if (start == target) tarixini qaytaradi; aks holda, agar (start > target) null qaytaradi; aks holda find find(start + 5, "(" + history + " + " + 5)") || find(start * 3, "(" + history + " * 3)"); ) return find(1, "1"); ) console.log(findSolution(24)); // → (((1 * 3) + 5) * 3)

Bu misol eng qisqa yechimni topishi shart emas - uni har qanday kishi qoniqtiradi. Dastur qanday ishlashini darhol tushunishingizni kutmayman. Ammo keling, rekursiv fikrlashda ushbu ajoyib mashqni tushunaylik.

Find ichki funksiyasi rekursiyani amalga oshiradi. Buning uchun ikkita argument kerak bo'ladi - joriy raqam va bu raqamga qanday etib kelganimiz haqidagi yozuvni o'z ichiga olgan qator. Va u bizning qadamlar ketma-ketligini ko'rsatadigan satrni yoki nullni qaytaradi.

Buning uchun funksiya uchta amaldan birini bajaradi. Agar berilgan raqam maqsadga teng bo'lsa, unda hozirgi hikoya aynan unga erishish yo'lidir, shuning uchun u qaytib keladi. Agar berilgan raqam maqsaddan kattaroq bo'lsa, ko'paytirish va qo'shishni davom ettirishning ma'nosi yo'q, chunki u faqat ortadi. Va agar biz hali maqsadga erishmagan bo'lsak, funktsiya ikkalasini ham sinab ko'radi mumkin bo'lgan usullar, berilgan raqamdan boshlanadi. U o'zini ikki marta, har bir usul bilan bir marta chaqiradi. Agar birinchi qo'ng'iroq null bo'lmasa, u qaytariladi. Boshqa holatda, ikkinchisi qaytariladi.

Funktsiya o'zining istalgan effektiga qanday erishishini yaxshiroq tushunish uchun 13 raqamiga yechim topish uchun qilgan qo'ng'iroqlarini ko'rib chiqaylik.

Toping(1, "1") toping(6, "(1 + 5)") toping(11, "((1 + 5) + 5)") toping(16, "(((1 + 5) + 5) ) + 5)") juda katta topildi(33, "(((1 + 5) + 5) * 3)") juda katta topildi(18, "((1 + 5) * 3)") juda katta topildi( 3, "(1 * 3)") toping(8, "((1 * 3) + 5)") toping(13, "(((1 * 3) + 5) + 5)") topildi!

Chiziq qo'ng'iroqlar to'plamining chuqurligini ko'rsatadi. Birinchi marta find funktsiyasi (1 + 5) va (1 * 3) bilan boshlangan echimlarni tekshirish uchun o'zini ikki marta chaqiradi. Birinchi qo'ng'iroq (1 + 5) bilan boshlanadigan yechimni qidiradi va kerakli sondan kamroq yoki unga teng sonni chiqaradigan barcha echimlarni tekshirish uchun rekursiyadan foydalanadi. Uni topa olmaydi va null qaytaradi. Keyin operator || va (1 * 3) variantni tekshiradigan funksiya chaqiruviga o'tadi. Bu yerda omadimiz keldi, chunki uchinchi rekursiv qo‘ng‘iroqda biz 13 ni olamiz. Bu qo‘ng‘iroq qatorni qaytaradi va har biri || yo'lda bu chiziqdan balandroq o'tadi, natijada yechim qaytariladi.

Funktsiyalarni o'stirish Dasturga funktsiyalarni kiritishning ikkita ko'proq yoki kamroq tabiiy usullari mavjud.

Birinchisi, shunga o'xshash kodni bir necha marta yozishingizdir. Bunga yo'l qo'ymaslik kerak - ko'proq kod xatolar uchun ko'proq joy va dasturni tushunishga harakat qilayotganlar uchun ko'proq o'qish materialini anglatadi. Shunday qilib, biz takroriy funksiyani olamiz va uni moslashtiramiz yaxshi ism va uni funktsiyaga qo'ying.

Ikkinchi usul - siz alohida funktsiyaga joylashtirishga arziydigan yangi funksiyalarga ehtiyoj sezasiz. Siz funktsiya nomidan boshlaysiz va keyin uning tanasini yozasiz. Siz hatto funktsiyaning o'zi aniqlanmasdan oldin funksiyadan foydalanadigan kodni yozishdan boshlashingiz mumkin.

Funktsiyani nomlash siz uchun qanchalik qiyinligi uning funksionalligini qanchalik yaxshi tushunganingizni ko'rsatadi. Keling, bir misol keltiraylik. Biz ikkita raqamni, fermadagi sigirlar va tovuqlar sonini, keyin "sigirlar" va "tovuqlar" so'zlarini chop etadigan dastur yozishimiz kerak. Oldindagi raqamlarga nol qo'shishingiz kerak, shunda har biri aniq uchta pozitsiyani egallaydi.

007 Sigirlar 011 Tovuqlar

Shubhasiz, bizga ikkita argumentli funksiya kerak. Keling, kodlashni boshlaylik.
// chop etish FarmInventory funktsiyasi printFarmInventory(cows, tovuqlar) ( var cowString = String(cows); while (cowString.length)< 3) cowString = "0" + cowString; console.log(cowString + " Коров"); var chickenString = String(chickens); while (chickenString.length < 3) chickenString = "0" + chickenString; console.log(chickenString + " Куриц"); } printFarmInventory(7, 11);

Agar satrga .length qo'shsak, uning uzunligini olamiz. Ma'lum bo'ladiki while tsikllari 3 ta belgidan iborat qatorni olmaguningizcha raqamlarga bosh nollarni qo'shing.

Tayyor! Ammo biz kodni fermerga yubormoqchi bo'lganimizda (albatta, katta chek bilan) u qo'ng'iroq qilib, uning fermasida cho'chqalar borligini va biz cho'chqalar sonining ko'rinishini qo'shishimiz mumkinligini aytdi. dastur?

Albatta mumkin. Ammo biz ushbu to'rt qatordan kodni nusxalash va joylashtirishni boshlaganimizda, biz to'xtab, o'ylashimiz kerakligini tushunamiz. Buning yaxshiroq yo'li bo'lishi kerak. Biz dasturni yaxshilashga harakat qilamiz:

// Nollar VA Yorliqlarni qo'shish bilan chiqish printZeroPaddedWithLabel(raqam, yorliq) ( var numberString = String(raqam); while (numberString.length)< 3) numberString = "0" + numberString; console.log(numberString + " " + label); } // вывестиИнвентаризациюФермы function printFarmInventory(cows, chickens, pigs) { printZeroPaddedWithLabel(cows, "Коров"); printZeroPaddedWithLabel(chickens, "Куриц"); printZeroPaddedWithLabel(pigs, "Свиней"); } printFarmInventory(7, 11, 3);

Ishlaydi! Lekin printZeroPaddedWithLabel nomi biroz g'alati. U uchta narsani birlashtiradi - chiqish, nol qo'shish va yorliq - bitta funktsiyaga. Funktsiyaga butun takrorlanuvchi fragmentni kiritish o'rniga, keling, bitta tushunchani ajratib ko'rsatamiz:

// nol funktsiyasini qo'shing zeroPad(raqam, kenglik) ( var string = String(raqam); while (string.length)< width) string = "0" + string; return string; } // вывестиИнвентаризациюФермы function printFarmInventory(cows, chickens, pigs) { console.log(zeroPad(cows, 3) + " Коров"); console.log(zeroPad(chickens, 3) + " Куриц"); console.log(zeroPad(pigs, 3) + " Свиней"); } printFarmInventory(7, 16, 3);

Chiroyli, aniq nomli zeroPad funksiyasi kodni tushunishni osonlashtiradi. Va u nafaqat bizning holatlarimizda, balki ko'p holatlarda ishlatilishi mumkin. Masalan, formatlangan jadvallarni raqamlar bilan ko'rsatish uchun.

Funktsiyalar qanchalik aqlli va ko'p qirrali bo'lishi kerak? Raqamni uchta pozitsiyagacha nol bilan to'ldiradigan oddiy funktsiyani yoki murakkab funktsiyani yozishimiz mumkin. umumiy maqsad raqamlarni formatlash, qo'llab-quvvatlovchi kasrlar, manfiy raqamlar, nuqtalarni tekislash, turli belgilar bilan to'ldirish va boshqalar uchun.

Yaxshi asosiy qoida - faqat foydali bo'lishini biladigan funksiyalarni qo'shishdir. Ba'zan har bir kichik ehtiyoj uchun umumiy maqsadli ramkalar yaratish jozibador. Unga qarshi turing. Siz hech qachon ishni tugata olmaysiz, shunchaki hech kim ishlatmaydigan bir qancha kod yozishni tugatasiz.

Funktsiyalar va nojo'ya ta'sirlar Funksiyalarni taxminan yon ta'siri uchun chaqirilganlarga va ba'zi qiymatlarni olish uchun chaqirilganlarga bo'lish mumkin. Albatta, bu xususiyatlarni bitta funktsiyada birlashtirish ham mumkin.

Ferma misolidagi birinchi yordamchi funksiya printZeroPaddedWithLabel chaqiriladi, chunki u yon ta'sirga ega: u satrni chop etadi. Ikkinchisi, zeroPad, qaytish qiymati tufayli. Va ikkinchi funktsiya birinchisiga qaraganda tez-tez yordam berishi tasodif emas. Qiymatlarni qaytaradigan funktsiyalarni bir-biri bilan birlashtirish nojo'ya ta'sirlarni keltirib chiqaradigan funktsiyalarga qaraganda osonroqdir.

Sof funktsiya - bu nafaqat nojo'ya ta'sirlarga ega bo'lmagan, balki kodning qolgan qismining nojo'ya ta'siriga ham bog'liq bo'lmagan qiymatni qaytaruvchi funksiyaning maxsus turi - masalan, u tasodifiy bo'lishi mumkin bo'lgan global o'zgaruvchilar bilan ishlamaydi. boshqa joyda o'zgartirildi. Sof funksiya bir xil argumentlar bilan chaqirilganda bir xil natijani qaytaradi (va boshqa hech narsa qilmaydi) - bu juda yaxshi. U bilan ishlash oson. Bunday funktsiyaga qo'ng'iroqni kodning ma'nosini o'zgartirmasdan, uning ishining natijasi bilan aqliy ravishda almashtirish mumkin. Bunday funktsiyani sinab ko'rmoqchi bo'lganingizda, uni shunchaki chaqirishingiz mumkin va agar u berilgan kontekstda ishlayotgan bo'lsa, u har qanday kontekstda ishlashiga ishonch hosil qiling. Kamroq sof funktsiyalar ko'p omillarga qarab turli xil natijalarni berishi va sinash va hisobga olish qiyin bo'lgan yon ta'sirga ega bo'lishi mumkin.

Biroq, siz butunlay sof bo'lmagan funktsiyalarni yozishdan yoki bunday funktsiyalardan muqaddas kodni tozalashni boshlashdan uyalmasligingiz kerak. Yon ta'siri ko'pincha foydalidir. console.log funksiyasining toza versiyasini yozishning imkoni yo'q va bu funksiya juda foydali. Ba'zi operatsiyalar nojo'ya ta'sirlar yordamida ifodalash osonroq.

Xulosa Ushbu bob o'z funksiyalaringizni qanday yozishni ko'rsatdi. Funktsiya kalit so'zi ifoda sifatida ishlatilsa, funktsiya chaqiruviga ko'rsatgichni qaytaradi. Ko'rsatma sifatida foydalanilganda, siz o'zgaruvchiga funksiya chaqiruvini belgilash orqali e'lon qilishingiz mumkin.

Funksiyalarni tushunishning kaliti mahalliy qamrovdir. Funktsiya ichida e'lon qilingan parametrlar va o'zgaruvchilar u uchun lokal bo'lib, har safar chaqirilganda qayta yaratiladi va tashqaridan ko'rinmaydi. Boshqa funksiya ichida e'lon qilingan funksiyalar uning doirasiga kirish huquqiga ega.

Dastur tomonidan bajariladigan turli vazifalarni funksiyalarga ajratish juda foydali. O'zingizni takrorlashingiz shart emas; kitobning bo'limlari va bo'limlari oddiy matnni tartibga solishga yordam berganidek, funksiyalar kodni mazmunli qismlarga bo'lish orqali uni yanada o'qilishi mumkin qiladi.

ExercisesMinimum Oldingi bobda biz o'zining eng kichik argumentlarini qaytaruvchi Math.min funksiyasini eslatib o'tdik. Endi bunday funktsiyani o'zimiz yozishimiz mumkin. Ikki argumentni oladigan va ulardan minimalini qaytaruvchi min funksiyani yozing.

Console.log(min(0, 10)); // → 0 console.log(min(0, -10)); // → -10

Rekursiya Biz ko'rdikki, % (modulo) operatori sonning (% 2) juft ekanligini aniqlash uchun ishlatilishi mumkin. Buni aniqlashning yana bir usuli:

Nol teng.
Birlik g'alati.
Har qanday N soni N-2 bilan bir xil paritetga ega.

Ushbu qoidalarga muvofiq rekursiv isEven funksiyasini yozing. U raqamni qabul qilishi va mantiqiy qiymatni qaytarishi kerak.

Uni 50 va 75 da sinab ko'ring. Uni -1 qilib ko'ring. Nega u shunday harakat qilmoqda? Uni qandaydir tarzda tuzatish mumkinmi?

Uni 50 va 75 da sinab ko'ring. -1 da qanday ishlashini ko'ring. Nega? Buni tuzatishning yo'lini o'ylab ko'rasizmi?

Console.log(isEven(50)); // → true console.log(isEven(75)); // → false console.log(isEven(-1)); // → ??

Fasollarni hisoblash.

Satrning N belgilar raqamini unga .charAt(N) (“string”.charAt(5)) qo‘shish orqali olish mumkin – xuddi .length yordamida satr uzunligini olish kabi. Qaytish qiymati bitta belgidan iborat satr bo'ladi (masalan, "k"). Satrning birinchi belgisi 0 pozitsiyasiga ega, ya'ni oxirgi belgi string.length - 1 pozitsiyasiga ega bo'ladi. Boshqacha qilib aytganda, ikki belgidan iborat satr uzunligi 2 ga, uning belgilar pozitsiyalari esa 0 va 1 ga teng bo'ladi.

Argument sifatida satrni oladigan va qatordagi “B” belgilar sonini qaytaruvchi countBs funksiyasini yozing.

Keyin countBs kabi ishlaydi, lekin ikkinchi parametrni oladi - biz satrda izlayotgan belgini (faqat "B" belgilar sonini sanash o'rniga) countChar deb nomlangan funktsiyani yozing. Buning uchun countBs funksiyasini qayta ishlang.

Funktsiyalar JavaScript-dagi kodning eng muhim qurilish bloklaridan biridir.

Funktsiyalar buyruqlar to'plamidan iborat va odatda bittasini bajaradi aniq vazifa(masalan, raqamlarni yig'ish, ildizlarni hisoblash va boshqalar).

Funktsiyaga joylashtirilgan kod faqat ushbu funktsiyaga aniq chaqiruvdan so'ng bajariladi.

Funktsiya deklaratsiyasi

1. Sintaksis:

//Functionname(ln1, ln2)( Funksiya kodi) //funksiyani chaqirishFunctionname(ln1,lr2);

2. Sintaksis:

//Funksiya deklaratsiyasi var function name=function(ln1, ln2)(Funksiya kodi) //Funksiya funksiyasini chaqirish(ln1,lr2);

funktsiya nomi funksiya nomini bildiradi. Sahifadagi har bir funktsiya o'ziga xos nomga ega bo'lishi kerak. Funktsiya nomi lotin harflarida ko'rsatilishi va raqamlar bilan boshlanmasligi kerak.

ln1 va ln2 - bu funktsiyaga o'tkazilishi mumkin bo'lgan o'zgaruvchilar yoki qiymatlar. Har bir funktsiyaga cheksiz miqdordagi o'zgaruvchilar o'tkazilishi mumkin.

Iltimos, diqqat qiling: funktsiyaga hech qanday o'zgaruvchi berilmasa ham, funktsiya nomidan keyin "()" qavslar qo'yishni unutmang.

JavaScript-dagi funksiya nomlari katta-kichik harflarga sezgir ekanligini unutmang.

Misol uchun JavaScript funksiyasi

Quyidagi misoldagi messageWrite() funksiyasi faqat tugma bosilgandan keyin bajariladi.

E'tibor bering, ushbu misolda onclick hodisasi qo'llaniladi. JavaScript voqealari keyinroq ushbu darslikda batafsil muhokama qilinadi.

// Funktsiya sahifa funksiyasiga matn yozadi messageWrite() ( document.write("Ushbu matn sahifaga JavaScript yordamida yozilgan!"); )

O‘zgaruvchilarni funksiyalarga o‘tkazish

Siz funktsiyalarga cheksiz miqdordagi o'zgaruvchilarni o'tkazishingiz mumkin.

E'tibor bering: funktsiyalar ichidagi o'zgaruvchilar bilan barcha manipulyatsiyalar aslida o'zgaruvchilarning o'zida emas, balki ularning nusxalarida amalga oshiriladi, shuning uchun o'zgaruvchilarning mazmuni funktsiyalarni bajarish natijasida o'zgarmaydi.

/* O'tkazilgan o'zgaruvchiga 10 qo'shadigan va natijani sahifada ko'rsatadigan funksiyani aniqlaymiz */ function plus(a)( a=a+10; document.write("Funktsiya chiqishi: " + a+"
"); ) var a=25; document.write("Funktsiya chaqiruvidan oldingi o'zgaruvchining qiymati: "+a+"
"); // Funksiyaga a plus(a) o'zgaruvchisini berish orqali chaqiring; document.write("Funktsiya chaqirilgandan keyin o'zgaruvchining qiymati: "+a+"
");

Tez ko'rish

Global o‘zgaruvchiga funksiyaning nusxasidan emas, balki undan kirish uchun window.variable_name dan foydalaning.

Function plus(a)( window.a=a+10; ) var a=25; document.write("Funktsiya chaqiruvidan oldingi o'zgaruvchining qiymati: "+a+"
"); plus(a); document.write("Funktsiya chaqirilgandan keyin o'zgaruvchining qiymati: "+a+"
");

Tez ko'rish

qaytarish buyrug'i

Qaytish buyrug'i bilan siz funktsiyalardan qiymatlarni qaytarishingiz mumkin.

//sum funksiyasi o‘ziga berilgan o‘zgaruvchilar yig‘indisini qaytaradi sum(v1,v2)( return v1+v2; ) document.write("5+6=" + sum(5,6) + "
"); document.write("10+4=" + summa(10,4) + "
");

Tez ko'rish

O'rnatilgan funktsiyalar

Foydalanuvchi tomonidan belgilangan funktsiyalardan tashqari, JavaScript o'rnatilgan funktsiyalarga ham ega.

Masalan, o'rnatilgan isFinite funksiyasi o'tkazilgan qiymat haqiqiy raqam yoki yo'qligini tekshirish imkonini beradi.

Document.write(isFinite(40)+"
"); document.write(isFinite(-590)+"
"); document.write(isFinite(90.33)+"
"); document.write(isFinite(NaN)+"
"); document.write(isFinite("Bu string")+"
");

Tez ko'rish

Eslatma: to'liq ro'yxat o'rnatilgan JavaScript funktsiyalari Siz uni bizdan topishingiz mumkin.

Mahalliy va global o'zgaruvchilar

Funktsiyalar ichida yaratilgan o'zgaruvchilar mahalliy o'zgaruvchilar deb ataladi. Bunday o'zgaruvchilarga faqat ular aniqlangan funktsiyalar doirasida kirishingiz mumkin.

Funktsiya kodi bajarilishini tugatgandan so'ng, bunday o'zgaruvchilar yo'q qilinadi. Bu shuni anglatadiki, bir xil nomdagi o'zgaruvchilar turli funktsiyalarda aniqlanishi mumkin.

Funktsiya kodidan tashqarida yaratilgan o'zgaruvchilar global o'zgaruvchilar deb ataladi; bunday o'zgaruvchilarga kodning istalgan joyidan kirish mumkin.

Agar siz funktsiya ichida varsiz o'zgaruvchini e'lon qilsangiz, u ham global bo'ladi.

Global o'zgaruvchilar sahifa yopilgandan keyingina yo'q qilinadi.

//var1 va var2 global o'zgaruvchilarni e'lon qilish var var1="var1 mavjud"; var var2; func1() ( //Func1 funksiyasi ichida var2 qiymatini belgilash var var2="var2"; ) //Boshqa funksiyadan var1 va var2 oʻzgaruvchilari mazmunini func2() sahifa funksiyasiga chop eting ( //Chop etish var1 document.write oʻzgaruvchisining mazmuni( var1 + "
"); //var2 document.write(var2); ) oʻzgaruvchisining mazmunini chiqaring.

Tez ko'rish

E'tibor bering, ekranga chop etilganda var2 bo'sh qiymatga ega bo'ladi, chunki func1 var2 ning mahalliy "versiyasi" da ishlaydi.

Anonim funktsiyalardan foydalanish

E'lon qilinganida nomi bo'lmagan funksiyalar anonim deyiladi.

Anonim funksiyalar, asosan, oddiy funksiyalar kabi koddan chaqirilmaydi, balki parametr sifatida boshqa funksiyalarga o'tkazilishi e'lon qilinadi.

Funktsiya arrMap(arr,func)( var res=new Array; for (var i=0;i)