Функциите са един от най-важните градивни елементи на кода в JavaScript.

Функциите се състоят от набор от команди и обикновено изпълняват една конкретна задача (например сумиране на числа, изчисляване на корен и т.н.).

Кодът, поставен във функция, ще бъде изпълнен само след изричното извикване на тази функция.

Декларация на функция

1.Синтаксис:

//Декларация на функция function FunctionName(var1, var2)( Код на функция ) //Извикване на функция FunctionName(var1,var2);

2. Синтаксис:

//Декларация на функция var functionname=function(var1, var2)(Function code) //Извикване на функция functionname(var1,var2);

име на функцияуказва името на функцията. Всяка функция на страницата трябва да има уникално име. Името на функцията трябва да бъде посочено с латински букви и не трябва да започва с цифри.

per1и per2са променливи или стойности, които могат да бъдат предадени във функция. Към всяка функция могат да бъдат подавани неограничен брой променливи.

Забележка:дори ако на функцията не са предадени променливи, не забравяйте да поставите скоби "()" след името на функцията.

Забележка:Имената на функциите в JavaScript са чувствителни към главни и малки букви.

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

Функцията messageWrite() в примера по-долу ще бъде изпълнена само след щракване върху бутона.

Забележка:този пример използва събитието onclick. JavaScript събитияще бъдат обсъдени подробно по-късно в този урок.

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

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

Забележка:всички манипулации върху променливи във функции всъщност се извършват не върху самите променливи, а върху тяхното копие, така че съдържанието на самите променливи не се променя в резултат на изпълнението на функциите.

