Пример скрипта функции двух параметров в js. Является ли функция в JavaScript объектом? Для чего нужны функции в программировании

Люди считают, что компьютерные науки – это искусство для гениев. В реальности всё наоборот – просто множество людей делают вещи, которые стоят друг на друге, будто составляя стену из маленьких камушков.

Дональд Кнут

Вы уже видели вызовы функций, таких как alert . Функции – это хлеб с маслом программирования на JavaScript. Идея оборачивания куска программы и вызова её как переменной очень востребована. Это инструмент для структурирования больших программ, уменьшения повторений, назначения имён подпрограммам, и изолирование подпрограмм друг от друга.

Самое очевидное использование функций – создание нового словаря. Придумывать слова для обычной человеческой прозы – дурной тон. В языке программирования это необходимо.

Средний взрослый русскоговорящий человек знает примерно 10000 слов. Редкий язык программирования содержит 10000 встроенных команд. И словарь языка программирования определён чётче, поэтому он менее гибок, чем человеческий. Поэтому нам обычно приходится добавлять в него свои слова, чтобы избежать излишних повторений.

Определение функции Определение функции – обычное определение переменной, где значение, которое получает переменная, является функцией. Например, следующий код определяет переменную square, которая ссылается на функцию, подсчитывающую квадрат заданного числа:

Var square = function(x) { return x * x; }; console.log(square(12)); // → 144

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

У функции может быть несколько параметров, или вообще их не быть. В следующем примере makeNoise не имеет списка параметров, а у power их целых два:

Var makeNoise = function() { console.log("Хрясь!"); }; makeNoise(); // → Хрясь! var power = function(base, exponent) { var result = 1; for (var count = 0; count < exponent; count++) result *= base; return result; }; console.log(power(2, 10)); // → 1024

Некоторые функции возвращают значение, как power и square, другие не возвращают, как makeNoise, которая производит только побочный эффект. Инструкция return определяет значение, возвращаемое функцией. Когда обработка программы доходит до этой инструкции, она сразу же выходит из функции, и возвращает это значение в то место кода, откуда была вызвана функция. return без выражения возвращает значение undefined .

Параметры и область видимости Параметры функции – такие же переменные, но их начальные значения задаются при вызове функции, а не в её коде.

Важное свойство функций в том, что переменные, созданные внутри функции (включая параметры), локальны внутри этой функции. Это означает, что в примере с power переменная result будет создаваться каждый раз при вызове функции, и эти отдельные её инкарнации никак друг с другом не связаны.

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

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

Var x = "outside"; var f1 = function() { var x = "inside f1"; }; f1(); console.log(x); // → outside var f2 = function() { x = "inside f2"; }; f2(); console.log(x); // → inside 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()); // → ___/""""\______/"\_

Функции flat и mountain видят переменную result, потому что они находятся внутри функции, в которой она определена. Но они не могут видеть переменные count друг друга, потому что переменные одной функции находятся вне области видимости другой. А окружение снаружи функции landscape не видит ни одной из переменных, определённых внутри этой функции.

Короче говоря, в каждой локальной области видимости можно увидеть все области, которые её содержат. Набор переменных, доступных внутри функции, определяется местом, где эта функция описана в программе. Все переменные из блоков, окружающих определение функции, видны – включая и те, что определены на верхнем уровне в основной программе. Этот подход к областям видимости называется лексическим.

Люди, изучавшие другие языки программирования, могут подумать, что любой блок, заключённый в фигурные скобки, создаёт своё локальное окружение. Но в JavaScript область видимости создают только функции. Вы можете использовать отдельно стоящие блоки:

