Роботландский Университет © А.А.Дуванов

урок 5: функции
дополнительный материал

формальные и фактические аргументы функции

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

Можно считать описание функции:

function Sum(a, b)
{
  return a+b;
}

— “документацией” на устройство этой функции. Аргументы a и b здесь формальные.

Когда записывается вызов функции x = Sum(10,20), то задаются фактические аргументы (в данном случае — константы 10 и 20).

Что происходит дальше? Браузер обращается к “документации” на функцию Sum и начинает выполнение. Локальным (для функции) переменным a и b (формальным аргументам) браузер присваивает значения 10 и 20. Затем вычисляет сумму и возвращает число 30.

сколько return может иметь функция

Команда return прерывает выполнение кода функции немедленно. Пусть, например, функция описана так:

function F(a)
{
  return 0;
  if(a<0) return -a;
}

Вызов F(x) при любом значении x всегда вернет число 0.

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

// Функция возвращает модуль числа a.
function abs(a)
{
  if(a<0) return -a;
  else    return a;
}

Эту функцию лучше записать так:

// Функция возвращает модуль числа a.
function abs(a)
{
  var ret = a;
  if(ret<0) ret = -ret;
  return ret;
}

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

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

Функцию abs можно записать и так:

// Функция возвращает модуль числа a.
function abs(a)
{
  if(a<0) a = -a;
  return a;
}

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

var x = -2;
var y = abs(x);

Выполнение по шагам:

  1. В переменную x помещается число -2.
  2. При входе в функцию abs браузер создает локальную (для функции) переменную a и копирует в эту переменную значение x, то есть a получает значение -2.
  3. После выполнения условной команды переменная a получает значение 2.
  4. Браузер записывает результат функции — число 2 и уничтожает переменную a.
  5. Переменная y получает значение 2 (результат работы функции), а переменная x остается по-прежнему равной -2.

На самом деле, самая красивая (и понятная!) запись для функции abs выглядит так:

// Функция возвращает модуль числа a.
function abs(a)
{
  return a > 0 ? a : -a;
}

Необходимости в самодельной функции abs нет. В JavaScript есть встроенный объект Math (смотрите справочник в конце книги). Среди методов этого объекта есть метод abs. Поэтому вычисление модуля x запишется как Math.abs(x).

функции и процедуры (для тех, кто работает на Паскале)

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

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

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

function f(x)
{
  return ++x;
}
var y = f(2) + 10;

В этом примере браузер вычислит f(2) и полученное значение 3 подставит вместо f(2) в выражение f(2) + 10. Таким образом, переменная y получит значение 13.

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

function g(x)
{
  alert(x);
}
var str="Чем мучиться и не жить, лучше жить и не мучиться!";
g(str);

А вот если функция возвращает значение, то ее можно использовать и как процедуру Паскаля. Вычисленное значение при этом просто теряется:

function w(x,str)
{
  alert(str);
  return ++x;
}

В этом коде:

var str = "Любил я женщин и проказы.";
var y = w(2,str);

на экран выводится сообщение, и переменная у получает значение 3. А в этом коде:

var str = "Наш Федя с детства связан был с землею.";
w(2,str);

на экран выводится сообщение, а вычисленное значение 3 просто теряется. Попробуйте предсказать, что будет выведено на экран в результате работы такого кода:

function f(x)
{
  return x++;
}
var y = f(2) + 10;
alert(y);

функции с переменным числом аргументов

Это очень полезная возможность JavaScript. Аргументы, которые передаются в функцию, браузер хранит в виде глобального массива имя_функции.arguments. Первый аргумент, как водится в массивах JavaScript, имеет индекс 0. Число аргументов хранится в свойстве length. Обратиться к нему можно так:

имя_функции.arguments.length

Внутри функции работают более простые обращения:

arguments[0] Первый аргумент функции.
arguments.length Число аргументов, переданных в функцию.

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

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

function sum()
{
  var s = 0;
  for(var i=arguments.length; --i>=0;)
    s += arguments[i];
  return s;
}
var x, y, z, t;
x = sum();      // x получит значение 0.
y = sum(1);     // y получит значение 1.
z = sum(1,2);   // z получит значение 3.
t = sum(1,2,3); // t получит значение 6.

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

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

// Функция выполняет заданную операцию op
// над своими аргументами.
function calc(op, arg1, arg2)
{
  var str = arg1+op+arg2;
  var len = arguments.length;
  for(var i=3; i<len; i++)
    str += op+arguments[i];
  return eval(str);
}
var x, y, z, t;
x = calc("+", 1);       // Ошибка.
x = calc("+", 1, 2);    // x получит значение 3.
y = calc("*",2,3,4);    // y получит значение 24.
z = calc("-",10,5,3,1); // z получит значение 1.
t = calc("/",100,5,4);  // t получит значение 5.

Замечание. Стандартная функция eval(str) рассматривает свой аргумент str как выражение JavaScript, вычисляет его и возвращает полученное значение.

Оборонительное программирование

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

Лу Гринзоу, “Философия программирования для Windows 95/NT”

Данные

Если код работает с данными, которые поступают в него извне (например, их вводит пользователь, как в программе Угадайка), то он не должен предполагать, что полученные данные допустимы.

Оборонительный код должен проверить данные перед их “впрыском” в логику обработки. Проверить и принять адекватные меры: исправить ошибки, если это возможно, или отказать в обработке, выдав диагностику, оговоренную в документации.

Функции

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

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

Проекты

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

Месяц — это предельный срок, в течение которого программист еще помнит, как работает его программа.

 

урок 2 содержание письмо автору об авторах