Самые важные особенности функций заключаются в том, что они могут опреде_ ляться и вызываться, что было показано в предыдущем разделе. Определение и вызов функций – это синтаксические средства JavaScript и большинства дру_ гих языков программирования. Однако в JavaScript функции – это не только
8.3. Функции как данные
синтаксические конструкции, но и данные, а это означает, что они могут при_ сваиваться переменным, храниться в свойствах объектов или элементах масси_ вов, передаваться как аргументы функциями и т. д.1
Чтобы понять, как в JavaScript функции могут быть одновременно синтаксиче_ скими конструкциями и данными, рассмотрим следующее определение функции:
function square(x) { return x*x; }
Это определение создает новый объект функции и присваивает его переменной square. Имя функции действительно нематериально – это просто имя перемен_ ной, содержащей функцию. Функция может быть присвоена другой перемен_ ной, и при этом работать так же, как и раньше:
var a = square(4); // a содержит
число
var
b
=
square;
//
b
теперь ссылается на ту же функцию, что и square
var
c
=
b(5);
//
c
содержит
число
Функции могут быть также присвоены не только глобальным переменным, но и свойствам объектов. В этом случае их называют методами:
var o = new Object;
o.square = function(x) { return x*x; }; // функциональный литерал y = o.square(16); // y равно 256
У функций даже не обязательно должны быть имена, например в случае при_ сваивании их элементам массива:
var a = new Array(3);
a[0] = function(x) { return x*x; } a[1] = 20;
a[2] = a[0](a[1]); // a[2] содержит 400
Синтаксис вызова функции в последнем примере выглядит необычно, однако это вполне допустимый вариант применения оператора () в JavaScript!
В примере 8.2 подробно показано, что можно делать, когда функции выступают в качестве данных. Этот пример демонстрирует, каким образом функции могут передаваться другим функциям. Хотя пример может показаться вам несколько сложным, комментарии объясняют, что происходит, и он вполне достоин тща_ тельного изучения.
Пример 8.2. Использование функций как данных
// Здесь определяются несколько простых функций function add(x,y) { return x + y; }
function subtract(x,y) { return x _ y; } function multiply(x,y) { return x * y; } function divide(x,y) { return x / y; }
// Эта функция принимает одну из вышеприведенных функций
// в качестве аргумента и вызывает ее для двух операндов
1 Это может показаться не столь интересным, если вы незнакомы с такими языка_ ми, как Java, в которых функции являются частью программы, но не могут про_ граммой управляться.
150 Глава 8. Функции
function operate(operator, operand1, operand2)
{
return operator(operand1, operand2);
}
// Вот так можно вызвать эту функцию для вычисления значения выражения (2+3) + (4*5): var i = operate(add, operate(add, 2, 3), operate(multiply, 4, 5));
// Ради примера, мы реализуем эти функции снова, на этот раз с помощью
// функциональных литералов внутри литерала объекта.
var operators
= {
add:
function(x,y) {
return x+y; },
subtract:
function(x,y) {
return x_y; },
multiply:
function(x,y) {
return x*y; },
divide:
function(x,y) {
return x/y; },
pow:
Math.pow // Для
предопределенных функций это тоже работает
};
// Эта функция принимает имя оператора, отыскивает оператор в объекте,
// а затем вызывает его для переданных операндов. Обратите внимание
// на синтаксис вызова функции оператора.
function operate2(op_name, operand1, operand2)
{
if (typeof
operators[op_name] == "function")
return
operators[op_name](operand1, operand2);
else throw
"неизвестный оператор";
}
// Вот так мы можем вызвать эту функцию для вычисления значения
Если предыдущий пример не убедил вас в удобстве передачи функций в качестве аргументов другим функциям и других способов использования функций как значений, обратите внимание на функцию Array.sort(). Она сортирует элементы массива. Существует много возможных порядков сортировки (числовой, алфа_ витный, по датам, по возрастанию, по убыванию и т. д.), поэтому функция sort() принимает в качестве необязательного аргумента другую функцию, которая со_ общает о том, как выполнять сортировку. Эта функция делает простую работу – получает два элемента массива, сравнивает их, а затем возвращает результат, указывающий, какой из элементов должен быть первым. Этот аргумент функ_ ции делает метод Array.sort() совершенно универсальным и бесконечно гибким – он может сортировать любой тип данных в любом мыслимом порядке! (Пример использования функции Array.sort() вы найдете в разделе 7.7.3.)