В JavaScript функция тоже является объектом – объектом Function и тоже имеет прототип, свойства, методы, в том числе мощные методы call() и apply(). Эти методы позволяют вызывать функцию так, будто она является методом некоторого объекта. Первый аргумент методов call() и apply() – это объект, для которого выполняется функция. Этот аргумент становится значением ключевого слова this в теле функции.

Метод call() вызывает функцию с указанным значением this и индивидуально предоставленными аргументами. Первый аргумент служит контекстом вызо­ва и становится значением ключевого слова this в теле функции. Все оставшиеся аргументы call() – это значения, передаваемые вызываемой функции.

Синтаксис метода call:
func.call(context, arg1, arg2, ...)

Начнем со следующего кода:

Выполнить код »

Первый вызов fun() отобразит значение 10, так как this ссылается на глобальный объект Window. Второй вызов (через метод call ), отобразит значение 15. 15 – это значение свойства x внутри объекта obj. Здесь метод call() используется для вызова fun() (метода) от имени объекта obj.

После контекста в методе call можно передать аргументы для вызываемой функции:

Выполнить код »

При помощи call вы можете использовать метод, принадлежащий одному объекту, а вызвать его в контексте другого:

Выполнить код »

Метод apply() идентичен call(), за исключением того, что apply() требует, чтобы в качестве второго параметра был выбран массив (либо массивоподобный объект). Массив представляет аргументы для целевого метода.

func.call(context, arg1, arg2);
// идентичен вызову
func.apply(context, [arg1, arg2]);
// или
func.apply(context, new Array(arg1, arg2));

Метод apply() полезен, если у вас есть массив и вы хотите использовать его зна­чения как аргументы функции, для которых в противном случае пришлось бы писать цикл по массиву значений. Классический пример – поиск минимального или максимального числа в массиве. Вспомогательным функциям Math.max()/Math.min() можно передать любое количество аргументов, а они возвращают минимальное или максимальное значение, соответственно. Мы можем использовать метод apply(), что­бы вызвать эти функции с существующим массивом:

Выполнить код »

Обратите внимание, что вместо значения this мы просто передаем null. В данном случае в качестве контекста можно передавать что угодно, поскольку в своей внутренней реализации методы Math.max и не Math.min использует this вообще.

Обратите внимание, что this в методах call() и apply() может не быть реальным значением, видимым этим методом. Если метод является функцией в нестрогом режиме (без use strict), значения null и undefined будут заменены глобальным объектом (this = window), а примитивные значения будут упакованы в объекты:

Выполнить код »

В качестве второго параметра метода apply() вы также можете использовать псевдомассив arguments. Каждая функция имеет специальную локальную переменную arguments, доступную внутри её области применения. Псевдомассив arguments может использоваться для всех неопределённых аргументов вызываемого объекта. Вы можете даже не знать, сколько и какие аргументы будет требать вызываемый объект при использовании метода apply(). Таким образом, вы можете использовать arguments для передачи всех аргументов в вызываемый объект. Мнтерпретатор JavaScript самостоятельно разберётся с обработкой аргументов.

Чтобы исследовать свойства arguments, давайте создадим тестирующую функцию:

Выполнить код »

Здесь свойство callee псевдомассива arguments хранит ссылку на функцию-родитель.

Несмотря на то, что arguments выглядит как массив, но это всё же объект. Однако во многих случаях хотелось бы манипулировать им, как если бы это был массив. Чтобы превратить arguments в массив, воспользуемся следующим приёмом: возьмём метод массива slice.

Вызов arr.slice(start, end) возвращает новый массив, содержащий копию части исходного массива. start – индекс элемента в массиве arr, с которого будет начинаться новый массив; end – индекс элемента в массиве arr, на котором новый массив завершится. А если start и end не указаны, то копирует весь массив.

Вызовем его в контексте arguments:
Выполнить код »

Здесь вызов Array.prototype.slice() скопирует все элементы из this (arguments) в новый массив.

Стоит отметить, что вместо Array.prototype вы можете задать пустой массив как [] или new Array(): [].slice.call(arguments)|new Array().slice.call(arguments).

  • Методы call и apply позволяют вызывать функцию так, будто она является методом некоторого объекта.
  • Первый аргумент передоваемый методами call и apply служит контекстом вызо­ва и становится значением ключевого слова this в теле функции.
  • После контекста (this) в методе call можно передать аргументы для вызываемой функции.
  • При помощи call и apply вы можете использовать метод, принадлежащий одному объекту, а вызвать его в контексте другого.
  • Метод apply идентичен call, за исключением того, что apply требует, чтобы в качестве второго параметра был выбран массив (либо массивоподобный объект). Массив представляет аргументы для целевого метода.

  • Счетчик превысил заданное число

    Напишите функцию getMax(fn, num), которая принимает функцию и число num. Функция getMax должна возвращать функцию, которая при каждом вызове увеличивает свой внутренний счетчик (counter++). Если счетчик больше числа num, внутренняя функция должна возвращать строку «Максимум!»,

    function add(a,b){
            return a+b
        }
    
    function getMax(fn, num) {
    
    /*     ваш код     */
    
    }
    
    var addOnlyThreeTimes = getMax(add, 3);
    addOnlyThreeTimes(1,2) // 3
    addOnlyThreeTimes(2,2) // 4
    addOnlyThreeTimes(1,2) // 3
    addOnlyThreeTimes(1,2) // "Максимум!"
    
    Показать решение

    Решение:

    Выполнить код »
  • Вывести сумму четных чисел

    Напишите функцию sumEvenArguments, которая принимает все аргументы, переданные ей при вызове, и возвращает сумму четных чисел (из числа аргументов).

    sumEvenArguments(1,2,3,6) // 8
        sumEvenArguments(1,12,6) // 18
        sumEvenArguments(1,2) // 2
    
    // ваш код
    
    Показать решение
    Выполнить код »

Комментарии

пожелания к комментариям…
  • Приветствуются комментарии, соответствующие теме урока: вопросы, ответы, предложения.
  • Одну строчку кода оборачивайте в тег <code>, несколько строчек кода — в теги <pre><code>...ваш код...</code></pre>.
  • Допускаются ссылки на онлайн-песочницы (codepen, plnkr, JSBin и др.).