← Назад к вопросам

Как apply принимает аргументы?

2.3 Middle🔥 141 комментариев
#JavaScript Core

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI2 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

apply(): Способы передачи аргументов

apply() — это метод функции в JavaScript, который вызывает функцию с явно указанным контекстом (this) и аргументами в виде массива или array-like объекта.

Синтаксис

function.apply(thisArg, [argsArray])

Параметры:

  • thisArg — значение, которое будет использовано как this при вызове функции
  • argsArray — массив (или array-like объект) с аргументами для функции

Как apply принимает аргументы

1. Массив как второй параметр

function greet(greeting, punctuation) {
  return greeting + ' ' + this.name + punctuation;
}

const person = { name: 'John' };

// apply принимает массив аргументов
const result = greet.apply(person, ['Hello', '!']);
console.log(result); // "Hello John!"

// Эквивалентно:
greet.call(person, 'Hello', '!');

2. Array-like объект

const arrayLike = {
  0: 'Hello',
  1: '!',
  length: 2
};

const result = greet.apply(person, arrayLike);
console.log(result); // "Hello John!"

3. null или undefined как первый параметр

function sum(a, b) {
  console.log(this); // Window (в браузере) или undefined (в строгом режиме)
  return a + b;
}

const result = sum.apply(null, [5, 3]);
console.log(result); // 8

4. arguments объект (старый подход)

function wrapper() {
  console.log(arguments); // Arguments { 0: 1, 1: 2, 2: 3 }
  
  // apply принимает arguments как массив
  // arguments — это array-like, а не настоящий массив
  Math.max.apply(null, arguments); // Ищет максимум
}

wrapper(1, 2, 3);

Примеры использования

Пример 1: Поиск максимума/минимума

const numbers = [5, 6, 2, 3, 7];

// Math.max / Math.min не принимают массив
// Math.max(numbers) вернёт NaN

// apply распаковывает массив в отдельные аргументы
const max = Math.max.apply(null, numbers);
const min = Math.min.apply(null, numbers);

console.log(max); // 7
console.log(min); // 2

// Современный способ (ES6+):
const max2 = Math.max(...numbers); // Spread operator
const min2 = Math.min(...numbers);

Пример 2: Вызов конструктора с массивом аргументов

function Person(firstName, lastName, age) {
  this.firstName = firstName;
  this.lastName = lastName;
  this.age = age;
}

const args = ['John', 'Doe', 30];

// apply не работает с конструкторами!
// const person = Person.apply({}, args); // Неправильно

// Нужно использовать new или new.target
const person = new (Function.prototype.bind.apply(Person, [null, ...args]))();
// Или проще в ES6:
const person2 = new Person(...args);

Пример 3: Заимствование методов у других объектов

const arrayLike = {
  0: 'a',
  1: 'b',
  2: 'c',
  length: 3
};

// apply позволяет использовать методы Array на array-like объектах
const result = Array.prototype.slice.apply(arrayLike);
console.log(result); // ['a', 'b', 'c']

// Эквивалентно:
const result2 = Array.from(arrayLike);
const result3 = [...arrayLike]; // Не работает, т.к. не итерируемый

Пример 4: Логирование с переменным числом аргументов

const logger = {
  log: function(level, ...args) {
    console.log(`[${level}]`, ...args);
  }
};

function callLogger() {
  // apply передаёт все аргументы из массива
  logger.log.apply(logger, ['INFO', 'User logged in', { userId: 123 }]);
}

callLogger();
// Вывод: [INFO] User logged in { userId: 123 }

Сравнение: apply, call, bind

function introduce(greeting, punctuation) {
  return `${greeting}, I'm ${this.name}${punctuation}`;
}

const person = { name: 'Alice' };

// call — аргументы через запятую
const result1 = introduce.call(person, 'Hi', '!');
console.log(result1); // Hi, I'm Alice!

// apply — аргументы в массиве
const result2 = introduce.apply(person, ['Hi', '!']);
console.log(result2); // Hi, I'm Alice!

// bind — возвращает новую функцию
const boundFunc = introduce.bind(person, 'Hi', '!');
const result3 = boundFunc();
console.log(result3); // Hi, I'm Alice!

Частые ошибки при работе с apply

Ошибка 1: Забыли передать массив

// Плохо
const result = Math.max.apply(null, 5, 3, 2); // Второй параметр должен быть массивом!
// Это не будет работать как ожидается

// Правильно
const result = Math.max.apply(null, [5, 3, 2]);

Ошибка 2: Передали не array-like объект

const obj = { a: 1, b: 2 }; // Обычный объект, не array-like

// Плохо
Array.prototype.slice.apply(obj); // Вернёт пустой массив

// Нужен length
const arrayLike = { 0: 'a', 1: 'b', length: 2 };
Array.prototype.slice.apply(arrayLike); // ['a', 'b']

Ошибка 3: Забыли про контекст (this)

const obj = {
  name: 'Object',
  greet: function() {
    console.log('Hello, ' + this.name);
  }
};

// Плохо — this будет undefined или window
obj.greet.apply(null, []);

// Правильно
obj.greet.apply(obj, []);
// Или
obj.greet();

Производительность

const numbers = Array.from({ length: 100000 }, (_, i) => i);

// Способ 1: apply (старый)
console.time('apply');
Math.max.apply(null, numbers);
console.timeEnd('apply'); // ~5ms

// Способ 2: spread (новый)
console.time('spread');
Math.max(...numbers);
console.timeEnd('spread'); // ~5ms

Оба способа примерно одинаковые по производительности в современных браузерах.

Реальные примеры использования

Пример 1: Утилита для запуска функции с контекстом

function executeWithContext(
  func: Function,
  context: any,
  args: any[] = []
) {
  return func.apply(context, args);
}

const user = { name: 'Bob' };
const greet = (greeting: string) => `${greeting}, ${this.name}`;

const result = executeWithContext(greet, user, ['Hello']);
console.log(result); // Hello, Bob

Пример 2: Наследование в ES5

function Animal(name) {
  this.name = name;
}

function Dog(name, breed) {
  Animal.apply(this, [name]); // Вызываем родительский конструктор
  this.breed = breed;
}

const dog = new Dog('Max', 'Labrador');
console.log(dog); // { name: 'Max', breed: 'Labrador' }

Пример 3: Декоратор с apply

function withLogging(fn: Function, context: any) {
  return function(...args: any[]) {
    console.log(`Calling ${fn.name} with args:`, args);
    return fn.apply(context, args);
  };
}

const add = (a: number, b: number) => a + b;
const loggedAdd = withLogging(add, null);

loggedAdd(5, 3); // Calling add with args: [5, 3] -> 8

Современные альтернативы

В ES6+ часто используется spread operator вместо apply:

// Старый способ
Math.max.apply(null, numbers);

// Новый способ
Math.max(...numbers);

// call
func.call(obj, a, b);
// можно заменить
func.apply(obj, [a, b]);
// или
func.bind(obj, a, b)();

Заключение

apply() принимает аргументы следующим образом:

  1. Первый параметр — контекст (this)
  2. Второй параметр — массив или array-like объект с аргументами
  3. Аргументы распаковываются — apply превращает массив в отдельные параметры

apply() полезен для:

  • Вызова функций с переменным числом аргументов
  • Заимствования методов
  • Установки контекста (this)
  • Работы с array-like объектами

В современном JavaScript часто используется spread operator (...) как альтернатива apply, так как это более удобно и читаемо.

Как apply принимает аргументы? | PrepBro