Как найти определение функции в javascript

Содержание

  • Введение
  • Величины, типы и операторы
  • Структура программ
  • Функции
  • Структуры данных: объекты и массивы
  • Функции высшего порядка
  • Тайная жизнь объектов
  • Проект: электронная жизнь
  • Поиск и обработка ошибок
  • Регулярные выражения
  • Модули
  • Проект: язык программирования
  • JavaScript и браузер
  • Document Object Model
  • Обработка событий
  • Проект: игра-платформер
  • Рисование на холсте
  • HTTP
  • Формы и поля форм
  • Проект: Paint
  • Node.js
  • Проект: веб-сайт по обмену опытом
  • Песочница для кода

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

Дональд Кнут

Вы уже видели вызовы функций, таких как 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. В другом, она возвращается в конец программы.

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

Хранение стека требует места в памяти. Когда стек слишком сильно разрастается, компьютер прекращает выполнение и выдаёт что-то вроде “out of stack space” или “ 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) в качестве параметра number.

Рекурсия

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

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` используется в виде выражения, можно создавать функцию. Когда оно используется как инструкция, вы можете объявлять переменную, назначая ей функцию.

// Создаём f и присваиваем ей функцию
var f = function(a) {
  console.log(a + 2);
};

// Объявляем функцию g
function g(a, b) {
  return a * b * 3.5;
}

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

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

Упражнения

Минимум

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

console.log(min(0, 10));
// → 0
console.log(min(0, -10));
// → -10
Рекурсия

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

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

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

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

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, чтобы использовать новую функцию.

You encounter VM defined JS function ,you can try this command in Chrome console panel below.
Like this:
foo function name is window.P.execute

>window.P.execute.toString()
<'function(d,h){function n(){var a=null;e?a=h:"function"===typeof h&&(p.start=w(),a=h.apply(f,wa(d,k,l)),p.end=w());if(b){H[d]=a;a=d;for(da[a]=!0;(z[a]||[]).length;)z[a].shift()();delete z[a]}p.done=!0}var k=g||this;"function"===typeof d&&(h=d,d=E);b&&(d=d?d.replace(ha,""):"__NONAME__",V.hasOwnProperty(d)&&k.error(q(", reregistered by ",q(" by ",d+" already registered",V[d]),k.attribution),d),V[d]=k.attribution);for(var l=[],m=0;m<a.length;m++)l[m]=na[m].replace(ha,"");var p=B[d||"anon"+ ++xa]={depend:l,registered:w(),namespace:k.namespace};d&&ya.hasOwnProperty(d);c?n():ua(l,k.guardFatal(d,n),d);return{decorate:function(a){U[d]=k.guardFatal(d,a)}}}'

so we got full function code.

9 ноября, 2017 12:26 пп
2 496 views
| Комментариев нет

Java

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

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

Определение функции

Функции определяются или объявляются с помощью ключевого слова function. Синтаксис функции в JavaScript выглядит так:

function nameOfFunction() {
// Code to be executed
}

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

Читайте также:

  • Условные операторы в JavaScript
  • Циклы for в JavaScript

Для примера создайте файл greet.js и составьте в нем объявление функции, которая будет выводить в консоль приветствие:

// Initialize greeting function
function greet() {
console.log("Hello, World!");
}

Этот код выведет в консоль фразу Hello, World!, содержащуюся внутри функции greet (). Однако код не будет выполняться до тех пор, пока вы не вызовете функцию. Чтобы вызвать функцию, укажите имя функции и скобки.

// Invoke the function
greet();

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

// Initialize greeting function
function greet() {
console.log("Hello, World!");
}
// Invoke the function
greet();

Последняя строка – вызов функции – запустит функцию и выведет в консоль фразу «Hello, World!».

Hello, World!

Теперь вы можете повторно использовать эту функцию в программе.

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

Параметры функции

В файле greet.js вы создали базовую функцию, которая выводит в консоль приветствие. С помощью параметров можно расширить действия функции и сделать код более гибким. Параметры – это входные данные, которые передаются в функции как имена и ведут себя как локальные переменные.

К примеру, когда пользователи входят в приложение, программа может приветствовать их по имени.

Добавьте в функцию параметр name, чтобы объявить имя приветствуемого пользователя.

// Initialize custom greeting function
function greet(name) {
console.log(`Hello, ${name}!`);
}

Теперь функция greet имеет один параметр внутри круглых скобок. Имена параметров следуют тем же правилам, что и имена переменных. Теперь внутри функции вместо статической строки «Hello, World» есть строка шаблона, содержащая параметр, который ведет себя как локальная переменная.

Читайте также: Работа со строками в JavaScript

Как вы могли заметить, значение параметра name не присваивается в коде, это делается при вызове функции. При вызове функции имя пользователя передается в качестве аргумента. Аргумент – это фактическое значение, которое передается в функцию (в данном случае это имя пользователя, например 8host).

// Invoke greet function with "8host" as the argument
greet("8host");

Значение 8host передается в функцию через параметр name. Теперь параметр name будет представлять в данной функции это значение. Код файла greetUser.js выглядит так:

// Initialize custom greeting function
function greet(name) {
console.log(`Hello, ${name}!`);
}
// Invoke greet function with "8host" as the argument
greet("8host");

Запустив эту программу, вы получите такой вывод:

Hello, 8host!

Теперь вы знаете, как можно повторно использовать функцию.

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

Возвращение значений

В функции можно использовать более одного параметра. Вы можете передать в функцию несколько значений и вернуть значение. Для примера создайте файл sum.js и объявите в нем функцию, которая будет искать сумму двух значений, x и y.

// Initialize add function
function add(x, y) {
return x + y;
}
// Invoke function to find the sum
add(9, 7);

В этом коде определяется функция с параметрами х и у. Затем функция получает значения 9 и 7. Запустите программу:

16

Программа сложит полученные значения, 9 и 7, и вернет результат 16.

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

Функциональные выражения

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

Используйте предыдущую функцию, чтобы применить полученное значение к переменной sum.

// Assign add function to sum constant
const sum = function add(x, y) {
return x + y;
}
// Invoke function to find the sum
sum(20, 5);
25

Теперь константа sum является функцией. Это выражение можно сократить, превратив его в анонимную функцию (так называются функции без параметра name). В настоящее время функция называется add, но в функциональных выражениях имя обычно опускается.

// Assign function to sum constant
const sum = function(x, y) {
return x + y;
}
// Invoke function to find the sum
sum(100, 3);
103

Теперь у функции больше нет имени, она стала анонимной.

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

Стрелочные функции

До сих пор функции определялись с помощью ключевого слова function. Тем не менее, существует более новый и сжатый способ определения функции – это стрелочные функции ECMAScript 6. Стрелочные функции представлены значком равенства, за которым следует знак больше: =>.

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

// Define multiply function
const multiply = (x, y) => {
return x * y;
}
// Invoke function to find product
multiply(30, 4);
120

Вместо того чтобы писать function, можно просто использовать символы =>.

В остальном функция работает аналогично регулярному функциональному выражению, хотя и здесь есть несколько отличий, больше о которых можно узнать на Mozilla Developer Network.

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

// Define square function
const square = x => {
return x * x;
}
// Invoke function to find product
square(8);
64

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

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

// Define square function
const square = x => x * x;
// Invoke function to find product
square(10);
100

Заключение

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

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

Tags: Javascript

Функция (в общем случае) — это отношение между элементами, при котором изменение в одном элементе влечёт изменение в другом.

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

Функции позволяют не повторять один и тот же код во многих местах программы и являются основными «строительными блоками» программ. Языки программирования позволяют программисту использовать как встроенные функции (например, alert(message), prompt(message, default)confirm(question)), так  и создавать свои.

Функция:

  • должна быть соответствующим образом объявлена и определена;
  • может вызываться и принимать параметры;
  • определяет собственную (локальную) область видимости, куда входят:
    1. входные параметры, а также
    2. те переменные, которые объявляются непосредственно в теле самой функции;
  • должна возвращать некоторое значение (возможно пустое или другую функцию).

Процедура – это функция, которая возвращает пустое значение (при этом выполнение кода процедуры непосредственно сказывается на выполнении программы). 

Метод – это функция, которая является свойством объекта.

Важно отличать вызов функции от вызова метода:

  • для вызова метода необходим объект, которому принадлежит вызываемый метод (object.functionProperty() или object[‘functionProperty’]()), а для вызова функции — нет (expression()).

Замечание

В JS любая процедура на самом деле является функцией без return: если опустить return, функция всё равно неявно возвращает undefined и остаётся функцией.

[свернуть]

В JavaScript любая функция – это объект, и следовательно, ею можно манипулировать как объектом, в частности:

  1. передавать как аргумент и возвращать в качестве результата при вызове других функций (функций высшего порядка);
  2. создавать анонимно и присваивать в качестве значений переменных или свойств объектов.

Примеры

// Копирование функции *************************************

function sayHi() { // объявляем функцию

console.log( “Привет” );

}

let func = sayHi; // копируем (теперь функция может быть вызвана с помощью обеих переменных sayHi() и func())

func(); // Привет (вызываем копию – работает)

sayHi(); // Привет (тоже работает)

// Функция как свойство объекта ***************************

let obj = { doSomething : function(){} };

[свернуть]

Хорошим тоном в программировании является правило: одна функция – одно действие.

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

Хранение функций в JS

В JavaScript функции могут храниться тремя способами:

  1. в переменной:
    • let fn = function doSomething() {};

  2. в объекте:
    • let obj = { doSomething : function(){} };

  3. в массиве:
    • arr.push(function doSomething() {})

Объявлять функции в JavaScript можно с помощью синтаксиса:

  1. “function declaration”;
  2. “function expression”.

Объявление функции (Function Declaration)

Для создания функций используется её объявление, например:

function name([param1, param2, ..., paramN])  {

statements

};

где 

  • function – ключевое слово;
  • name – имя функции;
  • (paramN) –  список параметров (аргументов) в круглых скобках через запятую (может быть пустым) ;
  • {statements} – инструкции или исполняемый код функции (определение функции или «тело функции»), внутри фигурных скобок.

function sayHello(firstName, lastName) {

  console.log(`Hello, ${firstName} ${lastName}`);

}

sayHello(“Alex”, “NAV”); // Hello, Alex NAV

Разбор примера

  • function – ключевое слово;
  • sayHello – имя функции;
  • (firstName, lastName) –  список параметров в круглых скобках через запятую (может быть пустым) ;
  • {console.log(Hello, ${firstName} ${lastName});} – код функции («тело функции»), внутри фигурных скобок;
  • sayHello(“Alex”, “NAV”) – вызов функции с передачей параметров (аргументов).

[свернуть]

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

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

Выбор имени функции

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

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

Например, функции, начинающиеся с :

  • “show” – обычно что-то показывают;
  • “get…” – возвращают значение;
  • “calc…” – что-то вычисляют;
  • “create…” – что-то создают;
  • “check…” – что-то проверяют и возвращают логическое значение, и т.д.

Примеры таких имён:

showMessage(..) // показывает сообщение

getAge(..) // возвращает возраст (в каком либо значении)

calcSum(..) // вычисляет сумму и возвращает результат

createForm(..) // создаёт форму (и обычно возвращает её)

checkPermission(..) // проверяет доступ, возвращая true/false

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

Замечание: Имена функций, которые используются очень часто, иногда делают сверхкороткими.

Например, во фреймворке jQuery есть функция с именем $. В библиотеке Lodash основная функция представлена именем _.

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

[свернуть]

Объявление функции (Function Expression)

Существует ещё один синтаксис создания функций, который называется Function Expression (Функциональное Выражение). В данном случае ключевое слово function может использоваться для определения функции внутри выражения (при этом функция может являться анонимной, т.е. может не иметь собственного именования).

Синтаксис:

var myFunction = function [name]([param1[, param2[, ..., paramN]]]) {

statements

};

где

  • name – имя функции, является локальным для её тела и может быть опущено (в таком случае функция является анонимной);
  • paramN – имя аргумента, передаваемого в функцию;
  • statements – инструкции (программный код), составляющие тело функции.

// Функциональное выражение, возвращающее значение с помощью оператора return

var square = function(x) {

  return x * x;

};

console.log(square(4)); // 16

// Функциональное выражение, используемое в качестве процедуры

let sayHi = function() {

  console.log(“Привет”);

};

sayHi(); // Привет

console.log(sayHi);  // выведет код функции (это не вызов функции, т.к. нет ())

Особенности использования функциональных выражений:

  1. Если необходимо сослаться на текущую функцию внутри тела этой функции, то нужно создать именованное функциональное выражение. Имя будет локальным (только для тела функции, её области видимости).
  2. Переменная, которой присвоено функциональное выражение, будет иметь свойство name, содержащее имя функции, и которое не изменяется при переприсваивании другой переменной:
    • для анонимной функции значением свойства name будет имя переменной (неявное имя);
    • если имя функции задано, то будет использовано имя функции (явное имя).
  3. При объявлении функции синтаксисом “function expression” “поднятие” или “всплытие” функции JavaScript (hoisting) не работает.

Примеры

Отсюда…

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

var math = {

  ‘factit’: function factorial(n) {

    console.log(n);

    if (n <= 1) {

      return 1;

    }

    return n * factorial(n 1);

  }

};

math.factit(3) //3;2;1;

Пример  использования неявного и явного имени функции:

var foo = function() {}

foo.name // “foo”

var foo2 = foo

foo2.name // “foo”

var bar = function baz() {}

bar.name // “baz”

console.log(foo === foo2); // true

console.log(typeof baz); // undefined

console.log(bar === baz); // Uncaught ReferenceError: baz is not defined

Пример ошибки при “поднятии (всплытии)”:

console.log(square(4)); // TypeError: square is not a function

var square = function(x) {

  return x * x;

};

[свернуть]

Параметры (аргументы) функции

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

Параметр функции — это переменная, которая используется в функции и указывается при объявлении функции в круглых скобках (если параметров несколько, то они указываются через запятую).

Аргумент функции — это значение, которое передаётся в функцию (указывается в скобках ) при её вызове.

// Объявление функции sayHello с параметрами firstName, lastName

function sayHello(firstName, lastName) {

  console.log(`Hello, ${firstName} ${lastName}`);

}

sayHello(“Alex”, “NAV”); // Вызов функции sayHello с аргументами “Alex”, “NAV”

В примере выше в функцию sayHello передаются два параметра: firstName и lastName. Когда функция вызывается, переданные значения копируются в локальные переменные firstName и lastName, которые затем используются в теле функции.

Параметры функции по умолчанию установлены на undefined.

Синтаксис “оставшиеся параметры”

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

function(a, b, ...theArgs) {

  // …

}

Если последний именованный аргумент функции имеет префикс , он автоматически становится массивом с элементами от 0 до theArgs.length в соответствии с актуальным количеством аргументов, переданных в функцию.

function myFunс(a, b, ...moreArgs) {

  console.log(“a”, a);

  console.log(“b”, b);

  console.log(“moreArgs”, moreArgs);

}

myFunс(“один”, 2, “три”, “четыре”, 5, “шесть”);

// Вывод в консоль:

// a, один

// b, два

// manyMoreArgs, [три, четыре, пять, шесть]

Отличия оставшихся параметров от объекта arguments:

  1. оставшиеся параметры включают только те, которым не задано отдельное имя, в то время как объект arguments содержит все аргументы, передаваемые в функцию;
  2. объект arguments не является массивом, в то время как оставшиеся параметры являются экземпляром Array и методы sort, map, forEach или pop могут непосредственно у них использоваться;
  3. объект arguments имеет дополнительную функциональность, специфичную только для него (например, свойство callee).

Оставшиеся параметры могут быть деструктуризованы (только массивы), т.е. их данные могут быть заданы как отдельные значения (подробнее…).

[свернуть]

Особенности использования параметров (аргументов) функций JavaScript:

  1. Параметры, которые передаются функции, могут передаваться как по значению, так и по ссылке.
  2. Определения функций JavaScript не указывают типы данных для параметров.
  3. Функции JavaScript не выполняют проверку типа на передаваемых аргументах.
  4. Функции JavaScript не проверяют количество полученных аргументов.

Особенности использования аргументов различных типов:

  1. Примитивные параметры (например, число) передаются функции значением; значение передаётся в функцию, но если функция меняет значение параметра, это изменение не отразится глобально или после вызова функции. Т.е. для переменной, переданной по значению, создаётся локальная копия и любые изменения, которые происходят в теле функции с переданной переменной, происходят с локальной копией и никак не сказываются на самой переменной.
  2. Для переменной, переданной по ссылке, изменения происходят в теле функции с самой переданной переменной. Т.е. если вы передадите как аргумент объект (не примитив, а например, массив или определяемые пользователем объекты), и функция изменит свойство переданного в неё объекта, это изменение будет видно и вне функции (пример ниже).

Пример

Отсюда…

function myFunc(theObject) {

theObject.make = ‘Toyota’;

}

var mycar = {make: ‘Honda’, model: ‘Accord’, year: 1998};

var x, y;

x = mycar.make; // x получает значение “Honda”

myFunc(mycar);

y = mycar.make; // y получает значение “Toyota” (свойство было изменено функцией)

[свернуть]

Параметры по умолчанию

См. также ES6 значения по умолчанию: базовый пример

Если параметр не указан, то его значением становится undefined:

function sayHello(firstName, lastName) {

  console.log(`Hello, ${firstName} ${lastName}`);

}

sayHello(“Alex”); // Hello, Alex undefined

В вызове не указан параметр lastName, поэтому предполагается что lastName=== undefined.

Если мы хотим задать параметру функции значение по умолчанию, то мы должны указать его после =:

function sayHelloDefault(firstName = “Default”, lastName = “Default”) {

  return `Hello, ${firstName} ${lastName}`;

}

let res_4 = sayHelloDefault();

let res_5 = sayHelloDefault(null);

console.log(res_4); // Hello, Default Default

console.log(res_5); // Hello, null Default

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

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

Использование объекта arguments функции

Объект arguments — это массивоподобный объект, доступный внутри любой нестрелочной функции и который содержит аргументы, переданные в функцию, а также позволяет ссылаться на них. Индексация аргументов начинается с 0.

Таким образом, внутри функции вы можете обращаться к аргументам следующим образом:

где i — это порядковый номер аргумента, отсчитывающийся с 0.

К первому аргументу, переданному функции, обращаются arguments[0]. Получить количество всех аргументов можно, используя свойство arguments.length.

Объект arguments функции является псевдо-массивом, в котором есть пронумерованные индексы и свойство length, к нему применимы некоторые методы массивов, например, for .. in. Однако он не обладает всеми методами массивов.

ВАЖНО! Никогда не называйте параметр arguments: он будет иметь приоритет над объектом arguments, который доступен для каждой функции.

С помощью объекта arguments вы можете вызвать функцию, передавая в неё больше аргументов, чем формально объявили принять. Это очень полезно, если вы не знаете точно, сколько аргументов должна принять функция. Вы можете использовать arguments.length для определения количества аргументов, переданных функции, а затем получить доступ к каждому аргументу, используя объект arguments.

Примеры использования объекта arguments

Функция, которая конкатенирует несколько строк:

function myConcat(separator) {

var result = ;

var i;

for (i = 1; i < arguments.length; i++) {

result += arguments[i] + separator;

}

return result;

}

myConcat(‘, ‘, ‘red’, ‘orange’, ‘blue’); // возвращает “red, orange, blue, “

myConcat(‘; ‘, ‘elephant’, ‘giraffe’, ‘lion’, ‘cheetah’); // возвращает “elephant; giraffe; lion; cheetah; “

myConcat(‘. ‘, ‘sage’, ‘basil’, ‘oregano’, ‘pepper’, ‘parsley’);

Использование for … in для объекта arguments

function func() {

for (value in arguments){

console.log(value);

}

}

func(1, 2, 3); // 1 // 2 // 3

Функция, которая принимает произвольное (любое) число массивов и удаляет из каждого массива первый элемент, а возвращает массив из оставшихся значений

function changeCollection() {

for (let i = 0; i < arguments.length; i++) {

if (Array.isArray(arguments[i]) === true) {

arguments[i] = arguments[i].slice(1);

}

}

return Array.from(arguments);

}

console.log(changeCollection([1, 2, 3], [4, 5, 6])); // Array [ (2) [2,3], (2) [5,6] ]

[свернуть]

Как и с обычными массивоподобными объектами для преобразования объекта arguments в обычный массив можно использовать метод Array.from() или оператор расширения ( ). Если последний именованный аргумент функции имеет префикс , он автоматически становится массивом с элементами от 0 до arguments .length в соответствии с актуальным количеством аргументов, переданных в функцию:

function myConcat(separator) {

  console.log(arguments[1] + separator + arguments[2]);

  console.log(Array.from(arguments));

  console.log([...arguments]);

}

myConcat(‘ : ‘,‘first’,‘second’) // вызов функции выведет в консоль:

// first : second

// (3) [” : “, “first”, “second”]

// (3) [” : “, “first”, “second”]

function sayHello(firstName = “Default”, lastName = “Default”, ...arguments) {

  console.log(`Hello, ${firstName} ${lastName}`); // Hello, null first

  console.log(`Hello, ${firstName} ${lastName} и остальные ${arguments}`); // Hello, null first и остальные 3,78

  console.log(...arguments); // 3 78

  console.log([...arguments]); // Array [3 78]

}

sayHello(null, “first”, 3, 78);

Переменные в функциях JavaScript

  1. Локальные переменные – это переменные, объявленные внутри функции, видны только внутри этой функции. Код вне функции не имеет доступа к её локальным переменным.
  2. Внешние переменные – это переменные, объявленные вне функции (например, во внешней функции при вложенности функций). Функция обладает полным доступом к внешним переменным и может изменять их значение.
  3. Глобальные переменные – это переменные, объявленные снаружи всех функций. Глобальные переменные видимы для любой функции (если только их не перекрывают одноимённые локальные переменные).

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

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

Алгоритм использования переменных в функции:

  1. Ищется локальная переменная (если она объявлена внутри тела функции).
  2. Внешняя переменная используется, только если внутри функции нет такой локальной.
  3. Если одноимённая переменная объявляется внутри функции, тогда она перекрывает внешнюю.

Примеры

Функция использует только локальную переменную:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

let x = 10; // внешняя (или глобальная) переменная

function foo() {

let x = 20; // объявление внутренней (локальной) переменной функции с помощью ключевых слов let, var или const

console.log(x);

return x; // для получения значения x вне функции

}

foo(); // 20 (вызов функции)

console.log(x + foo()); // 30 (10 + 20 = 30)

// ИЛИ *************************************************************

let userName = “Alex”;

function showMessage() {

  let userName = “Olga”; // инициируем локальную переменную

  let message =

    “Привет, “ + userName + ” (изменение внешней переменной (имени) функцией)”;

  console.log(message); // Привет, Olga (изменение локальной переменной (имени) функцией)

}

console.log(userName + ” (перед вызовом функции)”); // Alex (перед вызовом функции)

showMessage(); // Привет, Olga (изменение локальной переменной (имени) функцией)

console.log(userName + ” (после вызова функции)”); // Alex (после вызова функции)

// ИЛИ *******************************************************

function showMessage() {

let message = “Привет, я JavaScript!”; // локальная переменная

console.log( message );

}

showMessage(); // Привет, я JavaScript!

console.log( message ); // ошибка “ReferenceError: message is not defined”, т.к. локальная переменная вне функции не видна

У функции есть доступ к внешним переменным:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

let x = 10;

function foo() {

  x = 20; // без let, var или const функция изменяет глобальные переменные! НЕ рекомендуется!

  console.log(x); // выводим в консоль значение x = 20

  return x; // возврат значения x = 20 функцией

}

foo(); // вызываем функцию – изменяем значение внешней переменной

console.log(x + foo()); // 40 (20 + 20 = 40)

// ИЛИ *************************************************************

let userName = “Alex”;

function showMessage() {

  userName = “Olga”; // изменяем значение внешней переменной

  let message =

    “Привет, “ + userName + ” (изменение внешней переменной (имени) функцией)”;

  console.log(message); // Привет, Olga (изменение внешней переменной (имени) функцией)

}

console.log(userName + ” (перед вызовом функции)”); // Alex (перед вызовом функции)

showMessage();

console.log(userName + ” (после вызова функции)”); // Olga (после вызова функции)

[свернуть]

Вызов функции

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

sayHello(“Alex”, “NAV”); // Вызов функции sayHello с параметрами (“Alex”, “NAV”)

function sayHello(firstName, lastName) {

  console.log(`Hello, ${firstName} ${lastName}`);

}

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

Область видимости функции:

  • функция, в которой она определена, или
  • целая программа, если она объявлена по уровню выше.

Функции JS расположены  в области видимости, если они уже определены выше по коду, но функции вида “function declaration” могут быть подняты (“поднятие” или “всплытие” – hoisting), также как в примере выше (вызов функции расположен в коде выше, чем её объявление).

ВАЖНОЕ замечание (“поднятие” или “всплытие” функции JavaScript (hoisting))

“Поднятие” или “всплытие” функции JavaScript (hoisting) работает только тогда, когда объявлении функции использует синтаксис function funcName(){} (“function declaration”).

При объявлении функции синтаксисом “function expression” “поднятие” или “всплытие” функции JavaScript (hoisting) не работает.

Код ниже работать не будет!

console.log(square(4)); // TypeError: square is not a function

// Вызов функции синтаксис  <span style=”color: #ff0000;”>”function expression”</span>

var square = function(x) {

  return x * x;

};

[свернуть]

Рекурсивный вызов функции

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

Пример

Функция рекурсивного вычисления факториала:

function factorial(n) {

  if ((n === 0) || (n === 1))

  return 1;

  else

  return (n * factorial(n 1));

  }

console.log(factorial(5)); // 120 (результат вычисления 5!)

Функция перебора элементов массива:

const myArr = [

  [1, [“one”, “two”], 4],

  [5, 6],

  [7, 8, [9, 0]],

];

function iterationЕlements(arr) {

  for (let i = 0; i < arr.length; i++) {

    console.log(arr[i]);

    if (Array.isArray(arr[i])) {   // проверка элемента на тип Array

      iterationЕlements(arr[i]);   // рекурсия iterationЕlements(arr)

    }

  }

}

[свернуть]

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

Вызов функционального выражения по месту (IIFE)

Подробнее…

Когда функция (как выражение) обернута в пару ( ), мы можем вызвать эту функцию, добавив еще одни () в конце, например:

function foo(){ .. } ()

Первая окружающая пара ( ) делает функцию выражением, а вторая () – выполняет функцию.

// Вызов функции по месту

var a = 2;

(function foo() {

  var a = 3;

  console.log(a); // 3

})(); // сразу после объявления вызов на исполнение (вызов по месту)

console.log(a); // 2

Этот шаблон настолько в ходу, что существует термин для его обозначения: IIFE (Immediately (немедленно) Invoked (вызываемое) Function (функциональное) Expression (выражение)).

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

var a = 2;

(function IIFE() {

  var a = 3;

  console.log(a); // 3

})();

console.log(a); // 2

Другой вариант традиционной формы IIFE:

( function(){ .. } () )

Эти две формы идентичны по функциональности. Какую из них предпочесть — всего лишь ваш стилистический выбор.

Возврат значения функции JS

Функция может вернуть результат, который будет передан в вызвавший её код, с помощью директивы return.

var square = function(x) {

return x * x;

};

console.log(square(4)); // 16

// ИЛИ ************************************

function sum(a, b) {

return a + b;

}

let result = sum(1, 2);

alert( result ); // 3

Функция возвращает другую функцию

let val = 7;

function createAdder() {

  function addNumbers(a, b) {

    let ret = a + b;

    return ret;

  }

  return addNumbers;

}

let adder = createAdder();

let sum = adder(val, 8);

console.log(“Example of function returning a function: “, sum);

Разберем исполнение этого кода:

  1. Строка 1. Мы объявляем переменную val в глобальном контексте выполнения и присваиваем число 7 этой переменной.
  2. Строки 2-8. Мы объявляем переменную с именем createAdder в глобальном контексте выполнения и назначаем ей определение функции. Строки 3-7 описывают указанное определение функции. Но на данный момент тело этой функции не выполняется. Мы просто сохраняем определение функции в этой переменной (createAdder).
  3. Строка 9. Мы объявляем новую переменную с именем adder в глобальном контексте выполнения. Временно переменной adder присваивается undefined.
  4. Строка 9. Скобки () указывают, что нужно выполнить или вызвать функцию с именем createAdder, которая была создана на шаге 2:
    1. вызов функции createAdder (теперь мы находимся в строке 2);
    2. создается новый локальный контекст выполнения, мы можем создавать локальные переменные в новом контексте выполнения;
    3. движок добавляет новый контекст в стек вызовов. Функция createAdder не имеет аргументов, перейдем прямо к ее телу.
  5. Строки 3-6. У нас есть новое объявление функции. Мы создаем переменную addNumbers в локальном контексте выполнения (это важно, addNumbers существует только в локальном контексте выполнения). Мы сохраняем определение функции в локальной переменной с именем addNumbers, и переходим к строке 7.
  6. Строка 7. Мы возвращаем содержимое переменной addNumbers. Движок ищет переменную с именем addNumbers и находит ее: это определение функции. Таким образом, далее:
    1. возвращается (return addNumbers;) определение функции addNumbers, т.е. всё что находится в скобках в строках 4 и 5;
    2. удаляется локальный контекст выполнения из стека вызовов (переменная addNumbers больше не существует;
    3. определение функции addNumbers все еще существует, оно возвращается из функции и присваивается переменной adder (это переменная, которую мы создали на шаге 3).
  7. Строка 10. Мы определяем новую переменную sum в глобальном контексте выполнения. Временное значение undefined. Далее нужно выполнить функцию, которая определена в переменной с именем adder и которая принимает два параметра:
    1. переменная val, которую мы определили на шаге 1, она представляет число 7;
    2. число 8.
  8. Теперь мы должны выполнить эту функцию. Определение функции обозначено строками 3-5. Новый локальный контекст выполнения создан. В локальном контексте создаются две новые переменные: a и b. Им соответственно присваиваются значения 7 и 8, так как это были аргументы, которые мы передали функции на предыдущем шаге.
  9. Строка 4. В локальном контексте выполнения объявлена ​​новая переменная ret.
  10. Строка 4. Выполняется сложение содержимого переменной a и содержимого переменной b. Результат сложения (15) присваивается переменной ret.
  11. Переменная ret возвращается из этой функции. Локальный контекст выполнения уничтожается, он удаляется из стека вызовов, переменные a, b и ret больше не существуют. Возвращаемое значение присваивается переменной суммы, которую мы определили на шаге 9.
  12. Мы выводим значение суммы на консоль.

[свернуть]

Ещё примеры с разбором…

Особенности директивы return:

  1. Директива return может находиться в любом месте тела функции.
  2. Код в теле функции после директивы return не выполняется.
  3. Как только выполнение доходит до этого места, функция останавливается, и значение возвращается в вызвавший её код.
  4. Возможно использовать return и без значения, что приведёт к немедленному выходу из функции.
  5. Результат функции с пустым return или без него – undefined.
  6. Вызовов return может быть несколько.

Примеры

function sayHello(firstName, lastName) {

  if (!firstName || !lastName)

    return console.error(“Не введены параметры функции”);

  return `Hello, ${firstName} ${lastName}`;

  // после return код не выполняется

  console.log(“Hello, world!”); // выдаст подсказку ‘unreachable code after return statement’, код выполнен не будет

}

let res = sayHello(“Alex”, “VAN”);

let res1 = sayHello(“Alex”, “NAV”) + “!”;

let res2 = sayHello() + “!”;

console.log(res); // Hello, Alex VAN

console.log(res1); // Hello, Alex NAV!

console.log(res2); // Будет выведена ошибка “Не введены параметры функции” и затем “undefined!”

Несколько вызовов директивы return:

function checkAge(age) {

  if (age > 18) {

    return true;

  } else {

    return confirm(‘А родители разрешили?’);

  }

}

let age = prompt(‘Сколько вам лет?’, 18);

if ( checkAge(age) ) {

  alert( ‘Доступ получен’ );

} else {

  alert( ‘Доступ закрыт’ );

}

[свернуть]

Никогда не добавляйте перевод строки между return и его значением!

Для длинного выражения в return может быть заманчиво разместить его на нескольких отдельных строках, например так:

return

(some + long + expression + or + whatever * f(a) + f(b))

Код не выполнится, потому что интерпретатор JavaScript подставит точку с запятой после return. Для него это будет выглядеть так:

return;

(some + long + expression + or + whatever * f(a) + f(b))

Таким образом, это фактически стало пустым return.

Если мы хотим, чтобы возвращаемое выражение занимало несколько строк, нужно начать его на той же строке, что и return. Или, хотя бы, поставить там открывающую скобку, вот так:

return (

some + long + expression

+ or +

whatever * f(a) + f(b)

)

Свойства функций

Функция — это объект, поэтому она может иметь свои собственные свойства, например name, length или методы toString(), bind(), apply() и call().

let log = function logMessage(message) {

  console.log(message);

};

console.log(log.name); // logMessage

console.log(log.length); // 1

console.log(log.toString());

// function logMessage(message) {

//   console.log(message);

// }

Вступление

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

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

Определение функции

Функции определяются или объявляются с помощью ключевого слова + function +. Ниже приведен синтаксис функции в JavaScript.

function nameOfFunction() {
   // Code to be executed
}

Объявление начинается с ключевого слова + function +, за которым следует имя функции. Имена функций подчиняются тем же правилам, что и переменные – они могут содержать буквы, цифры, знаки подчеркивания и знаки доллара и часто пишутся по адресу https://www.digitalocean.com/community/tutorials/understanding-syntax-and-code-structure- in-javascript # идентификаторы [случай верблюда]. За именем следует набор скобок, которые можно использовать для необязательных параметров. Код функции содержится в фигурных скобках, например, в выражении for или в https: / /www.digitalocean.com/community/tutorials/how-to-write-conditional-statements-in-javascript[if Statement].

В нашем первом примере мы сделаем * объявление функции *, чтобы напечатать оператор приветствия на консоль.

// Initialize greeting function
function greet() {
   console.log("Hello, World!");
}

Здесь у нас есть код для вывода + Hello, World! + На консоль, содержащийся внутри функции + greet () +. Однако ничего не произойдет, и никакой код не будет выполняться, пока мы * не вызовем * или не вызовем функцию. Вы можете вызвать функцию, написав имя функции с последующими круглыми скобками.

// Invoke the function
greet();

Теперь мы соберем их вместе, определим нашу функцию и вызовем ее.

greet.js

// Initialize greeting function
function greet() {
   console.log("Hello, World!");
}

// Invoke the function
greet();

При вызове + greet (); + функция запустится, и мы получим + Hello, World! + В качестве вывода программы.

Теперь у нас есть код + greet () +, содержащийся в функции, и мы можем использовать его столько раз, сколько захотим.

Используя параметры, мы можем сделать код более динамичным.

Параметры функции

В нашем файле + greet.js мы создали базовую функцию, которая выводит на консоль` + Hello, World`. Используя параметры, мы можем добавить дополнительную функциональность, которая сделает код более гибким. * Параметры * являются входными данными, которые передаются в функции как имена и ведут себя как локальные переменные.

Когда пользователь входит в приложение, мы можем захотеть, чтобы программа приветствовала его по имени, вместо того, чтобы просто сказать «Привет, мир!».

Мы добавим в нашу функцию параметр, называемый + name +, для представления имени встречаемого человека.

// Initialize custom greeting function
function greet(name) {
   console.log(`Hello, ${name}!`);
}

Имя функции – + greet +, и теперь у нас есть один параметр в скобках. Имя параметра следует тем же правилам, что и имя переменной. Внутри функции вместо статической строки, состоящей из + Hello, World +, мы имеем https://www.digitalocean.com/community/tutorials/how-to-work-with-strings-in-javascript# переменные-в-строках-с-шаблоном-литералами [template literal] строка, содержащая наш параметр, который теперь ведет себя как локальная переменная.

Вы заметите, что мы нигде не определили наш параметр + name +. Мы присваиваем ему значение при вызове нашей функции. Предполагая, что нашего пользователя зовут Сэмми, мы вызовем функцию и поместим имя пользователя в качестве * аргумента *. Аргумент является фактическим значением, которое передается в функцию, в данном случае это строка " Sammy ".

// Invoke greet function with "Sammy" as the argument
greet("Sammy");

Значение " Sammy " передается в функцию через параметр + name +. Теперь каждый раз, когда + name + используется во всей функции, она будет представлять значение " Sammy ". Вот весь код.

greetSammy.js

// Initialize custom greeting function
function greet(name) {
   console.log(`Hello, ${name}!`);
}

// Invoke greet function with "Sammy" as the argument
greet("Sammy");

Когда мы запустим программу выше, мы получим следующий вывод.

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

В дополнение к параметрам переменные могут быть объявлены внутри функций. Эти переменные известны как * локальные переменные * и будут существовать только внутри scope их собственного функционального блока. Область действия переменной определяет доступность переменных; Переменные, которые определены внутри функции, не доступны извне функции, но они могут использоваться столько раз, сколько их функция используется в программе.

Возвращаемые значения

В функции можно использовать более одного параметра. Мы можем передать несколько значений в функцию и вернуть значение. Мы создадим функцию, чтобы найти сумму двух значений, представленных + x + и + y +.

sum.js

// Initialize add function
function add(x, y) {
   return x + y;
}

// Invoke function to find the sum
add(9, 7);

В приведенной выше программе мы определили функцию с параметрами + x + и + y +, а затем передали значения + 9 + и + 7 + в функцию. Когда мы запустим программу, мы получим сумму этих чисел в качестве выходных данных.

В этом случае, когда + 9 + и + 7 + переданы в функцию + sum () +, программа вернула + 16 +.

Когда используется ключевое слово + return +, функция перестает выполняться и возвращается значение выражения. Хотя в этом случае браузер будет отображать значение в консоли, это не то же самое, что использование + console.log () + для печати в консоли. Вызов функции выведет значение именно там, где была вызвана функция. Это значение может быть использовано немедленно или помещено в переменную.

Функциональные выражения

В последнем разделе мы использовали объявление функции, чтобы получить сумму двух чисел и вернуть это значение. Мы также можем создать * https: //developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/function [выражение функции] *, назначив функцию переменной.

Используя тот же пример функции + add +, мы можем напрямую применить возвращаемое значение к переменной, в этом случае + sum +.

functionExpression.js

// Assign add function to sum constant
const sum = function add(x, y) {
   return x + y;
}

// Invoke function to find the sum
sum(20, 5);

Теперь константа + sum + является функцией. Мы можем сделать это выражение более кратким, превратив его в * анонимную функцию *, которая является неназванной функцией. В настоящее время наша функция имеет имя + add +, но с помощью выражений функций нет необходимости называть функцию, и имя обычно опускается.

anonymousExpression.js

// Assign function to sum constant
const sum = function(x, y) {
   return x + y;
}

// Invoke function to find the sum
sum(100, 3);

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

Функции стрелок

До сих пор мы рассмотрели, как определять функции, используя ключевое слово + function. Однако есть более новый, более краткий метод определения функции, известный как * выражения функций стрелок * по состоянию на ECMAScript 6. Функции стрелок, как их обычно называют, представлены знаком равенства, за которым следует знак больше, чем: + ⇒ +.

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

arrow Function.js

// Define multiply function
const multiply = (x, y) => {
   return x * y;
}

// Invoke function to find product
multiply(30, 4);

Вместо того, чтобы писать ключевое слово + function, мы используем стрелку` + ⇒ + `для обозначения функции. В противном случае он работает аналогично регулярному выражению функции, с некоторыми дополнительными отличиями, о которых вы можете прочитать в https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Functions#Arrow_functions[Arrow. Функции на Сеть разработчиков Mozilla.

В случае только одного параметра круглые скобки могут быть исключены. В этом примере мы возводим в квадрат + x +, для которого в качестве аргумента необходимо передать только одно число. Скобки были опущены.

// Define square function
const square = x => {
   return x * x;
}

// Invoke function to find product
square(8);

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

// Define square function
const square = x => x * x;

// Invoke function to find product
square(10);

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

Заключение

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

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

Добавить комментарий