Var something = 1; { var something = 2; // Делаем что-либо с переменной something... } // Вышли из блока...

Но something внутри блока – это та же переменная, что и снаружи. Хотя такие блоки и разрешены, имеет смысл использовать их только для команды if и циклов.

Если это кажется вам странным – так кажется не только вам. В версии JavaScript 1.7 появилось ключевое слово let, которое работает как var, но создаёт переменные, локальные для любого данного блока, а не только для функции.

Функции как значения Имена функций обычно используют как имя для кусочка программы. Такая переменная однажды задаётся и не меняется. Так что легко перепутать функцию и её имя.

Но это – две разные вещи. Вызов функции можно использовать, как простую переменную – например, использовать их в любых выражениях. Возможно хранить вызов функции в новой переменной, передавать её как параметр другой функции, и так далее. Также переменная, хранящая вызов функции, остаётся обычной переменной и её значение можно поменять:

Var launchMissiles = function(value) { missileSystem.launch("пли!"); }; if (safeMode) launchMissiles = function(value) {/* отбой */};

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

Объявление функций Есть более короткая версия выражения “var square = function…”. Ключевое слово function можно использовать в начале инструкции:

Function square(x) { return x * x; }

Это объявление функции. Инструкция определяет переменную square и присваивает ей заданную функцию. Пока всё ок. Есть только один подводный камень в таком определении.

Console.log("The future says:", future()); function future() { return "We STILL have no flying cars."; }

Такой код работает, хотя функция объявляется ниже того кода, который её использует. Это происходит оттого, что объявления функций не являются частью обычного исполнения программ сверху вниз. Они «перемещаются» наверх их области видимости и могут быть вызваны в любом коде в этой области. Иногда это удобно, потому что вы можете писать код в таком порядке, который выглядит наиболее осмысленно, не беспокоясь по поводу необходимости определять все функции выше того места, где они используются.

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

Function example() { function a() {} // Нормуль if (something) { function b() {} // Ай-яй-яй! } }

Стек вызовов Полезным будет присмотреться к тому, как порядок выполнения работает с функциями. Вот простая программа с несколькими вызовами функций:

Function greet(who) { console.log("Привет, " + who); } greet("Семён"); console.log("Покеда");

Обрабатывается она примерно так: вызов greet заставляет проход прыгнуть на начало функции. Он вызывает встроенную функцию console.log, которая перехватывает контроль, делает своё дело и возвращает контроль. Потом он доходит до конца greet, и возвращается к месту, откуда его вызвали. Следующая строчка опять вызывает console.log.

Схематично это можно показать так:

Top greet console.log greet top console.log top

Поскольку функция должна вернуться на то место, откуда её вызвали, компьютер должен запомнить контекст, из которого была вызвана функция. В одном случае, console.log должна вернуться обратно в greet. В другом, она возвращается в конец программы.

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

Хранение стека требует места в памяти. Когда стек слишком сильно разрастается, компьютер прекращает выполнение и выдаёт что-то вроде “stack overflow” или “ too much recursion”. Следующий код это демонстрирует – он задаёт компьютеру очень сложный вопрос, который приводит к бесконечным прыжкам между двумя функциями. Точнее, это были бы бесконечные прыжки, если бы у компьютера был бесконечный стек. В реальности стек переполняется.

Function chicken() { return egg(); } function egg() { return chicken(); } console.log(chicken() + " came first."); // → ??

Необязательные аргументы Следующий код вполне разрешён и выполняется без проблем:

Alert("Здрасьте", "Добрый вечер", "Всем привет!");

Официально функция принимает один аргумент. Однако, при таком вызове она не жалуется. Она игнорирует остальные аргументы и показывает «Здрасьте».

JavaScript очень лоялен по поводу количества аргументов, передаваемых функции. Если вы передадите слишком много, лишние будут проигнорированы. Слишком мало – отсутствующим будет назначено значение undefined.

Минус этого подхода в том, что возможно,- и даже вероятно,- передать функции неправильное количество аргументов, и вам никто на это не пожалуется.

Плюс в том, что вы можете создавать функции, принимающие необязательные аргументы. К примеру, в следующей версии функции power её можно вызывать как с двумя, так и с одним аргументом,- в последнем случае экспонента будет равна двум, и функция работает как квадрат.

Function power(base, exponent) { 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, которая создаёт локальную переменную. Затем она возвращает функцию, которая читает эту локальную переменную и возвращает её значение.

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

Это допустимо и работает так, как должно – доступ к переменной остаётся. Более того, в одно и то же время могут существовать несколько экземпляров одной и той же переменной, что ещё раз подтверждает тот факт, что с каждым вызовом функции локальные переменные пересоздаются.

Эта возможность работать со ссылкой на какой-то экземпляр локальной переменной называется замыканием. Функция, замыкающая локальные переменные, называется замыкающей. Она не только освобождает вас от забот, связанных с временем жизни переменных, но и позволяет творчески использовать функции.

С небольшим изменением мы превращаем наш пример в функцию, умножающую числа на любое заданное число.

Function multiplier(factor) { return function(number) { return number * factor; }; } var twice = multiplier(2); console.log(twice(5)); // → 10

Отдельная переменная вроде localVariable из примера с wrapValue уже не нужна. Так как параметр – сам по себе локальная переменная.

Потребуется практика, чтобы начать мыслить подобным образом. Хороший вариант мысленной модели – представлять, что функция замораживает код в своём теле и обёртывает его в упаковку. Когда вы видите return function(...) {...}, представляйте, что это пульт управления куском кода, замороженным для употребления позже.

В нашем примере multiplier возвращает замороженный кусок кода, который мы сохраняем в переменной twice. Последняя строка вызывает функцию, заключённую в переменной, в связи с чем активируется сохранённый код (return number * factor;). У него всё ещё есть доступ к переменной factor, которая определялась при вызове multiplier, к тому же у него есть доступ к аргументу, переданному во время разморозки (5) в качестве числового параметра.

Рекурсия Функция вполне может вызывать сама себя, если она заботится о том, чтобы не переполнить стек. Такая функция называется рекурсивной. Вот пример альтернативной реализации возведения в степень:

Function power(base, exponent) { if (exponent == 0) return 1; else return base * power(base, exponent - 1); } console.log(power(2, 3)); // → 8

Примерно так математики определяют возведение в степень, и, возможно, это описывает концепцию более элегантно, чем цикл. Функция вызывает себя много раз с разными аргументами для достижения многократного умножения.

Однако, у такой реализации есть проблема – в обычной среде JavaScript она раз в 10 медленнее, чем версия с циклом. Проход по циклу выходит дешевле, чем вызов функции.

Дилемма «скорость против элегантности» довольно интересна. Есть некий промежуток между удобством для человека и удобством для машины. Любую программу можно ускорить, сделав её больше и замысловатее. От программиста требуется находить подходящий баланс.

В случае с первым возведением в степень, неэлегантный цикл довольно прост и понятен. Не имеет смысла заменять его рекурсией. Часто, однако, программы работают с такими сложными концепциями, что хочется уменьшить эффективность путём повышения читаемости.

Основное правило, которое уже не раз повторяли, и с которым я полностью согласен – не беспокойтесь насчёт быстродействия, пока вы точно не уверены, что программа тормозит. Если так, найдите те части, которые работают дольше всех, и меняйте там элегантность на эффективность.

Конечно, мы не должны сразу же полностью игнорировать быстродействие. Во многих случаях, как с возведением в степень, особой простоты от элегантных решений мы не получаем. Иногда опытный программист сразу видит, что простой подход никогда не будет достаточно быстрым.

Я заостряю на этом внимание оттого, что слишком много начинающих программистов хватаются за эффективность даже в мелочах. Результат получается больше, сложнее и часто не без ошибок. Такие программы дольше писать, а работают они часто не сильно быстрее.

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

Вот вам загадка: можно получить бесконечное количество чисел, начиная с числа 1, и потом либо добавляя 5, либо умножая на 3. Как нам написать функцию, которая, получив число, пытается найти последовательность таких сложений и умножений, которые приводят к заданному числу? К примеру, число 13 можно получить, сначала умножив 1 на 3, а затем добавив 5 два раза. А число 15 вообще нельзя так получить.

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

Function findSolution(target) { function find(start, history) { if (start == target) return history; 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)

Этот пример не обязательно находит самое короткое решение – он удовлетворяется любым. Не ожидаю, что вы сразу поймёте, как программа работает. Но давайте разбираться в этом отличном упражнении на рекурсивное мышление.

Внутренняя функция find занимается рекурсией. Она принимает два аргумента – текущее число и строку, которая содержит запись того, как мы пришли к этому номеру. И возвращает либо строчку, показывающую нашу последовательность шагов, либо null.

Для этого функция выполняет одно из трёх действий. Если заданное число равно цели, то текущая история как раз и является способом её достижения, поэтому она и возвращается. Если заданное число больше цели, продолжать умножения и сложения смысла нет, потому что так оно будет только увеличиваться. А если мы ещё не достигли цели, функция пробует оба возможных пути, начинающихся с заданного числа. Она дважды вызывает себя, один раз с каждым из способов. Если первый вызов возвращает не null, он возвращается. В другом случае возвращается второй.

Чтобы лучше понять, как функция достигает нужного эффекта, давайте просмотрим её вызовы, которые происходят в поисках решения для числа 13.

Find(1, "1") find(6, "(1 + 5)") find(11, "((1 + 5) + 5)") find(16, "(((1 + 5) + 5) + 5)") too big find(33, "(((1 + 5) + 5) * 3)") too big find(18, "((1 + 5) * 3)") too big find(3, "(1 * 3)") find(8, "((1 * 3) + 5)") find(13, "(((1 * 3) + 5) + 5)") found!

Отступ показывает глубину стека вызовов. В первый раз функция find вызывает сама себя дважды, чтобы проверить решения, начинающиеся с (1 + 5) и (1 * 3). Первый вызов ищет решение, начинающееся с (1 + 5), и при помощи рекурсии проверяет все решения, выдающие число, меньшее или равное требуемому. Не находит, и возвращает null. Тогда-то оператор || и переходит к вызову функции, который исследует вариант (1 * 3). Здесь нас ждёт удача, потому что в третьем рекурсивном вызове мы получаем 13. Этот вызов возвращает строку, и каждый из операторов || по пути передаёт эту строку выше, в результате возвращая решение.

Выращиваем функции Существует два более-менее естественных способа ввода функций в программу.

Первый – вы пишете схожий код несколько раз. Этого нужно избегать – больше кода означает больше места для ошибок и больше материала для чтения тех, кто пытается понять программу. Так что мы берём повторяющуюся функциональность, подбираем ей хорошее имя и помещаем её в функцию.

Второй способ – вы обнаруживаете потребность в некоей новой функциональности, которая достойна помещения в отдельную функцию. Вы начинаете с названия функции, и затем пишете её тело. Можно даже начинать с написания кода, использующего функцию, до того, как сама функция будет определена.

То, насколько сложно вам подобрать имя для функции, показывает, как хорошо вы представляете себе её функциональность. Возьмём пример. Нам нужно написать программу, выводящую два числа, количество коров и куриц на ферме, за которыми идут слова «коров» и «куриц». К числам нужно спереди добавить нули так, чтобы каждое занимало ровно три позиции.

007 Коров 011 Куриц

Очевидно, что нам понадобится функция с двумя аргументами. Начинаем кодить.
// вывестиИнвентаризациюФермы function printFarmInventory(cows, chickens) { 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 символа.

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

Можно, конечно. Но когда мы начинаем копировать и вставлять код из этих четырёх строчек, мы понимаем, что надо остановиться и подумать. Должен быть способ лучше. Пытаемся улучшить программу:

// выводСДобавлениемНулейИМеткой function printZeroPaddedWithLabel(number, label) { 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 немного странное. Оно объединяет три вещи – вывод, добавление нулей и метку – в одну функцию. Вместо того, чтобы вставлять в функцию весь повторяющийся фрагмент, давайте выделим одну концепцию:

// добавитьНулей function 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, и эта функция весьма полезна. Некоторые операции легче выразить, используя побочные эффекты.

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

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

Очень полезно разделять разные задачи, выполняемые программой, на функции. Вам не придётся повторяться, функции делают код более читаемым, разделяя его на смысловые части, так же, как главы и секции книги помогают в организации обычного текста.

УпражненияМинимум В предыдущей главе была упомянута функция Math.min, возвращающая самый маленький из аргументов. Теперь мы можем написать такую функцию сами. Напишите функцию min, принимающую два аргумента, и возвращающую минимальный из них.

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

Рекурсия Мы видели, что оператор % (остаток от деления) может использоваться для определения того, чётное ли число (% 2). А вот ещё один способ определения:

Ноль чётный.
Единица нечётная.
У любого числа N чётность такая же, как у N-2.

Напишите рекурсивную функцию isEven согласно этим правилам. Она должна принимать число и возвращать булевское значение.

Потестируйте её на 50 и 75. Попробуйте задать ей -1. Почему она ведёт себя таким образом? Можно ли её как-то исправить?

Test it on 50 and 75. See how it behaves on -1. Why? Can you think of a way to fix this?

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

Считаем бобы.

Символ номер N строки можно получить, добавив к ней.charAt(N) (“строчка”.charAt(5)) – схожим образом с получением длины строки при помощи.length. Возвращаемое значение будет строковым, состоящим из одного символа (к примеру, “к”). У первого символа строки позиция 0, что означает, что у последнего символа позиция будет string.length – 1. Другими словами, у строки из двух символов длина 2, а позиции её символов будут 0 и 1.

Напишите функцию countBs, которая принимает строку в качестве аргумента, и возвращает количество символов “B”, содержащихся в строке.

Затем напишите функцию countChar, которая работает примерно как countBs, только принимает второй параметр - символ, который мы будем искать в строке (вместо того, чтобы просто считать количество символов “B”). Для этого переделайте функцию countBs.

Another essential concept in coding is functions , which allow you to store a piece of code that does a single task inside a defined block, and then call that code whenever you need it using a single short command - rather than having to type out the same code multiple times. In this article we"ll explore fundamental concepts behind functions such as basic syntax, how to invoke and define them, scope, and parameters.

Prerequisites: Objective:
Basic computer literacy, a basic understanding of HTML and CSS, JavaScript first steps .
To understand the fundamental concepts behind JavaScript functions.
Where do I find functions?

In JavaScript, you"ll find functions everywhere. In fact, we"ve been using functions all the way through the course so far; we"ve just not been talking about them very much. Now is the time, however, for us to start talking about functions explicitly, and really exploring their syntax.

Pretty much anytime you make use of a JavaScript structure that features a pair of parentheses - () - and you"re not using a common built-in language structure like a for loop , while or do...while loop , or if...else statement , you are making use of a function.

Built-in browser functions

We"ve made use of functions built in to the browser a lot in this course. Every time we manipulated a text string, for example:

Var myText = "I am a string"; var newString = myText.replace("string", "sausage"); console.log(newString); // the replace() string function takes a string, // replaces one substring with another, and returns // a new string with the replacement made

Or every time we manipulated an array:

Var myArray = ["I", "love", "chocolate", "frogs"]; var madeAString = myArray.join(" "); console.log(madeAString); // the join() function takes an array, joins // all the array items together into a single // string, and returns this new string

Or every time we generated a random number:

Var myNumber = Math.random(); // the random() function generates a random // number between 0 and 1, and returns that // number

We were using a function!

Note : Feel free to enter these lines into your browser"s JavaScript console to re-familiarize yourself with their functionality, if needed.

The JavaScript language has many built-in functions to allow you to do useful things without having to write all that code yourself. In fact, some of the code you are calling when you invoke (a fancy word for run, or execute) a built in browser function couldn"t be written in JavaScript - many of these functions are calling parts of the background browser code, which is written largely in low-level system languages like C++, not web languages like JavaScript.

Bear in mind that some built-in browser functions are not part of the core JavaScript language - some are defined as part of browser APIs, which build on top of the default language to provide even more functionality (refer to this early section of our course for more descriptions). We"ll look at using browser APIs in more detail in a later module.

Functions versus methods

One thing we need to clear up before we move on - technically speaking, built in browser functions are not functions - they are methods . This sounds a bit scary and confusing, but don"t worry - the words function and method are largely interchangeable, at least for our purposes, at this stage in your learning.

The distinction is that methods are functions defined inside objects. Built-in browser functions (methods) and variables (which are called properties ) are stored inside structured objects, to make the code more efficient and easier to handle.

You don"t need to learn about the inner workings of structured JavaScript objects yet - you can wait until our later module that will teach you all about the inner workings of objects, and how to create your own. For now, we just wanted to clear up any possible confusion of method versus function - you are likely to meet both terms as you look at the available related resources across the Web.

Custom functions

You"ve also seen a lot of custom functions in the course so far - functions defined in your code, not inside the browser. Anytime you saw a custom name with parentheses straight after it, you were using a custom function. In our random-canvas-circles.html example (see also the full ) from our loops article , we included a custom draw() function that looked like this:

Function draw() { ctx.clearRect(0,0,WIDTH,HEIGHT); for (var i = 0; i < 100; i++) { ctx.beginPath(); ctx.fillStyle = "rgba(255,0,0,0.5)"; ctx.arc(random(WIDTH), random(HEIGHT), random(50), 0, 2 * Math.PI); ctx.fill(); } }

This function draws 100 random circles inside an element. Every time we want to do that, we can just invoke the function with this

rather than having to write all that code out again every time we want to repeat it. And functions can contain whatever code you like - you can even call other functions from inside functions. The above function for example calls the random() function three times, which is defined by the following code:

Function random(number) { return Math.floor(Math.random()*number); }

We needed this function because the browser"s built-in Math.random() function only generates a random decimal number between 0 and 1. We wanted a random whole number between 0 and a specified number.

Invoking functions

You are probably clear on this by now, but just in case ... to actually use a function after it has been defined, you"ve got to run - or invoke - it. This is done by including the name of the function in the code somewhere, followed by parentheses.

Function myFunction() { alert("hello"); } myFunction() // calls the function once

Anonymous functions

You may see functions defined and invoked in slightly different ways. So far we have just created a function like so:

Function myFunction() { alert("hello"); }

But you can also create a function that doesn"t have a name:

Function() { alert("hello"); }

This is called an anonymous function - it has no name! It also won"t do anything on its own. You generally use an anonymous function along with an event handler, for example the following would run the code inside the function whenever the associated button is clicked:

Var myButton = document.querySelector("button"); myButton.onclick = function() { alert("hello"); }

The above example would require there to be a element available on the page to select and click. You"ve already seen this structure a few times throughout the course, and you"ll learn more about and see it in use in the next article.

You can also assign an anonymous function to be the value of a variable, for example:

Var myGreeting = function() { alert("hello"); }

This function could now be invoked using:

MyGreeting();

This effectively gives the function a name; you can also assign the function to be the value of multiple variables, for example:

Var anotherGreeting = function() { alert("hello"); }

This function could now be invoked using either of

MyGreeting(); anotherGreeting();

But this would just be confusing, so don"t do it! When creating functions, it is better to just stick to this form:

Function myGreeting() { alert("hello"); }

You will mainly use anonymous functions to just run a load of code in response to an event firing - like a button being clicked - using an event handler. Again, this looks something like this:

MyButton.onclick = function() { alert("hello"); // I can put as much code // inside here as I want }

Function parameters

Some functions require parameters to be specified when you are invoking them - these are values that need to be included inside the function parentheses, which it needs to do its job properly.

Note : Parameters are sometimes called arguments, properties, or even attributes.

As an example, the browser"s built-in Math.random() function doesn"t require any parameters. When called, it always returns a random number between 0 and 1:

Var myNumber = Math.random();

The browser"s built-in string replace() function however needs two parameters - the substring to find in the main string, and the substring to replace that string with:

Var myText = "I am a string"; var newString = myText.replace("string", "sausage");

Note : When you need to specify multiple parameters, they are separated by commas.

It should also be noted that sometimes parameters are optional - you don"t have to specify them. If you don"t, the function will generally adopt some kind of default behavior. As an example, the array join() function"s parameter is optional:

Var myArray = ["I", "love", "chocolate", "frogs"]; var madeAString = myArray.join(" "); // returns "I love chocolate frogs" var madeAString = myArray.join(); // returns "I,love,chocolate,frogs"

If no parameter is included to specify a joining/delimiting character, a comma is used by default.

Function scope and conflicts

Let"s talk a bit about scope - a very important concept when dealing with functions. When you create a function, the variables and other things defined inside the function are inside their own separate scope , meaning that they are locked away in their own separate compartments, unreachable from inside other functions or from code outside the functions.

The top level outside all your functions is called the global scope . Values defined in the global scope are accessible from everywhere in the code.

JavaScript is set up like this for various reasons - but mainly because of security and organization. Sometimes you don"t want variables to be accessible from everywhere in the code - external scripts that you call in from elsewhere could start to mess with your code and cause problems because they happen to be using the same variable names as other parts of the code, causing conflicts. This might be done maliciously, or just by accident.

For example, say you have an HTML file that is calling in two external JavaScript files, and both of them have a variable and a function defined that use the same name:

greeting(); // first.js var name = "Chris"; function greeting() { alert("Hello " + name + ": welcome to our company."); } // second.js var name = "Zaptec"; function greeting() { alert("Our company is called " + name + "."); }

Both functions you want to call are called greeting() , but you can only ever access the second.js file"s greeting() function - it is applied to the HTML later on in the source code, so its variable and function overwrite the ones in first.js .

Keeping parts of your code locked away in functions avoids such problems, and is considered best practice.

It is a bit like a zoo. The lions, zebras, tigers, and penguins are kept in their own enclosures, and only have access to the things inside their enclosures - in the same manner as the function scopes. If they were able to get into other enclosures, problems would occur. At best, different animals would feel really uncomfortable inside unfamiliar habitats - a lion or tiger would feel terrible inside the penguins" watery, icy domain. At worst, the lions and tigers might try to eat the penguins!

The zoo keeper is like the global scope - he or she has the keys to access every enclosure, to restock food, tend to sick animals, etc.

Active learning: Playing with scope

Let"s look at a real example to demonstrate scoping.

  • First, make a local copy of our function-scope.html example. This contains two functions called a() and b() , and three variables - x , y , and z - two of which are defined inside the functions, and one in the global scope. It also contains a third function called output() , which takes a single parameter and outputs it in a paragraph on the page.
  • Open the example up in a browser and in your text editor.
  • Open the JavaScript console in your browser developer tools. In the JavaScript console, enter the following command: output(x); You should see the value of variable x output to the screen.
  • Now try entering the following in your console output(y); output(z); Both of these should return an error along the lines of "ReferenceError: y is not defined ". Why is that? Because of function scope - y and z are locked inside the a() and b() functions, so output() can"t access them when called from the global scope.
  • However, what about when it"s called from inside another function? Try editing a() and b() so they look like this: function a() { var y = 2; output(y); } function b() { var z = 3; output(z); } Save the code and reload it in your browser, then try calling the a() and b() functions from the JavaScript console: a(); b(); You should see the y and z values output in the page. This works fine, as the output() function is being called inside the other functions - in the same scope as the variables it is printing are defined in, in each case. output() itself is available from anywhere, as it is defined in the global scope.
  • Now try updating your code like this: function a() { var y = 2; output(x); } function b() { var z = 3; output(x); } Save and reload again, and try this again in your JavaScript console:
  • a(); b(); Both the a() and b() call should output the value of x - 1. These work fine because even though the output() calls are not in the same scope as x is defined in, x is a global variable so is available inside all code, everywhere.
  • Finally, try updating your code like this: function a() { var y = 2; output(z); } function b() { var z = 3; output(y); } Save and reload again, and try this again in your JavaScript console:
  • a(); b(); This time the a() and b() calls will both return that annoying "
  • Функция - это программный код, который определяется один раз и затем может вызываться на выполнение любое количество раз.

    В JavaScript функция является значением, поэтому её можно присваивать переменным, элементам массива, свойствам объектов, передавать в качестве аргумента функциям и возвращать в качестве значения из функций.

    Объявление и вызов функции

    Существует три способа объявления функции: Function Declaration, Function Expression и Named Function Expression.

    Function Declaration (сокращённо FD) – это "классическое" объявление функции. В JavaScript функции объявляются с помощью литерала функции. Синтаксис объявления FD:

    Литерал функции состоит из следующих четырёх частей:

  • Ключевое слово function .
  • Обязательный идентификатор, определяющий имя функции. В качестве имени функции обычно выбирают глагол, т. к. функция выполняет действие.
  • Пара круглых скобок вокруг списка из нуля или более идентификаторов, разделяемых запятыми. Данные идентификаторы называются параметрами функции.
  • Тело функции, состоящее из пары фигурных скобок, внутри которых располагаются инструкции. Тело функции может быть пустым, но фигурные скобки должны быть указаны всегда.
  • Простой пример:

    Function sayHi() { alert("Hello"); }

    Встречая ключевое слово function интерпретатор создаёт функцию и затем присваивает ссылку на неё переменной с именем sayHi (переменная с данным именем создаётся интерпретатором автоматически).

    Обратившись к переменной sayHi можно увидеть, что в качестве значения там находится функция (на самом деле ссылка на неё):

    Alert(sayHi); // function sayHi() { alert("Hello"); }

    Function Expression (сокращённо FE) – это объявление функции, которое является частью какого-либо выражения (например присваивания). Синтаксис объявления FE:

    Function (параметры) { инструкции }

    Простой пример:

    Var sayHi = function () { alert("Hello"); };

    Функцию FE иначе ещё называют "анонимной функцией ".

    Named Function Expression (сокращённо NFE) – это объявление функции, которое является частью какого-либо выражения (например присваивания). Синтаксис объявления NFE:

    Function идентификатор (параметры) { инструкции }

    Простой пример:

    Var sayHi = function foo() { alert("Hello"); };

    Объявления FE и NFE обрабатываются интерпретатором точно так же, как и объявление FD: интерпретатор создаёт функцию и сохраняет ссылку на неё в переменной sayHi.

    Программный код, расположенный в теле функции, выполняется не в момент объявления функции, а в момент её вызова. Для вызова функции используется оператор () (вызов функции):

    Function sayHi() { alert("Hello"); } var sayHi2 = function () { alert("Hello2"); }; var sayHi3 = function foo() { alert("Hello3"); }; sayHi(); // "Hello" sayHi2(); // "Hello2" sayHi3(); // "Hello3"

    Разница между представленными тремя объявлениями заключается в том, что функции, объявленные как FD, создаются интерпретатором до начала выполнения кода (на этапе анализа), поэтому их можно вызывать (в той области видимости где они объявлены) до объявления:

    // Вызов функции до её объявления в коде верхнего уровня foo(); function foo() { alert("Вызов функции foo() в глобальной области видимости."); // Вызов функции до её объявления в области видимости функции bar(); function bar() { alert("Вызов функции bar() в области видимости функции."); } }

    Функции, объявленные как FE или NFE, создаются в процессе выполнения кода, поэтому их можно вызывать только после того как они объявлены:

    // sayHi(); // Ошибка. Функция sayHi ещё не существует var sayHi = function () { alert("Hello!"); }; sayHi();

    Функции, объявленные внутри блока, находятся в блочной области видимости:

    // foo(); // Ошибка. Функция не объявлена. { foo(); // 1 function foo() { console.log(1); } } foo(); // Ошибка. Функция не объявлена.

    В отличие от FE, функция, объявленная как NFE, имеет возможность обращаться к себе по имени при рекурсивном вызове. Имя функции доступно только внутри самой функции:

    (function sayHi(str) { if (str) { return; } sayHi("hi"); // Имя доступно внутри функции })(); sayHi(); // Ошибка. Функция не объявлена

    Функция обратного вызова

    Функция обратного вызова – это функция, которая передаётся в качестве аргумента другой функции для последующего её вызова.

    Функции обратного вызова часто используются, в качестве обработчиков событий.

    Ниже приведён пример функции, принимающей в качестве своего аргумента ссылку на другую функцию для её последующего вызова:

    Function foo(callback) { return callback(); } foo (function() { alert("Hello!"); });

    Этот пример наглядно демонстрирует принцип действия обратного вызова.

    Как создать функцию в JavaScript ? Такой вопрос достаточно распространён среди начинающих программистов. И эта статья написана как раз для таких новичков. Для начала необходимо разобраться с вопросом: а что такое функция? Давайте с этого и начнём.

    Вы должны помнить из школы, что такое функция. Если нет, то напоминаю. У функции есть определённые параметры, которыми она манипулирует, и возвращает результат. Например, функция y = 2 * x +5. Здесь мы можем задать x = 3, а в ответ получим y = 11. Вот это пример функции из математики. Абсолютно аналогичные функции и в JavaScript , только тут функцией может быть не просто какое-то вычисление какого-то выражения, а всё, что угодно.

    Давайте для начала создадим функцию, которая 3 раза выводит сообщение "Привет ".


    function hello() {
    alert("Привет");
    alert("Привет");
    alert("Привет");
    }
    hello();

    Первая строчка сообщает, что дальше идёт сценарий на языке JavaScript . На следующей строке объявляется функция. Как видите, всё начинается с ключевого слова function . Внутри скобок указываются параметры, но в данном случае параметров нет, поэтому внутри скобок всё пусто. Дальше идут фигурные скобки, внутри которых находится код функции, который должен быть выполнен при её вызове. В качестве этого кода используется функция alert() , которая вызывает информационное окошко, в котором написан текст, заданный параметром. В качестве параметра мы функции alert() (это пример встроенной функции) передаём строку "Привет ". Функцию alert() мы вызываем три раза.

    Когда функция написана, то необходимо поставить закрывающую фигурную скобку. На следующей строке мы вызываем функцию hello() . Надеюсь, Вам стало понятно, что такое функция. Возможно, что некоторые из Вас спросят: "А какие преимущества? Ведь мы могли бы просто написать три раза alert() и результат был бы тот же". Вы правы, и так и нужно поступать, если Вам нужно использовать этот код всего один раз. А представьте, что Вам это нужно 3, 4 или большее число раз. Разумеется, неудобно писать постоянно этот повторяющийся код. Гораздо проще написать одно слово hello(). Согласны? Думаю, что да. В конце статьи я, исходя из этого, сделаю один очень важный вывод, поэтому дочитайте её до конца.

    Теперь поговорим о функциях с параметрами. Давайте создадим один из таких примеров (буду писать сразу функцию и её вызов, без тега ).

    Function sum (x, y) {
    var sum = x + y;
    document.write(sum);
    }
    sum(5, 4);

    Код достаточно прозрачный, однако, давайте прокомментирую. Опять ключевое слово function , потом название функции (в данном случае, пусть будет sum ). Внутри скобок указал два параметра, которые требуются (x и y ). Внутри функции я создаю ещё одну переменную sum (это совершенно нормально называть переменные и функции одинаково), которой присваиваю сумму x и y (которые были переданы). И затем вывожу в окно браузера полученный результат. После функции вызываю её, передав параметры 5 и 4 . Вы можете проверить, и увидите в окне браузера - 9 .

    И, напоследок, скажу о возвращаемых значениях. В примере выше мы результат сразу печатали, однако, для данной функции будет наиболее логично ничего не печатать, а возвратить результат. А что дальше с ним делать - это уже другой вопрос. Давайте перепишем функцию так:

    Function sum(x, y) {
    var sum = x + y;
    return sum;
    }
    var z = sum(4, 5) + sum(1,-3);
    document.write(z);

    Обратите внимание на ключевое слово return . Оно возвращает результат (в нашем случае - сумму двух чисел). Таким образом, sum(4,5) возвращает 9 . Это число мы складываем с результатом работы функции sum(1, -3) , то есть -2 . В итоге мы получим 7 . И только потом печатаем результат в браузере.

    Надеюсь, Вы оценили возможности функций, поэтому создавать и использовать функции в JavaScript надо обязательно. И сейчас я сделаю один важный вывод, принцип, которому надо всегда следовать: любой повторяющийся код - необходимо выделять в отдельную функцию . Если у Вас 20 раз выводится одинаковые 5 строк (к примеру), то необходимо эти 5 строк выделить в отдельную функцию. Тем самым Вы вместо 100 строк (20 раз * 5 строк), напишите только 20 строк, да ещё и более понятных (название функции - это гораздо проще, чем 5 строк кода). Хочется сказать, что если этому правилу не следовать, то размер программы может увеличиться в десятки, а, может, и в сотни раз.

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

    Любому программисту хорошо известно, что такое функции и зачем они нужны. Однако функциям в языке Javascript присущи некоторые особенности. Если вы давно программируете именно на этом языке, то наверняка знаете, что существуют разные . Если же вы пришли из другого языка, то при прочтении некоторых статей скорее всего видели вот такое, странное на первый взгляд, объявление функции:

    Var add = function(arg1, arg2) { var sum = arg1 + arg2; return sum; } var result = add(5, 2); //result теперь 7

    То есть функция, во-первых, не имеет имени. Во-вторых, она присваивается переменной, но не просто присваивается, а тут же идет ее тело. Лично у меня, до этого писавшего на таких языках как VB, C++, подобное объявление вызывало недоумение и непонимание, как это устроено и зачем вообще так писать.

    Я привык к "классическому" объявлению функции и ее вызову, вот так:

    Function add(arg1, arg2) { var sum = arg1 + arg2; return sum; } var result = add(5, 3); //result теперь 8

    И вот здесь как раз мы подходим к особенностям функций в Javascript. Для простоты понимания представьте себе, что функция в JS - это обычное значение, как, например, число или строка. Вы же можете записать в переменную result число 5? Или что-то более сложное, вроде массива, а потом вывести это на экран? Можете. Так вот, если представить, что функция - это обычное значение, пусть и весьма сложной структуры, то первый способ объявления уже не кажется чем-то невероятным.

    Следующий интересный факт является логическим продолжением первого. После того как мы помещаем данные в переменную, мы можем посредством имени этой переменной передать данные в другую переменную:

    Var a = 5; var b = a; alert(b); //выведет 5

    Обычное дело. А теперь взгляните вот на этот код:

    Var add = function(arg1, arg2) { var sum = arg1 + arg2; return sum; } var calcSum = add; alert(calcSum(5, 6)); //выведет 11

    Начинаете уже догадываться? Поскольку функция - как переменная, мы можем "размножать" ее посредством обычного присваивания в другие переменные, превращая их так же в функции. Теперь calcSum тоже умеет складывать два числа. Однако код

    Var calcSum = add(1, 1); //calcSum теперь равно 2, это не функция, а переменная с числом alert(calcSum(5, 6)); //ошибка

    Не выполнится, поскольку в первой строке мы присвоили не саму функцию, а результат ее выполнения (круглые скобки говорят о том, что нужно выполнить функцию, а не присвоить).

    Если вам понадобится вызвать функцию в самой себе, то это делается следующим образом:

    Var calcFact = function fact(val) { if (val == 1) ? val: val * fact(val — 1); //вычисление факториала с помощью рекурсии } alert(calcFact(4)); //выведет 24

    Здесь, присваивая функцию переменной, мы задали ей имя fact. Однако это имя будет доступно только внутри самой функции и нигде больше. Причины этого кроются в принципе работы интерпретатора и выходят за рамки урока.

    Возможно, вы задаетесь вопросом "Хм, интересная возможность! Но какое преимущество у этого способа? Есть ли ситуации, когда без этого не обойтись или это хотя бы более удобно, чем обычное объявление?". Не возьмусь утверждать, что есть ситуации, где без такого подхода обойтись нельзя, но пример, где он уменьшает количество кода, привести могу. Допустим, вам нужно поприветствовать человека в зависимости от времени суток:

    Var date = new Date(); var hello = (date.getHours() < 12) ? function() {alert("Доброе утро!")} : (date.getHours() < 18) ? function() {alert("Добрый день!")} : function() {alert("Добрый вечер!")}; hello();

    Как видите, функции крайне простые, с одной-единственной командой alert.

    Если бы мы решили пойти "классическим путем", то пришлось бы писать три отдельные функции, а потом вызывать их в условии проверки времени:

    Function goodMorning() { alert("Доброе утро!"); } function goodAfternoon() { alert("Добрый день!"); } function goodEvning() { alert("Добрый вечер!"); } var date = new Date(); (date.getHours() < 12) ? goodMorning() : (date.getHours() < 18) ? goodAfternoon() : goodEvning();

    Код значительно увеличился визуально, даже учитывая, что мы использовали краткую форму записи условного оператора. Если допустить, что файл содержит действительно важные функции, выполняющие вычисления, то засорять список вот такими мини-функциями, не несущими важной логики, да и которые используются, скорее всего, лишь однажды, не лучшая идея. К тому же мы вынуждены каждой функции давать уникальное имя и указывать его при вызове. Поэтому, если понадобится изменить название одной из них, то придется менять его в двух местах, что повышает вероятность возникновения ошибки.

    Во-вторых, если мы используем "классический" метод, то лишаемся возможности присвоить функцию переменной. То есть написать

    Function add(a, b) { return a + b; } var calcSum = add; calcSum(5, 5);

    Уже нельзя. Поэтому в нашем примере, если все-таки понадобится приветствовать гостя не единожды, нам придется каждый раз дублировать вот этот фрагмент:

    (date.getHours() < 12) ? goodMorning() : (date.getHours() < 18) ? goodAfternoon() : goodEvning();

    А в первом случае достаточно будет написать всего лишь hello(); и результат будет тот же.

    Я рассказал вам про интересную особенность функций JS и привел примеры. Таким образом вы увидели, что способы вызова функций в Javascript не ограничиваются одним-единственным видом. Даже если вы и не сможете сразу найти применение этим возможностям в ваших проектах, то хотя бы будете знать, что такие возможности есть. И когда подвернется действительно удачный случай, то сможете сократить объем кода и избежать лишней путаницы и ошибок.!