/* Дефиниране на функция, която добавя 10 към подадената променлива и извежда резултата на страницата */ function plus(a)( 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=нов масив; for (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.

Хората смятат, че компютърните науки са изкуство за гении. В действителност е точно обратното – просто много хора правят неща, които стоят едно върху друго, сякаш съставят стена от малки камъчета.

Доналд Кнут

Вече сте виждали извиквания на функции като alert. Функциите са хлябът и маслото на програмирането с JavaScript. Идеята за обвиване на част от програма и извикването й като променлива е много популярна. Това е инструмент за структуриране на големи програми, намаляване на повторенията, присвояване на имена на подпрограми и изолиране на подпрограми една от друга.

Най-очевидното използване на функции е създаването на нов речник. Измислянето на думи за обикновена човешка проза е лоша форма. В езика за програмиране това е необходимо.

Средният възрастен, говорещ руски език, знае около 10 000 думи. Рядък език за програмиране съдържа 10 000 вградени команди. И речникът на езика за програмиране е по-ясно дефиниран, така че е по-малко гъвкав от човешкия. Затова обикновено трябва да добавим собствени думи към него, за да избегнем ненужно повторение.

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

Дефиницията на функция е нормална дефиниция на променлива, където стойността, която променливата получава, е функцията. Например, следният код дефинира променлива квадрат, която препраща към функция, която изчислява квадрата на дадено число:

var square = function(x) ( return x * x; ); console.log(квадрат(12)); // → 144

Функция се създава от израз, започващ с ключовата дума function. Функциите имат набор от параметри (в този случай само x) и тяло, съдържащо инструкциите, които трябва да бъдат изпълнени, когато функцията бъде извикана. Тялото на функция винаги е затворено във фигурни скоби, дори ако се състои от един оператор.

Една функция може да има няколко параметъра или нито един. В следния пример makeNoise няма списък с параметри, докато power има два:

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

Някои функции връщат стойност, като мощност и квадрат, други не, като makeNoise, което има само страничен ефект. Операторът return дефинира стойността, върната от функцията. Когато програмната обработка достигне тази инструкция, тя незабавно излиза от функцията и връща тази стойност на мястото в кода, откъдето е извикана функцията. return без израз връща undefined.

Параметри и обхват

Функционалните параметри са точно като променливите, но първоначалните им стойности се задават при извикването на функцията, а не в нейния код.

Важно свойство на функциите е, че променливите, създадени във функция (включително параметри), са локални в рамките на тази функция. Това означава, че в примера за мощност резултатната променлива ще се създава при всяко извикване на функцията и тези отделни нейни въплъщения не са свързани помежду си по никакъв начин.

Тази локализация на променливите се отнася само за параметри и променливи, създадени във функции. Променливите, зададени извън която и да е функция, се наричат ​​глобални променливи, защото са видими в цялата програма. Можете също така да получите достъп до такива променливи в рамките на функция, освен ако не сте декларирали локална променлива със същото име.

Следният код илюстрира това. Той дефинира и извиква две функции, които присвояват стойност на x. Първият го декларира като локален, като по този начин променя само локалната променлива. Вторият не декларира, така че работата с x във функцията се отнася до глобалната променлива x, която е зададена в началото на примера.

var x = "извън"; var f1 = function() ( var x = "вътре в f1";); f1(); конзолен дневник (x); // → извън var f2 = function() ( x = "inside f2"; ); f2(); конзолен дневник (x); // → вътре в f2

Това поведение помага за предотвратяване на случайно взаимодействие между функциите. Ако всички променливи се използват навсякъде в програмата, би било много трудно да се гарантира, че една променлива не се използва за различни цели. И ако трябваше да използвате повторно променлива, ще се натъкнете на странни ефекти, при които кодът на трета страна се забърква със стойностите на вашата променлива. Чрез третиране на променливи, локални за функциите, така че да съществуват само във функцията, езикът позволява да се работи с функции, сякаш са отделни малки вселени, което ви позволява да не се притеснявате за целия код като цяло.

Вложени обхвати

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

Например, следната доста безсмислена функция съдържа още две вътре:

var landscape = function() ( var result = ""; var flat = function(size) ( for (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 something = 1; ( var something = 2; // Направете нещо с променлива something... ) // Изход от блока...

Но нещо вътре в блока е същата променлива като отвън. Въпреки че такива блокове са разрешени, има смисъл да се използват само за оператори if и цикли.

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

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

Имената на функции обикновено се използват като име за част от програма. Такава променлива се задава веднъж и не се променя. Така че е лесно да объркате функция с нейното име.

Но това са две различни неща. Извикването на функция може да се използва като проста променлива - например те могат да се използват във всеки израз. Възможно е да съхраните извикване на функция в нова променлива, да я прехвърлите като параметър на друга функция и т.н. Освен това променливата, която съхранява извикването на функцията, остава обикновена променлива и нейната стойност може да се променя:

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

В Глава 5 ще обсъдим прекрасните неща, които могат да бъдат направени чрез предаване на извиквания на функции към други функции.

Декларация на функция

Има по-кратка версия на израза „var square = function…“. Ключовата дума функция може да се използва в началото на израз:

функция square(x) ( return x * x; )

Това е декларация на функция. Операторът дефинира променливата square и й присвоява дадената функция. Засега всичко е наред. Има само един подводен камък в такова определение.

Console.log("Бъдещето казва:", бъдеще()); функция бъдеще() (връщане "Ние ВСЕ ОЩЕ нямаме летящи коли.";)

Този код работи, въпреки че функцията е декларирана под кода, който я използва. Това е така, защото декларациите на функциите не са част от нормалното изпълнение на програмите отгоре надолу. Те се "преместват" в горната част на своя обхват и могат да бъдат извикани от всеки код в този обхват. Това понякога е удобно, защото можете да напишете кода в реда, който има най-голям смисъл, без да се налага да се притеснявате, че трябва да дефинирате всички функции по-горе, където се използват.

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

Функция example() ( function a() () // Normule if (something) ( function b() () // Ay-yy-yy! ) )

стек за повиквания
Полезно е да разгледате по-отблизо как редът за изпълнение работи с функциите. Ето една проста програма с няколко извиквания на функции:

Функция greet(who) ( console.log("Hi, " + who); ) greet("Semyon"); console.log("Pokeda");

Обработва се по следния начин: извикването на greet кара пропуска да прескочи до началото на функцията. Той извиква вградената функция console.log, която поема контрола, върши работата си и връща контрола. След това стига до края на поздрава и се връща на мястото, откъдето е извикано. Следващият ред отново извиква console.log.

Схематично това може да се покаже по следния начин:

Отгоре поздравете console.log поздравете отгоре console.log отгоре

Тъй като функцията трябва да се върне там, откъдето е извикана, компютърът трябва да запомни контекста, от който е извикана функцията. В един случай console.log трябва да се промени обратно на поздрав. В друг се връща към края на програмата.

Мястото, където компютърът запомня контекста, се нарича стек. Всеки път, когато функцията бъде извикана, текущият контекст се избутва в горната част на стека. Когато функцията се върне, тя изважда горния контекст от стека и го използва, за да продължи.

Съхранението на стека изисква място в паметта. Когато стекът стане твърде голям, компютърът спира да се изпълнява и издава нещо като „препълване на стека“ или „твърде много рекурсия“. Следният код демонстрира това - той задава на компютъра много сложен въпрос, който води до безкрайни скокове между две функции. По-точно, това биха били безкрайни скокове, ако компютърът имаше безкраен стек. В действителност стекът препълва.

Функция chicken() ( return egg(); ) function egg() ( return chicken(); ) console.log(chicken() + " дойде първи."); // → ??

Незадължителни аргументи
Следният код е напълно легален и работи без проблеми:

Alert("Здравейте", "Добър вечер", "Здравейте на всички!");

Официално функцията приема един аргумент. Въпреки това, когато е предизвикана по този начин, тя не се оплаква. Той игнорира останалите аргументи и показва „Здравей“.

JavaScript е много снизходителен по отношение на броя на аргументите, предавани на функция. Ако преминете твърде много, допълнителните ще бъдат игнорирани. Твърде малко - на липсващите ще бъде присвоена недефинирана стойност.

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

Предимството е, че можете да създавате функции, които приемат незадължителни аргументи. Например в следващата версия на степенната функция тя може да бъде извикана както с два, така и с един аргумент - във втория случай експонентата ще бъде равна на две и функцията работи като квадрат.

Функционална степен (основа, експонента) ( if (exponent == undefined) exponent = 2; var result = 1; for (var count = 0; count< 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 function() ( return localVariable; ); ) var wrap1 = wrapValue(1); var wrap2 = wrapValue(2); конзолен дневник (wrap1()); // → 1 console.log(wrap2()); // → 2

Това е валидно и работи както трябва - достъпът до променливата остава. Освен това, множество екземпляри на една и съща променлива могат да съществуват едновременно, което допълнително потвърждава факта, че локалните променливи се създават отново с всяко извикване на функция.

Тази способност за работа с препратка към някакъв екземпляр на локална променлива се нарича затваряне. Функция, която затваря локални променливи, се нарича затваряща функция. Не само ви освобождава от безпокойството за променлив живот, но също така ви позволява да използвате функциите творчески.

С лека промяна превръщаме нашия пример във функция, която умножава числата по произволно дадено число.

Функция multiplier(factor) ( return function(number) ( return number * factor; ); ) var two = multiplier(2); console.log(два пъти(5)); // → 10

Вече не е необходима отделна променлива като localVariable от примера wrapValue. Тъй като самият параметър е локална променлива.

Необходима е практика, за да започнете да мислите така. Добра версия на менталния модел е да си представите, че функцията замразява кода в тялото си и го обвива в пакет. Когато видите return function(...) (...), мислете за това като за контролен панел за част от кода, замразен за използване по-късно.

В нашия пример множителят връща замразена част от кода, която съхраняваме в променливата two. Последният ред извиква функцията, съдържаща се в променливата, която активира записания код (връща номер * фактор;). Той все още има достъп до факторната променлива, която е била дефинирана при извикването на множителя, и също така има достъп до аргумента, подаден по време на unfreeze (5) като числов параметър.

рекурсия

Една функция може да се самоизвика, ако се погрижи да не препълни стека. Такава функция се нарича рекурсивна. Ето пример за алтернативно изпълнение на степенуване:

Функция степен (основа, експонента) ( if (експонента == 0) връща 1; иначе връща основа * степен (основа, степен - 1); ) console.log(power(2, 3)); // → 8

Ето как математиците определят степенуването и може би това описва концепцията по-елегантно от цикъл. Функцията се извиква много пъти с различни аргументи, за да постигне многократно умножение.

Тази имплементация обаче има проблем – в нормална JavaScript среда е 10 пъти по-бавна от версията с цикъл. Цикълът е по-евтин от извикването на функция.

Дилемата скорост срещу елегантност е доста интересна. Има известна разлика между удобството на човека и удобството на машината. Всяка програма може да бъде ускорена, като стане по-голяма и по-сложна. От програмиста се изисква да намери правилния баланс.

В случай на първо степенуване, неелегантният цикъл е доста прост и ясен. Няма смисъл да се заменя с рекурсия. Често обаче програмите работят с толкова сложни концепции, че човек иска да намали ефективността чрез увеличаване на четливостта.

Основното правило, което е повтаряно многократно и с което съм напълно съгласен - не се притеснявайте за производителността, докато не сте сигурни, че програмата се забавя. Ако е така, намерете частите, които издържат най-дълго и заменете елегантност с ефективност там.

Разбира се, не бива напълно да пренебрегваме производителността веднага. В много случаи, както при степенуването, не получаваме много простота от елегантните решения. Понякога опитен програмист вижда веднага, че простият подход никога няма да бъде достатъчно бърз.

Повдигам това, защото твърде много начинаещи програмисти държат на ефективността дори в дребни неща. Резултатът е по-голям, по-сложен и често не без грешки. Такива програми отнемат повече време за писане и често работят не много по-бързо.

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

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

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

Функция findSolution(target) ( функция find(start, history) ( if (start == target) връща история; else if (start > target) return null; else return find(start + 5, "(" + history + " + 5)") || find(start * 3, "(" + history + " * 3)"); ) return find(1, "1"); ) console.log(findSolution(24)); // → (((1 * 3) + 5) * 3)

Този пример не намира непременно най-краткото решение - той удовлетворява всички. Не очаквам веднага да разберете как работи програмата. Но нека стигнем до дъното на това страхотно упражнение по рекурсивно мислене.

Намирането на вътрешна функция е рекурсивно. Необходими са два аргумента - текущото число и низ, който съдържа запис за това как сме стигнали до това число. И връща или низ, показващ нашата последователност от стъпки, или нула.

За целта функцията изпълнява едно от трите действия. Ако даденото число е равно на целта, тогава текущата история е само начинът за постигането й, поради което се връща. Ако даденото число повече цел, няма смисъл да продължаваме да умножаваме и събираме, защото така само ще се увеличава. И ако все още не сме достигнали целта, функцията пробва и двата възможни пътя, започвайки от даденото число. Тя се призовава два пъти, по веднъж с всеки от начините. Ако първото повикване не върне null, то се връща. В противен случай се връща вторият.

За да разберем по-добре как функцията постига желания ефект, нека разгледаме нейните извиквания, които се случват в търсене на решение за числото 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)") find(8, "((1 * 3) + 5)") find(13, "(((1 * 3) + 5) + 5)") намерено!

Вдлъбнатината показва дълбочината на стека на повикванията. Първият път, когато функцията за намиране се извиква два пъти, за да провери решенията, започващи с (1 + 5) и (1 * 3). Първото извикване търси решение, започващо с (1 + 5) и използва рекурсия, за да провери всички решения, които дават число, по-малко или равно на желаното число. Не го намира и връща нула. След това операторът || и преминава към извикване на функция, която проверява опцията (1 * 3). Тук имаме късмет, защото при третото рекурсивно извикване получаваме 13. Това извикване връща низ и всеки от || предава този низ по-горе по пътя, връщайки решението като резултат.

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

Има два повече или по-малко естествени начина за въвеждане на функции в програма.

Първо, пишете подобен код няколко пъти. Това трябва да се избягва - повече код означава повече място за грешки и повече материал за четене за онези, които се опитват да разберат програмата. Така че ние вземаме повтаряща се функция, съпоставяме я хубаво имеи го поставете във функция.

Вторият начин е да откриете нужда от някаква нова функционалност, която си заслужава да бъде поставена в отделна функция. Започвате с името на функцията и след това пишете нейното тяло. Можете дори да започнете, като напишете кода, който използва функцията, преди самата функция да бъде дефинирана.

Колко трудно ви е да наименувате функция показва колко добре разбирате нейната функционалност. Да вземем пример. Трябва да напишем програма, която отпечатва две числа, броя на кравите и пилетата във фермата, последвани от думите "крави" и "пилета". Трябва да добавите нули към числата отпред, така че всяко да заема точно три позиции.

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

Очевидно имаме нужда от функция с два аргумента. Да започнем да кодираме.
// функция printFarmInventory printFarmInventory(крави, пилета) ( 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);

Ако добавим .length към низ, получаваме неговата дължина. Оказва се, че циклите while добавят водещи нули към числата, докато получат низ от 3 знака.

Готов! Но веднага щом се каним да изпратим кода на фермера (заедно със солиден чек, разбира се), той се обажда и ни казва, че има прасета във фермата и можем ли да добавим резултата от броя на прасетата към програмата?

Разбира се, че е възможно. Но когато започнем да копираме и поставяме кода от тези четири реда, разбираме, че трябва да спрем и да помислим. Трябва да има по-добър начин. Опитваме се да подобрим програмата:

// функция outputZeroPaddedWithLabel printZeroPaddedWithLabel(число, етикет) ( var numberString = String(number); 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);

Върши работа! Но името printZeroPaddedWithLabel е малко странно. Той комбинира три неща - изход, нулева подложка и етикет - в една функция. Вместо да натъпкваме целия повтарящ се фрагмент във функция, нека подчертаем една концепция:

// добавяне на Zeros функция zeroPad(number, width) ( var string = String(number); 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);

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

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

Добро правило е да добавяте само функционалността, от която определено се нуждаете. Понякога е изкушаващо да създадете рамки с общо предназначение за всяка малка нужда. Съпротивете му се. Никога няма да завършите работата, а просто ще напишете куп код, който никой няма да използва.

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

Функциите могат грубо да се разделят на такива, които се извикват поради техните странични ефекти и такива, които се извикват, за да получат някаква стойност. Разбира се, възможно е също тези свойства да се комбинират в една функция.

Първата помощна функция в примера на фермата, printZeroPaddedWithLabel, се извиква поради страничния ефект от отпечатването на низ. Вторият, zeroPad, поради върнатата стойност. И не е случайно, че втората функция е полезна по-често от първата. Функциите, които връщат стойности, се комбинират по-лесно помежду си от функциите, които създават странични ефекти.

Чистата функция е специален вид функция за връщане на стойност, която не само няма странични ефекти, но и не зависи от страничните ефекти на останалата част от кода - например не работи с глобални променливи, които могат да бъдат случайно променени някъде другаде. Една чиста функция, когато е извикана със същите аргументи, връща същия резултат (и не прави нищо друго) - което е доста хубаво. С нея се работи лесно. Извикването на такава функция може да бъде заменено мислено с резултата от нейната работа, без да се променя значението на кода. Когато искате да тествате такава функция, можете просто да я извикате и да сте сигурни, че ако работи в този контекст, ще работи и във всеки. Не толкова чистите функции могат да върнат различни резултати в зависимост от много фактори и имат странични ефекти, които са трудни за тестване и отчитане.

Въпреки това, човек не трябва да се срамува да пише функции, които не са съвсем чисти, или да започне свещено почистване на кода от такива функции. Страничните ефекти често са полезни. Няма как да се пише чиста версияфункция console.log и тази функция е доста полезна. Някои операции са по-лесни за изразяване чрез странични ефекти.

Резултат

Тази глава ви показа как да пишете свои собствени функции. Когато ключовата дума на функцията се използва като израз, връща указател към извикването на функцията. Когато се използва като израз, можете да декларирате променлива, като й присвоите извикване на функция.

Ключът към разбирането на функциите са локалните обхвати. Параметрите и променливите, декларирани във функцията, са локални за нея, пресъздават се при всяко извикване и не се виждат отвън. Функциите, декларирани в друга функция, имат достъп до нейния обхват.

Много е полезно различните задачи, изпълнявани от програмата, да се разделят на функции. Не е нужно да се повтаряте, функциите правят кода по-четлив, като го разделят на семантични части, по същия начин, по който главите и разделите на книга помагат за организирането на обикновен текст.

Упражнения

минимум
В предишната глава беше спомената функцията Math.min, която връща най-малкия от своите аргументи. Сега можем сами да напишем такава функция. Напишете функция min, която приема два аргумента и връща минималния от тях.

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

рекурсия
Видяхме, че операторът % (остатък) може да се използва за определяне дали дадено число е четно (% 2). Ето още един начин за определяне:

Нулата е четна.
Единицата е странна.
Всяко число N има същата четност като N-2.

Напишете рекурсивна функция isEven според тези правила. Трябва да приеме число и да върне булева стойност.

Тествайте го на 50 и 75. Опитайте да му дадете -1. Защо се държи по този начин? Възможно ли е да се поправи по някакъв начин?

Тествайте го на 50 и 75. Вижте как се държи на -1. Защо? Можеш лиизмислете начин да поправите това?

Console.log(isEven(50)); // → вярно console.log(isEven(75)); // → false console.log(isEven(-1)); // → ??

Броим боба.

Номерът на знака N на низ може да бъде получен чрез добавяне на .charAt(N)("string".charAt(5)) към него, по подобен начин на получаване на дължината на низ с .length. Върнатата стойност ще бъде низ от един знак (например "k"). Първият символ на низа има позиция 0, което означава, че последният знак ще има позиция string.length - 1. С други думи, низ от два знака има дължина 2 и позициите на символите му ще бъдат 0 и 1.

Напишете функция countBs, която приема низ като аргумент и връща броя на символите "B" в низа.

След това напишете функция countChar, която работи много като countBs, с изключение на това, че приема втори параметър, знакът, който ще търсим в низа (вместо просто да брои броя на символите "B"). За да направите това, пренапишете функцията countBs.

Инструкции за прескачане и обработка на изключения

Друга категория езикови оператори на JavaScript са операторите за прескачане. Както подсказва името, тези изрази карат интерпретатора на JavaScript да прескача на друго място в програмния код. Операторът break кара интерпретатора да премине към края на цикъл или друг оператор. Инструкцията за продължаване кара интерпретатора да пропусне останалата част от тялото на цикъла, да се върне обратно към началото на цикъла и да започне нова итерация. AT JavaScriptвъзможно е да се етикетират изрази с имена, така че операторите break и continue да могат изрично да бъдат посочени към кой цикъл или към кой друг израз принадлежат.

Операторът return кара интерпретатора да премине от извиканата функция обратно до точката, в която е била извикана, и да върне стойността на извикването. Операторът throw хвърля изключение и е проектиран да работи заедно с операторите try/catch/finally, които дефинират блока. програмен кодза обработка на изключението. Това е доста сложен вид изрази за прескачане: когато възникне изключение, интерпретаторът прескача до най-близкия обхващащ манипулатор на изключения, който може да бъде в същата функция или по-висок, в стека за връщане на извиканата функция.

Всеки от тези оператори за прескачане е описан по-подробно в следващите подраздели.

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

Всеки израз може да бъде маркиран с идентификатор и двоеточие преди него:

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

Когато маркирате инструкция, вие й давате име, което след това може да се използва като препратка навсякъде в програмата. Можете да маркирате всяка инструкция, но има смисъл да маркирате само инструкции, които имат тяло, като цикли и условни оператори.

Като дадете име на цикъла, той може да се използва в оператори за прекъсване и продължаване, вътре в цикъла, за да излезете от него или да отидете в началото на цикъла, към следващата итерация. Операторите break и continue са единствените изрази в езика JavaScript, които могат да съдържат етикети – те се обсъждат по-подробно по-късно. Следното е пример за оператор while с етикет и оператор за продължаване, използващ този етикет:

Mainloop: while (token != null) ( // Кодът е пропуснат... продължаване на mainloop; // Преминете към следващата итерация на посочения цикъл)

Идентификаторът, използван като етикет на израза, може да бъде всеки валиден идентификатор на JavaScript, с изключение на запазена дума. Имената на етикетите са отделни от имената на променливи и функции, така че можете да използвате идентификатори, които съответстват на имена на променливи или функции като етикети.

Етикетите на инструкциите се дефинират само в рамките на инструкциите, за които се отнасят (и, разбира се, в рамките на инструкциите, вложени в тях). Вложените инструкции не могат да бъдат маркирани със същите идентификатори като съдържащите ги инструкции, но две независими инструкции могат да бъдат маркирани със същия етикет. Маркираните инструкции могат да бъдат повторно маркирани. Това означава, че всяка инструкция може да има множество етикети.

изявление за прекъсване

Операторът break предизвиква незабавно излизане от най-вътрешния цикъл или оператор switch. Вече видяхме примери за използване на оператор break в оператор switch по-рано. В циклите обикновено се използва за незабавно излизане от цикъла, когато по някаква причина е необходимо да се прекрати изпълнението на цикъла.

Когато цикълът е много сложно състояниезавършване, често е по-лесно да се приложат тези условия с команда за прекъсване, отколкото да се опитате да ги изразите в единичен условен цикъл. Следващият пример се опитва да намери елемент от масив с конкретна стойност. Цикълът завършва по обичайния начин, когато се достигне краят на масива, или с командата break, веднага щом се намери желаната стойност:

Var arr = ["a","b","c","d","e"], резултат; за (променлива i = 0; i

В JavaScript е позволено да се посочи името на етикета след ключова думапрекъсване (идентификатор без двоеточие):

прекъсване име_на_етикет;

Когато операторът break се използва с етикет, той скача до края на посочения оператор или прекратява изпълнението му. При липса на инструкция с посочения етикет, опитът да се използва тази форма на оператора break генерира синтактична грешка. Наименуваният оператор не трябва да бъде цикъл или оператор switch. Означеният оператор за прекъсване може да "избяга" от всеки съдържащ оператор. Приложената инструкция може дори да бъде прост блокинструкции, оградени във фигурни скоби с единствената цел да го маркират.

Между ключовата дума break и името на етикета не може да се вмъкне знак за нов ред. Това е така, защото интерпретаторът на JavaScript автоматично вмъква липсващи точки и запетая: ако прекъснете ред от код между ключовата дума break и етикета, който я следва, интерпретаторът ще приеме, че сте имали предвид простата форма на този оператор без етикет, и ще добави точка и запетая .

Означеният оператор break се изисква само когато искате да прекъснете изпълнението на оператор, който не е най-близкият обхващащ цикъл или оператор switch.

продължи изявление

Операторът continue е подобен на оператора break. Въпреки това, вместо да излезе от цикъла, операторът continue започва нова итерация на цикъла. Синтаксисът за оператора continue е толкова прост, колкото синтаксиса за оператора break. Операторът за продължаване може да се използва и с етикет.

Операторът continue, независимо дали е немаркиран или етикетиран, може да се използва само в тялото на цикъл. Използването му навсякъде другаде води до синтактична грешка. Когато се изпълни оператор за продължение, текущата итерация на цикъла се прекъсва и започва следващата. За различни видовецикли означава различни неща:

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

    Цикълът do/while скача до края на цикъла, където условието се проверява отново, преди цикълът да се повтори.

    В цикъла for изразът за нарастване се оценява и тестовият израз се оценява отново, за да се определи дали трябва да се извърши следващата итерация.

    В цикъла for/in цикълът започва наново с присвояването на указаното име променливаследващ имот.

Обърнете внимание на разликата в поведението на оператора continue в циклите while и for. Цикълът while се връща директно към своето състояние и за цикълпърво оценява израза за нарастване и след това се връща към условието. Следващият пример показва използването на неозначен оператор за продължаване за излизане от текущата итерация на цикъл за четни числа:

променлива сума = 0; // Изчисляване на сумата на нечетните числа от 0 - 10 за (var i = 0; i

Операторът за продължаване, подобно на break, може да се използва във вложени цикли във форма, която включва етикет, в който случай рестартираният цикъл не е непременно този, който непосредствено съдържа израза за продължаване. Освен това, както при прекъсването, нови редове между ключовата дума continue и името на етикета не са разрешени.

изявление за връщане

Извикването на функция е израз и като всички изрази има стойност. Инструкцията return във функциите се използва за определяне на стойността, върната от функцията. Операторът return може да бъде поставен само в тялото на функция. Присъствието му навсякъде другаде е синтактична грешка. Когато се изпълни оператор за връщане, функцията връща стойността на израза на извикващата програма. Например:

Ако дадена функция няма израз за връщане, когато бъде извикана, интерпретаторът ще изпълни инструкциите в тялото на функцията една по една, докато стигне до края на функцията и след това ще върне контрола на програмата, която я е извикала. В този случай изразът за повикване ще върне недефиниран. Операторът return често е последният оператор във функция, но това е напълно незадължително: функцията ще върне контрола на извикващата програма веднага щом се достигне операторът return, дори ако е последван от други оператори в тялото на функцията.

Операторът return може да се използва и без израз, в който случай той просто прекъсва функцията и се връща недефиниран на извикващия. Например:

Функция myFun(arr) ( // Ако масивът съдържа отрицателни числа, прекъснете функцията за (var i = 0; i

изявление за хвърляне

Изключениее сигнал, показващ появата на някакъв вид изключение или грешка. Повдигане на изключение (хвърляне)е начин за сигнализиране на такава грешка или изключение. Да уловиш изключение (catch) означава да го обработиш, т.е. да предприеме необходимите или подходящи действия за възстановяване от изключението.

В JavaScript изключения се хвърлят, когато възникне грешка по време на изпълнение и когато програмата изрично я повдигне с оператора throw. Изключенията се улавят с помощта на оператори try/catch/finally, които са описани по-късно.

Операторът throw има следния синтаксис:

хвърлям израз;

Резултатът от израз може да бъде стойност от произволен тип. Операторът throw може да бъде подаден като число, представляващо кода на грешката, или низ, съдържащ текста на съобщението за грешка. Интерпретаторът на JavaScript хвърля изключения, използвайки екземпляр на клас грешкаедин от неговите подкласове и можете също да използвате подобен подход. Обектът Error има свойство име, който дефинира типа на грешката и свойството съобщение A, съдържащ низа, предаден на функцията конструктор. Следното е пример за функция, която поражда обект Error, когато е извикана с невалиден аргумент:

// Факториалната функция на числова функция factorial(number) ( // Ако входният аргумент не е валидна стойност, // възниква изключение! if (число 1; i *= число, число--); /* празно тяло на цикъл */ return i; ) конзола. log("5! = ", факториал(5)); конзола. log("-3! = ", факториел(-3));

Когато бъде хвърлено изключение, интерпретаторът на JavaScript незабавно прекъсва нормалното изпълнение на програмата и преминава към най-близкия манипулатор на изключения. Обработчиците на изключения използват командата catch на конструкцията try/catch/finally, която е описана в следващия раздел.

Ако блокът от код, в който е хвърлено изключението, няма съответна catch конструкция, интерпретаторът анализира следното външно тялопрограмен код и проверява дали с него е свързан манипулатор на изключения. Това продължава, докато манипулаторът бъде намерен.

Ако изключение бъде хвърлено във функция, която не съдържа конструкция try/catch/finally, която да го обработи, тогава изключението се разпространява нагоре в кода, който е извикал функцията. По този начин изключенията се разпространяват по лексикалната структура JavaScript методинагоре в стека на повикванията. Ако манипулатор на изключения никога не бъде намерен, изключението се третира като грешка и се съобщава на потребителя.

опитвам/хващам/накрая конструирам

Конструкцията try/catch/finally прилага механизма за обработка на изключения на JavaScript. изявление за опитв тази конструкция просто дефинира блок от код, в който се обработват изключения. Блокът try е последван от изявление за уловс блок от изрази, който да бъде извикан, ако възникне изключение някъде в блока try. Изявлението catch е последвано от блок накрая A, който съдържа код, който изпълнява финалните операции и е гарантирано да се изпълнява независимо от това какво се случва в блока try.

И блокът catch, и блокът finally не са задължителни, но поне един от тях трябва да присъства след блока try. опитвам, хващам и накрая блокира началото и края фигурни скоби. Това е задължителна част от синтаксиса и не може да бъде пропусната дори ако между тях има само един израз.

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

Опитайте ( // Обикновено този код ще работи гладко от началото до края. // Но в даден момент може // да бъде хвърлено изключение в него, или директно с оператора throw, или индиректно, // чрез извикване на метода, който хвърля изключението. ) catch (ex) ( // Операторите в този блок се изпълняват, ако и само ако възникне изключение в блока try //. Тези оператори могат да използват локална променлива ex, която // препраща към обекта Error или към друга стойност, посочена в оператора throw. // Този блок може или да обработи изключението по някакъв начин, или // да го игнорира и да направи нещо друго, или // да хвърли отново изключението с оператор throw. ) finally ( // Този блок съдържа оператори, които винаги се изпълняват, независимо дали , // какво се е случило в блока try Те се изпълняват, ако блокът try е приключил: // 1) както обикновено, достигайки края на блока // 2) поради прекъсване, оператори за продължаване или връщане // 3) с изключение, управлявано от в блока catch по-горе // ​​4) с неуловено изключение, което продължава // да се разпространява към по-високи нива)

Имайте предвид, че ключовата дума catch е последвана от идентификатор в скоби. Този идентификатор е подобен на функционален параметър. Когато бъде уловено изключение, този параметър ще бъде зададен на изключение (например обект Error). За разлика от нормалната променлива, идентификаторът, свързан с оператор catch, съществува само в тялото на блока catch.

Следното е по-реалистичен пример за конструкция try/catch. Той извиква метода factorial(), дефиниран в предишния пример, и бързи методи() и alert() клиентски JavaScript за организиране на вход и изход:

Опитайте ( // Попитайте потребителя за число var n = Number(prompt("Въведете положително число", "")); // Изчислете факториела на число, като приемете, че // въведеното е валидно var f = factorial( n); // Отпечатване на резултата alert(n + "! = " + f); ) catch (ex) ( // Ако данните са неправилни, управлението ще бъде прехвърлено тук alert(ex); // Уведомете потребителя за грешката )

Ако потребителят въведе отрицателно число, ще се покаже предупредително съобщение:

Това е пример за конструкция try/catch без израз finally. Въпреки че finally не се използва толкова често, колкото catch, въпреки това понякога е полезно. Блокът finally се гарантира, че ще се изпълни, ако поне част от блока try е изпълнена, независимо как е завършил кодът в блока try. Тази функция обикновено се използва за извършване на финални операции, след като кодът е бил изпълнен в продължение на опит.

В нормална ситуация контролът достига до края на блока try и след това преминава към блока finally, който изпълнява необходимите финални операции. Ако контролът излезе от блок try в резултат на оператор return, continue или break, блокът finally се изпълнява, преди контролът да бъде прехвърлен другаде.

Ако възникне изключение в блок try и има подходящ блок catch, който да го обработи, контролът първо се прехвърля към блока catch и след това към блока finally. Ако няма локален catch блок, тогава контролът първо преминава към finally блока и след това прескача към най-близкия външен catch блок, който може да се справи с изключението.

Ако самият блок finally прехвърли управление с оператор return, continue, break или throw или чрез извикване на метод, който хвърля изключение, чакащото прехвърляне се отменя и се изпълнява ново. Например, ако блокът finally хвърли изключение, това изключение ще замени всяко хвърлено преди това изключение.

Оператор връщанепрекратява текущата функция и връща нейната стойност.

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

Синтаксис

връщане [[израз]]; израз Изразът, чиято стойност ще бъде върната. Ако не е посочено, вместо това се връща undefined.

Описание

Когато във функция се извика израз за връщане, изпълнението му спира. Посочената стойност се връща на мястото, където е извикана функцията. Например следната функция връща стойността на квадрат на своя аргумент x (където x е число):

функция square(x) ( return x * x; ) var demo = square(3); // демонстрационната стойност ще бъде 9

Ако не е зададена върната стойност, вместо това се връща undefined.

Следните изрази винаги прекратяват изпълнението на функция:

връщане; връща вярно; връща невярно; връщане x; връщане x + y / 3;

Автоматични точки и запетая

функция magic(x) (връща функция calc(x) (връща x * 42); ) var answer = magic(); отговор(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

КомпютриПодвиженсървър
Chromeръб, крайFirefoxInternet ExplorerОперасафариandroid webviewChrome за AndroidFirefox за AndroidOpera за AndroidSafari на iOSИнтернет на SamsungNode.js
връщанеChrome Пълна подкрепа 1 ръб, край Пълна подкрепа 12 Firefox Пълна подкрепа 1 IE Пълна подкрепа 3 Опера Пълна подкрепадасафари Пълна подкрепадаwebview android Пълна подкрепа 1 Chrome Android Пълна подкрепа 18 Firefox Android Пълна подкрепа 4 OperaAndroid Пълна подкрепадаSafari iOS Пълна подкрепадаSamsung Internet Android Пълна подкрепа 1.0 nodejs Пълна подкрепада