Роботландский Университет © А.А.Дуванов |
Часто начинающие программисты испытывают трудности в понимании различия между формальными и фактическими аргументами функции.
Можно считать описание функции:
function Sum(a, b) { return a+b; }
документацией на устройство этой функции. Аргументы a и b здесь формальные.
Когда записывается вызов функции
Что происходит дальше? Браузер обращается к документации на функцию Sum и начинает выполнение. Локальным (для функции) переменным a и b (формальным аргументам) браузер присваивает значения 10 и 20. Затем вычисляет сумму и возвращает число 30.
Команда 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);
Выполнение по шагам:
На самом деле, самая красивая (и понятная!) запись для функции 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) в выражение
Функция может не возвращать значения, и тогда ее нельзя использовать в выражениях, она, подобно процедуре Паскаля, должна стоять в коде отдельной командой:
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, вычисляет его и возвращает полученное значение.
Если код работает с данными, которые поступают в него извне (например, их вводит пользователь, как в программе Угадайка), то он не должен предполагать, что полученные данные допустимы.
Оборонительный код должен проверить данные перед их впрыском в логику обработки. Проверить и принять адекватные меры: исправить ошибки, если это возможно, или отказать в обработке, выдав диагностику, оговоренную в документации.
Когда код передает управление другому, внешнему коду (вызывает стандартную, библиотечную функцию или функцию, написанную вашим партнером), вы не можете быть уверены, что результат обработки будет именно таким, как это описано в сопроводительной документации или зафиксировано устно в момент обсуждения проекта на кухне за чашкой чая.
В какой-то степени уберечь свой код от краха, следующего за вызовом чужих функций, можно путем тщательного тестирования того, что вы используете.
Ваш собственный код должен быть хорошо спроектирован, документирован и отлажен. Если программные комментарии и внешние описания не соответствуют тому, что делает код на самом деле, то это ловушка, в которую первым обычно попадается сам автор, решивший через какое-то время изменить алгоритм работы кода.
Месяц это предельный срок, в течение которого программист еще помнит, как работает его программа.
![]() |