Функции на JavaScript. Експресивен JavaScript: Функциите функционираат како вредности

Луѓето мислат дека компјутерската наука е уметност за генијалци. Во реалноста, спротивното е точно - само многу луѓе прават работи што стојат еден врз друг, како да прават ѕид од мали камчиња.

Доналд Кнут

Веќе сте виделе функциски повици како што е предупредување. Функциите се леб и путер на програмирањето JavaScript. Идејата да се завитка дел од програма и да се нарече како променлива е многу популарна. Тоа е алатка за структурирање големи програми, намалување на повторувањето, доделување имиња на потпрограми и изолирање на потпрограмите една од друга.

Најочигледната употреба на функциите е создавање нов речник. Да се ​​дојде до зборови за обична човечка проза е лоша форма. Во програмски јазик, ова е неопходно.

Просечниот возрасен руски говорител знае околу 10.000 зборови. Редок програмски јазик содржи 10.000 вградени команди. И вокабуларот на програмскиот јазик е појасно дефиниран, па затоа е помалку флексибилен од човечкиот. Затоа, обично мораме да додадеме свои зборови за да избегнеме непотребно повторување.

Дефиниција на функцијата

Дефиницијата на функцијата е нормална дефиниција на променливата, каде што вредноста што ја добива променливата е функцијата. На пример, следниов код дефинира квадрат на променлива што се однесува на функција која го пресметува квадратот на даден број:

var квадрат = функција (x) ( врати x * x; ); console.log(square(12)); // → 144

Функцијата се креира со израз кој започнува со клучниот збор за функција. Функциите имаат збир на параметри (во овој случај, само x) и тело што ги содржи инструкциите што треба да се извршат кога функцијата се повикува. Телото на функцијата е секогаш затворено во кадрави загради, дури и ако се состои од една изјава.

Функцијата може да има неколку параметри или воопшто да нема. Во следниот пример, makeNoise нема листа на параметри, додека моќта има два:

Var makeNoise = function() ( console.log ("Fack!"); ); направи врева(); // → Глупости! var моќ = функција (база, експонент) ( var резултат = 1; за (var count = 0; count< exponent; count++) result *= base; return result; }; console.log(power(2, 10)); // → 1024

Некои функции враќаат вредност, како моќност и квадрат, други не, како makeNoise, што има само несакан ефект. Изјавата за враќање ја дефинира вредноста што ја враќа функцијата. Кога обработката на програмата ќе ја достигне оваа инструкција, таа веднаш излегува од функцијата и ја враќа оваа вредност на местото во кодот од каде е повикана функцијата. врати без израз се враќа недефинирано.

Параметри и опсег

Параметрите на функцијата се исто како променливите, но нивните почетни вредности се поставуваат кога се повикува функцијата, а не во нејзиниот код.

Важно својство на функциите е тоа што променливите создадени во рамките на функцијата (вклучувајќи ги и параметрите) се локални во таа функција. Ова значи дека во примерот за моќност, резултатот на променливата ќе се креира секогаш кога ќе се повика функцијата, а овие посебни инкарнации на неа не се поврзани едни со други на кој било начин.

Овој локалитет на променливи се однесува само на параметри и променливи создадени во функциите. Променливите поставени надвор од која било функција се нарекуваат глобални променливи бидејќи се видливи низ програмата. Можете исто така да пристапите до таквите променливи во рамките на функцијата, освен ако сте декларирале локална променлива со истото име.

Следниот код го илустрира ова. Тој дефинира и повикува две функции кои доделуваат вредност на x. Првиот ја декларира како локална, со што се менува само локалната променлива. Втората не декларира, така што работата со x внатре во функцијата се однесува на глобалната променлива x што беше поставена на почетокот на примерот.

var x = "надвор"; var f1 = функција () ( var x = "внатре во f1"; ); f1(); дневник на конзола (x); // → надвор var f2 = функција() ( x = "внатре во f2"; ); f2(); дневник на конзола (x); // → внатре во f2

Ова однесување помага да се спречи случајна интеракција помеѓу функциите. Ако сите променливи се користат некаде во програмата, би било многу тешко да се осигураме дека една променлива не се користи за различни цели. И ако повторно употребите променлива, ќе наидете на чудни ефекти каде кодот од трета страна се меша со вредностите на вашата променлива. Со третирање на функционалните локални променливи така што тие постојат само во рамките на функцијата, јазикот овозможува да се работи со функции како да се посебни мали универзуми, што ви овозможува да не се грижите за целиот код како целина.

Вгнездени опфати

JavaScript прави разлика не само помеѓу глобалните и локалните променливи. Функциите може да се дефинираат во рамките на функциите, што резултира со неколку нивоа на локалитет.

На пример, следната прилично бесмислена функција содржи уште две внатре:

