В ECMAScript есть два метода, определенные для всех функций, – call() и apply(). Эти методы позволяют вызывать функцию так, будто она является методом не_ которого объекта. Первый аргумент методов call() и apply() – это объект, для которого выполняется функция; этот аргумент становится значением ключевого слова this в теле функции. Все оставшиеся аргументы call() – это значения, пере_ даваемые вызываемой функции. Так, чтобы передать функции f() два числа и вы_ звать ее как метод объекта o, можно использовать следующий прием:
f.call(o, 1, 2);
Это аналогично следующим строкам программы:
154 Глава 8. Функции
o.m = f; o.m(1,2); delete o.m;
Метод apply() похож на метод call(), за исключением того, что передаваемые функции аргументы задаются в виде массива:
f.apply(o, [1,2]);
Например, чтобы найти наибольшее число в массиве чисел, можно вызвать ме_ тод apply() для передачи элементов массива функции Math.max():
var biggest = Math.max.apply(null, array_of_numbers);
Практические примеры функций
В этом разделе приводятся примеры нескольких функций для работы с объекта_ ми и массивами, имеющие практическую ценность. Пример 8.3 содержит функ_ ции для работы с объектами.
Пример 8.3. Функции для работы с объектами
// Возвращает массив, содержащий имена перечислимых свойств объекта "o" function getPropertyNames(/* объект */o) {
var r = [];
for(name in o) r.push(name); return r;
}
// Копирует перечислимые свойства объекта "from" в объект "to".
// Если аргумент "to" равен null, создается новый объект.
// Функция возвращает объект "to" или вновь созданный объект.
function copyProperties(/* объект */ from, /* необязательный объект */ to) { if (!to) to = {};
for(p in from) to[p] = from[p]; return to;
}
// Копирует перечислимые свойства объекта "from" в объект "to",
// но только те, которые еще не определены в объекте "to".
// Это может оказаться необходимым, например, когда объект "from" содержит
// значения по умолчанию, которые необходимо скопировать в свойства,
// если они еще не были определены в объекте "to".
function copyUndefinedProperties(/* объект */ from, /* объект */ to) { for(p in from) {
if (!p in to) to[p] = from[p];
}
}
В следующем примере 8.4 приводятся функции для работы с массивами.
Пример 8.4. Функции для работы с массивами
// Передать каждый элемент массива "a" заданной функции проверки.
// Вернуть массив, хранящий только те элементы, для которых
8.7. Практические примеры функций
// функция проверки вернула значение true
function filterArray(/* массив */ a, /* функция проверки */ predicate) { var results = [];
var length = a.length; // На случай, если функция проверки изменит свойство length! for(var i = 0; i < length; i++) {
var element = a[i];
if (predicate(element)) results.push(element);
}
return results;
}
// Возвращает массив значений, которые являются результатом передачи
// каждого элемента массива функции "f"
function mapArray(/* массив */a, /* функция */ f) { var r = []; // результаты
var length = a.length; // На случай, если f изменит свойство length! for(var i = 0; i < length; i++) r[i] = f(a[i]);
return r;
}
Наконец, функции из примера 8.5 предназначены для работы с функциями. Фактически они используют и возвращают вложенные функции. Вложенные функции возвращаются способом, получившим некогда название «замыкание». Замыкания, которые могут оказаться сложными для понимания, рассматрива_ ются в следующем разделе.
Пример 8.5. Функции для работы с функциями
// Возвращает самостоятельную функцию, которая в свою очередь вызывает
// функцию "f" как метод объекта "o". Эта функция может использоваться,
// когда возникает необходимость передать в функцию метод.
// Если не связать метод с объектом, ассоциация будет утрачена, и метод,
// переданный функции, будет вызван как обычная функция.
function bindMethod(/* объект */ o, /* функция */ f) { return function() { return f.apply(o, arguments) }
}
// Возвращает самостоятельную функцию, которая в свою очередь вызывает
// функцию "f" с заданными аргументами и добавляет дополнительные
// аргументы, передаваемые возвращаемой функции.
// (Этот прием иногда называется "currying".)
function bindArguments(/* функция */ f /*, начальные аргументы... */) { var boundArgs = arguments;
return function() {
// Создать массив аргументов. Он будет начинаться с аргументов,
// определенных ранее, и заканчиваться аргументами, переданными сейчас var args = [];
for(var i = 1; i < boundArgs.length; i++) args.push(boundArgs[i]); for(var i = 0; i < arguments.length; i++) args.push(arguments[i]);
// Теперь вызвать функцию с новым списком аргументов