Поскольку JavaScript является слабо типизированным языком, аргументы ме_ тоды объявляются без указания их типов, а во время передачи значений функ_
8.2. Аргументы функций
ции не производится никакой проверки их типов. Вы можете сделать свой про_ граммный код самодокументируемым, выбирая описательные имена для аргу_ ментов функций и включая указание на тип в комментарии, как это сделано в только что рассмотренном примере с функцией arraycopy(). Для необязатель_ ных аргументов можно добавлять в комментарий слово «необязательный» («op_ tional»). А если метод предусматривает возможность принимать произвольное число аргументов, можно использовать многоточие:
function max(/* число... */) { /* тело функции */ }
Как отмечалось в главе 3, в случае необходимости JavaScript выполняет преоб_ разование типов. Таким образом, если вы создали функцию, которая ожидает получить строковый аргумент, а затем вызываете ее с аргументом какого_нибудь другого типа, значение аргумента просто преобразуется в строку, когда функция пытается обратиться к нему как к строке. В строку может быть преобразован лю_ бой элементарный тип, и все объекты имеют методы toString() (правда, не всегда полезные), тем самым устраняется вероятность появления ошибки.
Однако такой подход может использоваться не всегда. Рассмотрим еще раз ме_ тод arraycopy(), продемонстрированный ранее. Он ожидает получить массив в первом аргументе. Любое обращение к функции окажется неудачным, если первым аргументом будет не массив (или, возможно, объект, подобный масси_ ву). Если функция должна вызываться чаще, чем один_два раза, следует доба_ вить в нее проверку соответствия типов аргументов. При передаче аргументов ошибочных типов должно генерироваться исключение, которое зафиксирует этот факт. Гораздо лучше сразу же прервать вызов функции в случае передачи аргументов ошибочных типов, чем продолжать исполнение, которое потерпит неудачу, когда, например, функция попытается получить доступ к элементу массива с помощью числового аргумента, как в следующем фрагменте:
// Возвращает сумму элементов массива (или объекта, подобного массиву) a.
// Все элементы массива должны быть числовыми, при этом значения null
// и undefined игнорируются.
function sum(a) {
if ((a instanceof Array) || // если это массив
(a && typeof a == "object" && "length" in a)) { // или объект, подобный массиву var total = 0;
for(var i = 0; i < a.length; i++) { var element = a[i];
if (!element) continue; // игнорировать значения null и undefined if (typeof element == "number") total += element;
else throw new Error("sum(): все элементы должны быть числами");
}
return total;
}
else throw new Error("sum(): аргумент должен быть массивом");
}
Метод sum() весьма строго относится к проверке типов входных аргументов и ге_ нерирует исключения с достаточно информативными сообщениями, если типы входных аргументов не соответствуют ожидаемым. Тем не менее он остается дос_ таточно гибким, обслуживая наряду с настоящими массивами объекты, подоб_ ные массивам, и игнорируя элементы, имеющие значения null и undefined.
148 Глава 8. Функции
JavaScript – чрезвычайно гибкий и к тому же слабо типизированный язык, бла_ годаря чему можно писать функции, которые достаточно терпимо относятся к количеству и типам входных аргументов. Далее приводится метод flexsum(), реализующий такой подход (и, вероятно, являющийся примером другой край_ ности). Например, он принимает любое число входных аргументов и рекурсивно обрабатывает те из них, которые являются массивами. Вследствие этого он мо_ жет принимать переменное число аргументов или массив в виде аргумента. Кро_ ме того, он прилагает максимум усилий, чтобы преобразовать нечисловые аргу_ менты в числа, прежде чем сгенерировать исключение:
function flexisum(a) {
var total = 0;
for(var
i = 0; i < arguments.length; i++) {
var
element = arguments[i];
if (!element) continue; // Игнорировать значения null и undefined
// Попытаться преобразовать аргумент в число n исходя из типа аргумента
var
n;
switch(typeof element) {
case "number":
n = element;
// Преобразование не требуется
break;
case "object":
if (element instanceof Array) // Рекурсивный обход массива
n = flexisum.apply(this, element);
else n = element.valueOf( );
// Для других объектов вызвать valueOf
break;
case "function":
n = element( );
// Попытаться вызвать функцию
break;
case "string":
n = parseFloat(element);
// Попытаться преобразовать строку
break;
case "boolean":
n = NaN; // Логические значения преобразовать невозможно
break;
}
// Если было получено нормально число – добавить его к сумме. if (typeof n == "number" && !isNaN(n)) total += n;
// В противном случае сгенерировать исключение else
throw new Error("sum(): ошибка преобразования " + element + " в число");