var landscape = функција () ( var резултат = ""; var flat = функција (големина) ( за (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()); // → ___/""""\______/"\_

Функциите рамни и планински ја гледаат променливата резултат затоа што се наоѓаат во функцијата во која е дефинирана. Но, тие не можат да ги видат меѓусебните променливи за броење, бидејќи променливите на едната функција се надвор од опсегот на другата. И околината надвор од функцијата пејзаж не гледа ниту една од променливите дефинирани во оваа функција.

Накратко, во секој локален опсег, можете да ги видите сите опсези што го содржат. Множеството на променливи достапни во една функција се одредува според местото каде што оваа функција е декларирана во програмата. Сите променливи од блоковите што ја опкружуваат дефиницијата на функцијата се видливи - вклучувајќи ги и оние дефинирани на највисоко нивово главната програма. Овој пристап кон опфатите се нарекува лексички.

Луѓето кои студирале други програмски јазици можеби мислат дека секој блок затворен во кадрави загради создава своја локална средина. Но, во JavaScript, само функциите создаваат опсег. Можете да користите самостојни блокови:

var нешто = 1; ( var something = 2; // Направи нешто со променлива нешто... ) // Излез од блок...

Но, нешто внатре во блокот е иста променлива како и надвор. Иако таквите блокови се дозволени, има смисла да се користат само за ако изјавите и јамките.

Ако ова ви изгледа чудно, така не само вам. JavaScript 1.7 го воведе клучниот збор let, кој работи како var, но создава променливи кои се локални за секој даден блок, а не само за функцијата.

Функционира како вредности

Имињата на функциите обично се користат како име за дел од програмата. Таквата променлива еднаш се поставува и не се менува. Така, лесно е да се помеша функцијата со нејзиното име.

Но, ова се две различни работи. Повикот на функција може да се користи како едноставна променлива - на пример, тие можат да се користат во кој било израз. Можно е да се складира повик на функција во нова променлива, да се пренесе како параметар на друга функција итн. Исто така, променливата што го зачувува повикот на функцијата останува обична променлива и нејзината вредност може да се промени:

Var launchMissiles = функција (вредност) ( ​missileSystem. launch ("ве молиме!"); ); ако (safeMode) launchMissiles = функција (вредност) (/* ослободување */);

Во Поглавје 5, ќе разговараме за прекрасните работи што може да се направат со пренесување повици на функции на други функции.

Декларација за функција

Постои пократка верзија на изразот „var Square = функција…“. Функцискиот клучен збор може да се користи на почетокот на изјавата:

функција квадрат (x) (врти x * x; )

Ова е декларација за функција. Изјавата ја дефинира променливата квадрат и ѝ ја доделува дадената функција. Досега се е во ред. Има само една замка во таквата дефиниција.

Console.log("Иднината вели:", future()); функцијата future() (врати „СЕШТЕ немаме летечки автомобили“; )

Овој код работи иако функцијата е декларирана под кодот што ја користи. Ова е затоа што декларациите на функции не се дел од нормалното извршување на програмите од горе надолу. Тие „се движат“ на врвот на нивниот опсег и можат да бидат повикани со кој било код во тој опсег. Ова понекогаш е погодно затоа што можете да го напишете кодот по редослед што има најголема смисла, без да се грижите дали треба да ги дефинирате сите функции погоре каде што се користат.

Но, што се случува ако поставиме декларација за функција во условен блок или циклус? Не мора да го правите тоа. Историски гледано, различни платформи за водење JavaScript постапувале поинаку со такви случаи, а сегашниот јазичен стандард го забранува тоа. Ако сакате вашите програми да работат постојано, користете декларации на функции само во други функции или во главната програма.

Пример за функција () ( функција a() () // Нормала ако (нешто) (функција b() () // Ay-yy-yy! ) )

магацинот за повикување
Корисно е подетално да се погледне како функционира налогот за извршување со функциите. Еве едноставна програмасо повеќе функционални повици:

Функција greet(who) ( console.log ("Здраво, " + кој); ) greet("Semyon"); console.log ("Покеда");

Се обработува вака: повикувањето поздрав предизвикува пропусницата да скокне на почетокот на функцијата. Ја повикува вградената функција console.log, која ја презема контролата, го прави своето и ја враќа контролата. Потоа стигнува до крајот на поздравот, и се враќа на местото од кое е повикан. Следната линија повторно го повикува console.log.

Шематски, ова може да се прикаже на следниов начин:

Топ greet console.log поздрави врвот конзола.log top

Бидејќи функцијата мора да се врати од каде што е повикана, компјутерот мора да го запомни контекстот од кој е повикана функцијата. Во еден случај, console.log треба да се смени назад во поздрав. Во друга, се враќа на крајот на програмата.

Местото каде што компјутерот го памети контекстот се нарекува стек. Секој пат кога ќе се повика функцијата, тековниот контекст се турка на врвот на оџакот. Кога функцијата се враќа, таа го исфрла горниот контекст од оџакот и го користи за да продолжи.

Складирањето на оџакот бара мемориски простор. Кога оџакот ќе стане преголем, компјутерот престанува да се извршува и издава нешто како „прелевање на стек“ или „премногу рекурзија“. Следниот код го демонстрира тоа - му поставува на компјутерот многу сложено прашање што води до бескрајни скокови помеѓу две функции. Поточно, би биле бесконечни скокови доколку компјутерот има бесконечен стек. Во реалноста, оџакот се прелева.

Функција chicken() ( return egg(); ) функција egg() ( return chicken(); ) console.log(chicken() + " дојде на прво место."); // → ??

Факултативни аргументи
Следниот код е целосно легален и работи без проблеми:

Аларм ("Здраво", "Добра вечер", "Здраво на сите!");

Официјално, функцијата зема еден аргумент. Меѓутоа, кога вака ја предизвикуваат, таа не се жали. Ги игнорира останатите аргументи и покажува „Здраво“.

JavaScript е многу попустлив за бројот на аргументи предадени на функцијата. Ако поминете премногу, дополнителните ќе бидат игнорирани. Премалку - на оние што недостасуваат ќе им се додели вредноста недефинирана.

Недостаток на овој пристап е што е можно - па дури и веројатно - да се пренесе погрешен број аргументи на функцијата и никој нема да ви се жали за ова.

Добрата страна е што можете да креирате функции кои земаат опционални аргументи. На пример, во следната верзија на функцијата моќност, може да се повика и со два и со еден аргумент - во вториот случај, експонентот ќе биде еднаков на два, а функцијата работи како квадрат.

Моќност на функцијата (основа, експонент) ( ако (експонент == недефиниран) експонент = 2; var резултат = 1; за (број на var = 0; брои< exponent; count++) result *= base; return result; } console.log(power(4)); // → 16 console.log(power(4, 3)); // → 64

Во следното поглавје, ќе видиме како телото на функцијата може да ви го каже точниот број на аргументи предадени на неа. Ова е корисно затоа што ви овозможува да креирате функција која зема било кој број аргументи. На пример, console.log го користи ова својство и ги печати сите аргументи што му се доставени:

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

Затворања

Способноста да се користат повици на функции како променливи, заедно со фактот дека локалните променливи повторно се создаваат секогаш кога се повикува некоја функција, нè доведува до интересна точка. Што се случува со локалните променливи кога функцијата не успее?

Следниот пример го илустрира ова прашање. Ја декларира функцијата wrapValue, која создава локална променлива. Потоа враќа функција која ја чита таа локална променлива и ја враќа нејзината вредност.

Функција wrapValue(n) ( var localVariable = n; return функција() ( return localVariable; ); ) var wrap1 = wrapValue(1); var wrap2 = wrapValue(2); дневник на конзола (wrap1()); // → 1 конзола.log(wrap2()); // → 2

Ова е валидно и функционира како што треба - останува пристапот до променливата. Покрај тоа, повеќе примероци на иста променлива може да постојат во исто време, што дополнително го потврдува фактот дека локалните променливи се повторно креирани со секој повик на функција.

Оваа способност за работа со упатување на некој пример на локална променлива се нарекува затворање. Функцијата што ги затвора локалните променливи се нарекува функција за затворање. Не само што ве ослободува од грижите за променливиот век на траење, туку ви овозможува и креативно користење на функциите.

Со мала промена, нашиот пример го претвораме во функција која множи броеви со кој било даден број.

Функциски множител(фактор) (функција за враќање (број) (вратен број * фактор; ); ) var двапати = множител (2); console.log(twice(5)); // → 10

Посебна променлива како localVariable од примерот wrapValue повеќе не е потребна. Бидејќи параметарот сам по себе е локална променлива.

Потребна е пракса за да почнете да размислувате вака. Добра верзија на менталниот модел е да се замисли дека функцијата го замрзнува кодот во своето тело и го завиткува во пакет. Кога ќе ја видите функцијата за враќање (...) (...), замислете ја како контролен панел за парче код замрзнато за подоцна да се користи.

Во нашиот пример, мултипликаторот враќа замрзнато парче код што го складираме во променливата двапати. Последната линија ја повикува функцијата содржана во променливата, која го активира зачуваниот код (повратен број * фактор;). Сè уште има пристап до променливата фактор што беше дефинирана кога беше повикан множителот, а исто така има пристап до аргументот донесен за време на одмрзнување (5) како нумерички параметар.

рекурзија

Функцијата може да се повика себеси ако внимава да не го преполни оџакот. Таквата функција се нарекува рекурзивна. Еве пример за алтернативна имплементација на степенување:

Функција моќност(основа, експонент) ( ако (експонент == 0) враќа 1; друго враќање база * моќност(основа, експонент - 1); ) console.log(power(2, 3)); // → 8

Вака математичарите ја дефинираат експоненцијацијата, и можеби ова го опишува концептот поелегантно од циклусот. Функцијата се повикува себеси многу пати со различни аргументи за да постигне повеќекратно множење.

Сепак, оваа имплементација има проблем - во нормална средина JavaScript, таа е 10 пати побавна од верзијата со циклус. Јамката е поевтина од повикувањето функција.

Дилемата брзина наспроти елеганција е доста интересна. Постои одреден јаз помеѓу удобноста на човекот и удобноста на машината. Секоја програма може да се забрза со тоа што ќе ја направи поголема и посложена. Од програмерот се бара да ја најде вистинската рамнотежа.

Во случај на прва експоненцијација, неелегантната јамка е прилично едноставна и јасна. Нема смисла да се замени со рекурзија. Меѓутоа, честопати програмите работат со толку сложени концепти што некој сака да ја намали ефикасноста со зголемување на читливоста.

Основното правило, кое е повторено многу пати, и со кое целосно се согласувам - не грижете се за перформансите додека не се уверите дека програмата успорува. Ако е така, пронајдете ги деловите што траат најдолго и заменете ја елеганцијата за ефикасност таму.

Се разбира, не треба целосно да ги игнорираме перформансите веднаш. Во многу случаи, како и со експонентацијата, не добиваме голема едноставност од елегантните решенија. Понекогаш искусен програмер веднаш гледа дека едноставниот пристап никогаш нема да биде доволно брз.

Ова го кажувам затоа што премногу програмери почетници се држат до ефикасноста дури и во мали нешта. Резултатот е поголем, покомплексен и често не без грешки. На таквите програми им е потребно подолго време за пишување, и тие често работат не многу побрзо.

Но, рекурзијата не е секогаш само помалку ефикасна алтернатива на циклусите. Некои проблеми полесно се решаваат со рекурзија. Најчесто, ова е траверса од неколку гранки од дрвја, од кои секоја може да се разгранува.

Еве една загатка за вас: можете да добиете бесконечен број броеви, почнувајќи од бројот 1, а потоа или собирајќи 5 или множете се со 3. Како да напишеме функција која, даден број, се обидува да најде низа од такви собирање и множење што водат до даден број? На пример, бројот 13 може да се добие со прво множење на 1 со 3, а потоа додавање 5 двапати. А бројот 15 генерално е невозможно да се добие таков.

Рекурзивно решение:

Функција findSolution(цел) ( функција наоѓа 5)") || find(start * 3, "(" + history + " * 3)"); ) врати find(1, "1"); ) console.log(findSolution(24)); // → (((1 * 3) + 5) * 3)

Овој пример не го наоѓа нужно најкраткото решение - го задоволува секое. Не очекувам веднаш да разберете како функционира програмата. Но, ајде да дојдеме до дното на оваа одлична вежба за рекурзивно размислување.

Внатрешното наоѓање на функцијата е рекурзивно. Потребни се два аргументи - тековниот број и низа што содржи запис за тоа како стигнавме до тој број. И враќа или низа што ја покажува нашата низа чекори, или нула.

За да го направите ова, функцијата врши една од трите дејства. Ако дадената бројка е еднаква на целта, тогаш сегашната историја е само начин да се постигне, поради што се враќа. Ако дадениот број е поголем од целта, нема смисла да се продолжи со множење и собирање, бидејќи на овој начин само ќе се зголемува. И ако сè уште не сме ја достигнале целта, функцијата ги пробува двете можни патеки почнувајќи од дадениот број. Таа се повикува двапати, еднаш на секој начин. Ако првиот повик не се врати нула, се враќа. Во спротивно, вториот се враќа.

За подобро да разбереме како функцијата го постигнува посакуваниот ефект, да ги погледнеме нејзините повици што се јавуваат во потрага по решение за бројот 13.

Find(1, "1") find(6, "(1 + 5)") find(11, "((1 + 5) + 5)") find(16, "((1 + 5) + 5 ) + 5)") премногу големо откритие (33, "((1 + 5) + 5) * 3)") премногу големо откритие (18, "((1 + 5) * 3)") премногу големо откритие ( 3, "(1 * 3)") најде (8, "((1 * 3) + 5)") најде (13, "((1 * 3) + 5) + 5)") пронајдено!

Вовлекувањето ја покажува длабочината на купот на повици. Првиот пат кога функцијата за наоѓање се повикува двапати за да ги провери решенијата почнувајќи од (1 + 5) и (1 * 3). Првиот повик бара решение кое започнува со (1 + 5) и користи рекурзија за да ги провери сите решенија што даваат број помал или еднаков на саканиот број. Не го наоѓа и враќа нула. Потоа операторот || и скока до повик на функција што ја испитува опцијата (1 * 3). Овде имаме среќа, бидејќи во третиот рекурзивен повик добиваме 13. Овој повик враќа низа, и секоја од || ја поминува оваа низа погоре на патот, враќајќи го растворот како резултат.

Функции за растење

Постојат два повеќе или помалку природни начини за воведување на функции во програма.

Прво, пишувате сличен код повеќе пати. Ова треба да се избегнува - повеќе код значи повеќе простор за грешки и повеќе материјал за читање за оние кои се обидуваат да ја разберат програмата. Значи, земаме повторлива функционалност, и даваме добро име и ја ставаме во функција.

Вториот начин е да откриете потреба од некоја нова функционалност која вреди да се стави во посебна функција. Почнувате со името на функцијата, а потоа го пишувате нејзиното тело. Можете дури и да започнете со пишување на кодот што ја користи функцијата пред да се дефинира самата функција.

Колку ви е тешко да именувате функција покажува колку добро ја разбирате нејзината функционалност. Да земеме пример. Треба да напишеме програма која печати два броја, бројот на крави и кокошки на фармата, проследени со зборовите „крави“ и „кокошки“. Треба да додадете нули на броевите пред, така што секој зазема точно три позиции.

007 Крави 011 Кокошки

Очигледно, ни треба функција со два аргументи. Ајде да започнеме со кодирање.
// printFarmInventory функција printFarmInventory(крави, кокошки) ( var cowString = String(крави); додека (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);

Ако додадеме .length на низа, ја добиваме нејзината должина. Излегува дека јамките while додаваат водечки нули на броевите додека не добијат низа од 3 знаци.

Подготвени! Но, штом ќе ја испратиме шифрата до фармерот (заедно со голема проверка, се разбира), тој се јавува и ни кажува дека има свињи на фармата и дали можеме да го додадеме резултатот од бројот на свињи на програмата?

Секако дека е можно. Но, кога ќе почнеме да го копираме и залепуваме кодот од овие четири линии, сфаќаме дека треба да застанеме и да размислиме. Мора да има подобар начин. Се обидуваме да ја подобриме програмата:

// outputZeroPaddedWithLabel функција printZeroPaddedWithLabel(број, ознака) ( var numberString = String(број); додека (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);

Работи! Но, името printZeroPaddedWithLabel е малку чудно. Комбинира три работи - излез, нула полнење и етикета - во една функција. Наместо да го внесеме целиот фрагмент кој се повторува во функција, ајде да истакнеме еден концепт:

// додадете нули функција zeroPad(број, ширина) ( var string = String (број); додека (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);

Функцијата со убаво, описно име zeroPad го прави кодот полесен за разбирање. И може да се користи во многу ситуации, не само во нашиот случај. На пример, за прикажување форматирани табели со бројки.

Колку треба да бидат паметни и разновидни функциите? Можеме да напишеме едноставна функција која пополнува број со нули до три позиции, како и фенси функција за општа намена за форматирање броеви што поддржува дропки, негативни броеви, порамнување точки, пополнување со различни знаци итн.

Добро правило е да ја додадете само функционалноста што дефинитивно ви е потребна. Понекогаш е примамливо да се создадат рамки за општа намена за секоја мала потреба. Спротивстави му се. Никогаш нема да ја завршите работата, туку само напишете еден куп код што никој нема да го користи.

Функции и несакани ефекти

Функциите може грубо да се поделат на оние кои се нарекуваат поради нивните несакани ефекти и оние кои се повикани да добијат одредена вредност. Се разбира, исто така е можно да се комбинираат овие својства во една функција.

Првата помошна функција во примерот на фармата, printZeroPaddedWithLabel, се нарекува поради несаканиот ефект на печатење низа. Вториот, zeroPad, поради повратната вредност. И не е случајно што втората карактеристика ни доаѓа многу почесто од првата. Функциите што враќаат вредности полесно се комбинираат едни со други отколку функциите што создаваат несакани ефекти.

Чиста функција е посебен вид на функција за враќање на вредноста која не само што нема несакани ефекти, туку и не зависи од несаканите ефекти на остатокот од кодот - на пример, не работи со глобални променливи што може случајно да се променат на друго место. Чистата функција, кога се повикува со истите аргументи, го враќа истиот резултат (и не прави ништо друго) - што е прилично убаво. Лесно е да се работи со неа. Повикот на таква функција може ментално да се замени со резултатот од неговата работа, без да се менува значењето на кодот. Кога сакате да тестирате таква функција, можете едноставно да ја повикате и да бидете сигурни дека ако работи во овој контекст, ќе работи во која било. Не толку чистите функции можат да дадат различни резултати во зависност од многу фактори и да имаат несакани ефекти кои е тешко да се тестираат и да се земат предвид.

Сепак, не треба да се срами да пишува функции што не се сосема чисти, или да започне свето чистење на кодот од таквите функции. Несаканите ефекти често се корисни. Не постои начин да се напише чиста верзија на функцијата console.log и оваа функција е доста корисна. Некои операции полесно се изразуваат со користење на несакани ефекти.

Исход

Ова поглавје ви покажа како да ги напишете вашите сопствени функции. Кога клучниот збор на функцијата се користи како израз, враќа покажувач на повикот на функцијата. Кога се користи како изјава, можете да декларирате променлива со доделување на повик на функција на неа.

Клучот за разбирање на функциите е локалниот опсег. Параметрите и променливите декларирани внатре во функцијата се локални за неа, се рекреираат секогаш кога се повикуваат и не се видливи однадвор. Функциите декларирани во друга функција имаат пристап до нејзиниот опсег.

Многу е корисно да се поделат различните задачи што ги извршува програмата во функции. Не мора да се повторувате, функциите го прават кодот почитлив со тоа што го одвојуваат на семантички делови, на ист начин како што поглавјата и деловите од книгата помагаат да се организира обичен текст.

Вежби

Минимум
Во претходното поглавје беше спомната функцијата Math.min која го враќа најмалиот од нејзините аргументи. Сега можеме самите да напишеме таква функција. Напиши функција мин A што зема два аргументи и го враќа најмалиот од нив.

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

рекурзија
Видовме дека операторот % (остаток) може да се користи за да се одреди дали бројот е парен (% 2). Еве уште еден начин да се одреди:

Нулата е парна.
Единицата е чудна.
Секој број N има ист паритет како N-2.

Напишете рекурзивна функција isEven според овие правила. Мора да земе број и да врати булова вредност.

Тестирајте го на 50 и 75. Обидете се да му дадете -1. Зошто таа се однесува вака? Дали е можно да се поправи некако?

Тестирајте го на 50 и 75. Погледнете како се однесува на -1. Зошто? Можеш лисмислиш начин да се поправи ова?

Console.log(isEven(50)); // → true console.log(isEven(75)); // → лажна конзола.log(isEven(-1)); // → ??

Броиме грав.

Знакот број N на стрингот може да се добие со додавање на .charAt(N)("string".charAt(5)) на него, на сличен начин како да се добие должината на низа со .length. Повратната вредност ќе биде низа со еден знак (на пример, "k"). Првиот знак од низата има позиција 0, што значи дека последен ликпозицијата ќе биде низа.должина - 1. Со други зборови, низата со два знака ќе има должина 2, а нејзините позиции на знаци ќе бидат 0 и 1.

Напишете функција countBs која зема низа како аргумент и го враќа бројот на знаци „B“ во низата.

Потоа напишете функција countChar која работи малку како countBs, освен што е потребен втор параметар, знакот што ќе го бараме во низата (наместо само да го броиме бројот на знаци „B“). За да го направите ова, препишете ја функцијата countBs.

Прескокни изјави и ракување со исклучоци

Друга категорија на JavaScript јазични оператори се jump operators. Како што сугерира името, овие изјави предизвикуваат преведувачот JavaScript да скокне на друга локација во програмскиот код. Изјавата пауза предизвикува преведувачот да скокне до крајот на циклус или друга изјава. Изјавата за продолжување предизвикува преведувачот да го прескокне остатокот од телото на јамката, да скокне назад на почетокот на циклусот и да започне нова итерација. JavaScript има способност да ги означува изјавите со имиња, така што изјавите за прекин и продолжување можат експлицитно да се наведат на која јамка или друга изјава припаѓаат.

Изјавата враќање предизвикува преведувачот да скокне од повиканата функција назад до точката во која била повикана и да ја врати вредноста на повикот. Изјавата за фрлање покренува исклучок и е дизајнирана да работи во врска со исказите try/catch/finally кои дефинираат блок од код за справување со исклучокот. Ова е прилично комплициран вид на искази за скок: кога ќе се појави исклучок, толкувачот скока до најблискиот управувач со исклучоци, кој може да биде во истата функција или повисок, на повратниот оџак на повиканата функција.

Секој од овие оператори за скок е подетално опишан во следните потсекции.

Етикети за инструкции

Секоја изјава може да се означи со идентификатор и две точки пред него:

идентификатор: инструкција

Кога означувате инструкција, и давате име што потоа може да се користи како референца каде било во програмата. Можете да означите која било инструкција, но има смисла да означите само инструкции што имаат тело, како што се циклуси и условни искази.

Со давање име на јамката, таа потоа може да се користи во исказите за прекин и продолжување, внатре во циклусот за да излезете од неа, или да скокнете на почетокот на циклусот, до следната итерација. Изјавите за пауза и продолжување се единствените изјави во јазикот JavaScript што можат да содржат етикети - тие се подетално разгледани подоцна. Следното е пример за изјава while со ознака и изјава за продолжување со користење на таа ознака:

Главна јамка: while (токен != нула) ( // Кодот на програмата е испуштен... продолжи со главната јамка; // Оди на следната итерација на именуваната јамка )

Идентификаторот што се користи како ознака за исказ може да биде кој било валиден JavaScript идентификатор, освен резервиран збор. Имињата на етикетите се одвоени од имињата на променливите и функциите, така што можете да користите идентификатори што одговараат на имињата на променливите или функциите како етикети.

Етикетите за инструкции се дефинирани само во инструкциите на кои се применуваат (и, се разбира, во рамките на инструкциите вгнездени во нив). Вгнездените инструкции не можат да се означат со истите идентификатори како и инструкциите што ги содржат, но две независни инструкции може да се означат со иста ознака. Обележените инструкции може повторно да се означат. Односно, секоја инструкција може да има повеќе етикети.

изјава за пауза

Изјавата за пауза предизвикува најтешката изјава за јамка или прекинувач веднаш да излезе. Веќе видовме примери за користење на изјава за прекин во изјава за прекинувач порано. Во јамките, обично се користи за веднаш излегување од јамката кога, поради некоја причина, е неопходно да се прекине извршувањето на јамката.

Кога циклусот е многу сложена состојбазавршување, често е полесно да се имплементираат овие услови со исказ за пауза отколку да се обиде да ги изрази во еден условен циклус. Следниот пример се обидува да пронајде елемент на низа со одредена вредност. Јамката завршува на вообичаен начин кога ќе се достигне крајот на низата, или со изјавата за пауза, штом се најде саканата вредност:

Var arr = ["a", "b", "c", "d", "e"], резултат; за (var i = 0; i

Во JavaScript, можете да го наведете името на етикетата по клучниот збор за прекин (идентификатор без две точки):

скрши етикета_име;

Кога изјавата пауза се користи со ознака, таа скока до крајот на именуваната изјава или го прекинува неговото извршување. Во отсуство на инструкција со наведената ознака, обидот да се користи оваа форма на изјавата за пауза генерира синтаксна грешка. Именуваниот исказ не мора да биде исказ за јамка или прекинувач. Обележената изјава за пауза може да „избега“ од која било изјава што содржи. Приложената изјава може да биде дури и едноставен блок од искази затворени во кадрави загради со единствена цел да се означи.

Не може да се вметне знак за нова линија помеѓу клучниот збор за прекин и името на етикетата. Ова е затоа што преведувачот JavaScript автоматски вметнува запирки што недостасуваат: ако прекинете линија на код помеѓу клучниот збор за прекин и етикетата што следи, толкувачот ќе претпостави дека мислевте на едноставната форма на овој оператор без ознака и ќе додаде точка запирка. .

Обележената изјава за пауза е потребна само кога сакате да го скршите извршувањето на изјавата што не е најблиската заокружена јамка или изјава за прекинувач.

продолжи изјавата

Изјавата за продолжување е слична на изјавата пауза. Меѓутоа, наместо да излезе од јамката, изјавата продолжи започнува нова итерација на јамката. Синтаксата за изјавата за продолжување е едноставна како и синтаксата за изјавата пауза. Изјавата продолжи може да се користи и со ознака.

Изјавата за продолжување, без разлика дали е неозначена или означена, може да се користи само во телото на јамката. Користењето на кое било друго место резултира со синтаксна грешка. Кога ќе се изврши изјавата за продолжување, тековната итерација на јамката се прекинува и започнува следната. За различни типовициклусите значат различни работи:

    Во циклусот while, изразот наведен на почетокот на циклусот повторно се проверува, а ако е точно, телото на циклусот се извршува од почеток.

    Јамката do/while скока до крајот на циклусот, каде што состојбата повторно се проверува пред циклусот да се повтори.

    Во јамката for, изразот за зголемување се оценува и тест изразот повторно се оценува за да се утврди дали треба да се изврши следното повторување.

    Во јамката for/in, јамката започнува одново, со доделување на наведената променлива името на следното својство.

Забележете ја разликата во однесувањето на изјавата продолжи во јамките while и for. Јамката while директно се враќа во нејзината состојба, додека јамката for прво го проценува изразот на зголемување, а потоа се враќа во состојбата. Следниот пример ја покажува употребата на неозначена изјава за продолжување за да излезете од тековната итерација на јамка за парни броеви:

var збир = 0; // Пресметај го збирот на непарните броеви од 0 - 10 за (var i = 0; i

Изјавата за продолжување, како прекин, може да се користи во вгнездени јамки во форма што вклучува ознака, во кој случај рестартираната јамка не мора да биде онаа што веднаш ја содржи изјавата за продолжување. Исто така, како и кај паузата, новите линии помеѓу клучниот збор продолжи и името на етикетата не се дозволени.

изјава за враќање

Повикот на функција е израз, и како и сите изрази, има вредност. Изјавата за враќање внатре во функциите се користи за одредување на вредноста што ја враќа функцијата. Изјавата за враќање може да се стави само во телото на функцијата. Неговото присуство на кое било друго место е синтаксна грешка. Кога ќе се изврши изјава за враќање, функцијата ја враќа вредноста на изразот во програмата што повикува. На пример:

Ако функцијата нема изјава за враќање, кога ќе се повика, толкувачот ќе ги изврши инструкциите во телото на функцијата една по една додека не стигне до крајот на функцијата, а потоа ќе ја врати контролата на програмата што ја повикала. Во овој случај, изразот на повикот ќе се врати недефиниран. Изјавата за враќање е често последната инструкцијаво функција, но ова е целосно опционално: функцијата ќе ја врати контролата на програмата што повикува веднаш штом ќе се достигне изјавата за враќање, дури и ако е проследена со други искази во телото на функцијата.

Изјавата враќање може да се користи и без израз, во тој случај едноставно ја прекинува функцијата и недефинирано се враќа на повикувачот. На пример:

Функција myFun(arr) ( // Ако низата содржи негативни броеви, прекинете ја функцијата за (var i = 0; i

фрли изјава

Исклучоке сигнал кој укажува на појава на некој вид исклучок или грешка. Подигнување исклучок (фрлање)е начин да се сигнализира таква грешка или исклучок. Да се ​​фати исклучок (фати) значи да се справи со него, т.е. преземете мерки неопходни или соодветни за да се опоравите од исклучокот.

Во JavaScript, исклучоците се исклучуваат кога ќе се појави грешка во времето на извршување и кога програмата експлицитно ја подигнува со изјавата за фрлање. Исклучоците се фатени со користење на исказите try/catch/finally, кои се опишани подоцна.

Изјавата за фрлање ја има следнава синтакса:

фрли израз;

Резултатот од изразот може да биде вредност од кој било тип. На изјавата за фрлање може да се пренесе број што го претставува кодот за грешка или низа што го содржи текстот на пораката за грешка. JavaScript толкувачот фрла исклучоци користејќи пример од класа грешкаедна од нејзините подкласи, а вие исто така можете да користите сличен пристап. Објектот Error има својство име, кој го дефинира типот на грешка и својството поракаА што ја содржи низата е предадена на функцијата конструктор. Следното е пример за функција која покренува објект Error кога се повикува со неважечки аргумент:

// Факториал на функција на бројна функција фактор (број) ( // Ако влезниот аргумент не е валидна вредност, // исклучок е исклучен! ако (број 1; i *= број, број--); /* празен тело на јамка */ return i ; ) конзола.log("5! = ", фактор(5)); log("-3! = ", фактор (-3));

Кога ќе се исфрли исклучок, преведувачот JavaScript веднаш го прекинува нормалното извршување на програмата и скока до најблискиот управувач со исклучоци. Ракувачите со исклучоци ја користат изјавата за фаќање на конструкцијата try/catch/finally, која е опишана во следниот дел.

Ако блокот код во кој се случил исклучокот нема соодветна конструкција за фаќање, толкувачот го анализира следниот надворешен блок код и проверува дали управувачот со исклучоци е поврзан со него. Ова продолжува додека не се најде управувачот.

Ако исклучок е фрлен во функција која не содржи обиди/фати/конечно конструкција за да се справи со него, тогаш исклучокот се пропагира во кодот што ја повикал функцијата. Ова е начинот на кој исклучоците се шират низ лексичката структура на методите на JavaScript до стекот на повици. Ако никогаш не се најде управувач со исклучоци, исклучокот се третира како грешка и се известува на корисникот.

обиди / фати / конечно конструирај

Конструкцијата try/catch/finally го имплементира механизмот за справување со исклучоци на JavaScript. пробај изјававо оваа конструкција едноставно дефинира блок од код во кој се постапуваат со исклучоци. Проб блокот е проследен со изјава за фатисо блок од искази што треба да се повикаат ако се појави исклучок каде било во блокот обид. Изјавата за фаќање е проследена со блок конечно A што содржи код што ги извршува последните операции и гарантирано ќе работи без оглед на тоа што се случува во блокот обид.

И блокот за фаќање и блокот за крај се опционални, но барем еден од нив мора да биде присутен по блокот обид. пробај, фати и конечно блокови почнуваат и завршуваат виткани протези. Ова е задолжителен дел од синтаксата и не може да се изостави дури и ако има само една изјава меѓу нив.

Следниот фрагмент ја илустрира синтаксата и целта на конструкцијата try/catch/finally:

Обидете се ( // Нормално, овој код ќе работи непречено од почеток до крај. // Но, во одреден момент, може да направи исклучок, // или директно со изјавата за фрлање, или индиректно, // со повикување на методот што го фрла Исклучок. вредноста наведена во изјавата за фрлање. // Овој блок може или да се справи со исклучокот на некој начин, или // да го игнорира и да направи нешто друго, или // да го врати исклучокот со изјава за фрлање. ) конечно ( // Овој блок содржи изјави кои секогаш се извршуваат, без разлика дали , // што се случило во блокот обид Тие се извршуваат ако блокот обид заврши: // 1) како и обично, достигнувајќи го крајот на блокот // 2) поради прекин, продолжи или враќање изјави // 3) со исклучок постапува од во блокот за фаќање погоре // 4) со нефатен исклучок што продолжува // да се шири на повисоки нивоа)

Забележете дека клучниот збор catch е проследен со идентификатор во загради. Овој идентификатор е сличен на параметар на функција. Кога е фатен исклучок, овој параметар ќе биде поставен на исклучок (на пример, објект Грешка). За разлика од нормалната променлива, идентификаторот поврзан со изјавата за фаќање постои само во телото на блокот за фаќање.

Следното е пореален пример за конструкција обиди/фати. Го повикува методот factorial() дефиниран во претходниот пример и методите prompt() и alert() на JavaScript од страна на клиентот за организирање на влезот и излезот:

Обидете се ( // Побарајте од корисникот број var n = Number(prompt("Внесете позитивен број", "")); // Пресметајте го факторот на број, под претпоставка дека // влезот е валиден var f = фактор( n); // Печатете го предупредувањето за резултатот(n + "! = " + f); ) catch (ex) ( // Ако податоците се неточни, контролата ќе се пренесе овде alert(ex); // Известете го корисникот за грешката)

Ако корисникот внесе негативен број, ќе се прикаже предупредувачка порака:

Ова е пример за обиди/фати конструкција без конечна изјава. Иако конечно не се користи толку често колку уловот, сепак понекогаш е корисен. Конечниот блок е загарантиран да се изврши ако барем некој дел од блокот обид е извршен, без оглед на тоа како завршил кодот во блокот обид. Оваа функција обично се користи за извршување на последните операции откако кодот е извршен во продолжение на обидот.

Во нормална ситуација, контролата стигнува до крајот на блокот обид и потоа скока до крајниот блок, кој ги извршува потребните завршни операции. Ако контролата излезе од блок обид како резултат на изјава за враќање, продолжување или прекин, конечното блок се извршува пред контролата да се пренесе на друго место.

Ако се појави исклучок во обид блок и има соодветен блок за фаќање за да се справи со него, контролата прво се пренесува на блокот за фаќање, а потоа на конечното блокирање. Ако нема локален блок за фаќање, тогаш контролата прво преминува на крајниот блок, а потоа скока до најблискиот надворешен блок за фаќање што може да се справи со исклучокот.

Ако самиот конечен блок ја пренесува контролата со користење на изјава за враќање, продолжување, прекин или исфрлање или со повикување метод кој исклучува исклучок, командата за пренос на чекање се откажува и се извршува нова. На пример, ако конечниот блок фрла исклучок, тој исклучок ќе го замени секој претходно исклучен исклучок.

Оператор враќањеја прекинува тековната функција и ја враќа нејзината вредност.

Изворниот код за овој интерактивен пример е зачуван во складиште на GitHub. Ако сакате да придонесете во проектот за интерактивни примери, ве молиме клонирајте https://github.com/mdn/interactive-examples

Синтакса

враќање [[израз]]; израз Изразот чија вредност ќе се врати. Ако не е одредено, наместо тоа се враќа undefined.

Опис

Кога изјавата за враќање се повикува во функција, нејзиното извршување престанува. Наведената вредност се враќа на местото каде што е повикана функцијата. На пример, следнава функција ја враќа квадратната вредност на нејзиниот аргумент, x (каде x е број):

функција квадрат (x) ( врати x * x; ) var демо = квадрат (3); // демо вредноста ќе биде 9

Ако не е наведена повратна вредност, наместо тоа се враќа undefined.

Следниве изрази секогаш го завршуваат извршувањето на функцијата:

враќање; врати вистина; врати лажни; врати x; врати x + y / 3;

Автоматски запирки

функцијата магија(x) (функцијата за враќање калц(x) (врати x * 42); ) var одговор = магија(); одговор (1337); // 56154

Спецификации

Спецификација Статус Коментар
ECMAScript 1-во издание (ECMA-262) Стандарден оригинална дефиниција
ECMAScript 5.1 (ECMA-262)
Стандарден
ECMAScript 2015 (6-то издание, ECMA-262)
Дефиниција за „Изјава за враќање“ во оваа спецификација.
Стандарден
Најновиот нацрт ECMAScript (ECMA-262)
Дефиниција за „Изјава за враќање“ во оваа спецификација.
Нацрт

Компатибилност со прелистувач

Табелата за компатибилност на оваа страница е генерирана од структурирани податоци. Ако сакате да придонесете за податоците, проверете ги од https://github.com/mdn/browser-compat-data складиштето и испратете ни барање за повлекување за вашите промени.

Ажурирајте ги податоците за компатибилност на GitHub

КомпјутериМобиленсервер
ХромрабFirefoxInternet ExplorerОперасафариандроид веб-прегледХром за АндроидFirefox за АндроидОпера за АндроидСафари на iOSСамсунг ИнтернетЈазол.js
враќањеХром Целосна поддршка 1 раб Целосна поддршка 12 Firefox Целосна поддршка 1 т.е Целосна поддршка 3 Опера Целосна поддршкаДасафари Целосна поддршкаДавеб-преглед на андроид Целосна поддршка 1 Хром Андроид Целосна поддршка 18 Firefox Андроид Целосна поддршка 4 OperaAndroid Целосна поддршкаДаSafari iOS Целосна поддршкаДаСамсунг Интернет Андроид Целосна поддршка 1.0 јазли Целосна поддршкаДа

Функциите се еден од најважните градежни блокови на код во JavaScript.

Функциите се состојат од збир на команди и обично извршуваат една специфична задача (на пример, собирање броеви, пресметување корен итн.).

Кодот поставен во функција ќе се изврши само по експлицитниот повик до таа функција.

Декларација за функција

1.Синтакса:

//Функција за декларација на функцијата FunctionName(var1, var2)( Код на функција ) //Повикување на функцијата FunctionName(var1,var2);

2. Синтакса:

//Декларација на функција var име на функција=функција(var1, var2)(Шифра на функција) //Име на функција за повикување на функција(var1,var2);

име на функцијаго одредува името на функцијата. Секоја функција на страницата мора да има единствено име. Мора да се даде име на функцијата со латински буквии не смее да започнува со бројки.

на 1И на 2се променливи или вредности кои можат да се пренесат во функција. На секоја функција може да се пренесат неограничен број променливи.

Забелешка:дури и ако ниедна променлива не е предадена на функцијата, не заборавајте да вметнете загради „()“ по името на функцијата.

Забелешка:Имињата на функциите во JavaScript се осетливи на букви.

Пример за функцијата JavaScript

Функцијата messageWrite() во примерот подолу ќе се изврши само откако ќе се кликне на копчето.

Забелешка:овој пример го користи настанот onclick. Настаните на JavaScript ќе бидат детално опфатени подоцна во ова упатство.



Предавање на променливи во функции

Можете да пренесете неограничен број променливи на функциите.

Забелешка:сите манипулации со променливите внатре во функциите всушност не се вршат на самите променливи, туку на нивната копија, така што содржината на самите променливи не се менува како резултат на извршувањето на функциите.

/* Дефинирајте функција која додава 10 на положената променлива и го дава резултатот на страницата */ функција плус(а)( a=a+10; document.write("Излез на функција: " + a+"
"); ) var a=25; document.write("Вредност на променливата пред повикот на функцијата: "+a+"
"); // Наречете ја функцијата што ја пренесува променливата a plus(a); document.write("Вредноста на променливата по повикувањето на функцијата: "+a+"
");

Брз преглед

За да пристапите до глобална променлива од функција наместо од нејзина копија, користете прозорец.име на променлива.

Функција plus(a)( window.a=a+10; ) var a=25; document.write("Вредност на променлива пред повик на функција: "+a+"
"); plus(a); document.write ("Вредност на променлива по повикот на функцијата: "+a+"
");

Брз преглед

команда за враќање

Со командата враќањеМожете да вратите вредности од функциите.



Брз преглед

Вградени функции

Во прилог на кориснички дефинирани функции во JavaScript, постојат и вградени функции.

На пример, вградената функција е Конечнови овозможува да проверите дали донесената вредност е валиден број.

Document.write(isFinite(40)+"
"); document.write(isFinite(-590)+"
"); document.write(isFinite(90.33)+"
"); document.write(isFinite(NaN)+"
"); document.write(isFinite("Ова е низа")+"
");

Брз преглед

Забелешка: целосна листавградени JavaScript функции што можете да ги најдете во нашата .

Локални и глобални променливи

Се повикуваат променливите создадени внатре во функциите локални променливи. Можете да пристапите до таквите променливи само во рамките на функциите во кои се дефинирани.

По извршувањето на функцискиот код, таквите променливи се уништуваат. Ова значи дека променливите со исто име може да се дефинираат во различни функции.

Се повикуваат променливите кои се креирани надвор од функцискиот код глобални променливидо таквите променливи може да се пристапи од каде било во кодот.

Ако декларирате променлива без var во функција, таа исто така станува глобална.

Глобалните променливи се уништуваат само кога страницата е затворена.



Брз преглед

Забелешка:кога ќе се прикаже, var2 ќе биде нула бидејќи func1 работи на локалната „верзија“ на var2.

Користење на анонимни функции

Се повикуваат функциите кои не содржат име кога се декларираат анонимни.

Анонимните функции во основа се декларирани не за нивното последователно повикување од кодот како обични функции, туку за преминување на други функции како параметар.

Функција arrMap(arr,func)(var res=нова низа; за (var i=0;i ");

Брз преглед

Направи го сам

Вежба 1. Поправете ги грешките во кодот подолу:

Вежба 1

Поправете ја грешката во кодот.



Задача 2.

  1. Репродуцирајте го кодот на функциите 1-3 со испитување на нивното однесување при пренесување на различни параметри.
  2. Одредете го клучниот збор со интеракција со функцијата 4.

Задача 2

//Повикај ја првата тајна функција document.write(secfunc1(4,12) + "
"); // Повикување на втората тајна функција document.write(secfunc2(100,10) + "
"); //Повикај ја третата тајна функција secfunc3(23,10); document.write("
"); // Повикување на четвртата тајна функција secfunc4("n");

Овозможете JavaScript за користење на системот за коментирање на Disqus.




Врв