Call: примеры (JAVASCRIPT)

Использование метода call в JavaScript: руководство с примерами
Раздел: Функции, Контекст выполнения
call(thisArg, ...args): any

Основные сведения о методе call

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

Когда используется:

  • Когда необходимо явно задать значение this внутри функции.
  • Для заимствования методов у других объектов.
  • Для вызова функций, которые могут быть определены в разных контекстах.
  • В ситуациях, где требуется немедленный вызов функции с конкретным контекстом.

Аргументы:

  1. thisArg (обязательный): Значение, которое будет использоваться как this при вызове функции. Если передано null или undefined, в нестрогом режиме контекстом становится глобальный объект (например, window). В строгом режиме ('use strict') значение передается как есть.
  2. arg1, arg2, ..., argN (необязательные): Аргументы, которые будут переданы вызываемой функции. Их количество не ограничено.

Возвращаемое значение:

Метод возвращает результат выполнения вызванной функции. Если функция не возвращает значение явно, будет возвращено undefined.

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

Пример 1: Изменение контекста для метода объекта.

const person1 = { name: 'Анна' };
const person2 = { name: 'Иван' };

function greet(greeting) {
  console.log(`${greeting}, ${this.name}!`);
}

greet.call(person1, 'Привет');
greet.call(person2, 'Здравствуй');
Привет, Анна!
Здравствуй, Иван!

Пример 2: Вызов функции без передачи конкретного объекта (нестрогий режим).

function showContext() {
  console.log(this);
}

// В браузере
showContext.call(null); // Выведет объект Window (или global в Node.js)
Window { ... }

Пример 3: Использование в строгом режиме.

'use strict';
function showStrictContext() {
  console.log(this);
}

showStrictContext.call(null); // null
showStrictContext.call(undefined); // undefined
null
undefined

Похожие методы в JavaScript

Function.prototype.apply(): Очень похож на call, но принимает аргументы для вызываемой функции в виде массива (или массивоподобного объекта). Удобен, когда аргументы динамически формируются в коллекцию.

function sum(a, b, c) { return a + b + c; }
const numbers = [1, 2, 3];
console.log(sum.apply(null, numbers));
6

Function.prototype.bind(): Создает новую функцию, которая при вызове имеет предустановленное значение this и, опционально, начальные аргументы. В отличие от call и apply, не вызывает функцию немедленно, а возвращает новую функцию для последующего вызова.

const boundGreet = greet.bind(person1, 'Привет');
boundGreet(); // Вызов позже
Привет, Анна!

Выбор метода:

  • Используют call, когда аргументы известны и передаются по отдельности.
  • Выбирают apply, если аргументы находятся в массиве.
  • Применяют bind, когда требуется отложенный вызов функции с фиксированным контекстом.

Аналоги в других языках программирования

Python: Прямого аналога нет, так как self передается явно как первый параметр метода. Для вызова метода одного объекта с контекстом другого можно использовать types.MethodType или просто присвоить функцию как атрибут.

class Person:
    def __init__(self, name):
        self.name = name

def greet(self, greeting):
    print(f'{greeting}, {self.name}!')

person1 = Person('Анна')
greet(person1, 'Привет')  # Явная передача 'self'
Привет, Анна!

PHP: Используют call_user_func() в сочетании с замыканиями (Closure::bind()) или call_user_func_array().

$greet = function($greeting) {
    echo $greeting . ', ' . $this->name . '!';
};

$person1 = (object)['name' => 'Анна'];
$boundGreet = Closure::bind($greet, $person1);
$boundGreet('Привет');
Привет, Анна!

C#: Прямого аналога для произвольной функции нет, но для делегатов можно использовать замыкания или явно передавать объект контекста как параметр.

Основное отличие JavaScript в том, что метод call является частью прототипа любой функции, обеспечивая единый и гибкий механизм управления контекстом.

Частые ошибки

Ошибка 1: Потеря контекста при передаче метода как коллбэка.

const obj = {
  value: 10,
  getValue: function() { console.log(this.value); }
};

setTimeout(obj.getValue, 100); // Контекст потеряется
// Решение:
setTimeout(obj.getValue.bind(obj), 100);
// Или с call, обернув в функцию:
setTimeout(() => obj.getValue.call(obj), 100);
undefined // (или ошибка в strict mode)
10 // после исправления

Ошибка 2: Неверное значение thisArg в строгом режиме.

'use strict';
function func() { console.log(this); }
func.call(undefined); // Будет undefined, а не глобальный объект
undefined

Ошибка 3: Использование call с примитивами. Примитивы оборачиваются в объекты.

function checkType() { console.log(typeof this); }
checkType.call(42); // 'object' (Number) в нестрогом режиме
checkType.call('text'); // 'object' (String)
object
object

История изменений

Метод Function.prototype.call был стандартизирован в ECMAScript 3 (1999) и с тех пор его поведение остается стабильным. В современных стандартах (ES5, ES6+) не было внесено значительных изменений в его работу. Единственное уточнение касается строгого режима ('use strict'), введенного в ES5, где значение thisArg не преобразуется в объект, если переданы null или undefined.

Расширенные примеры

Пример 1: Заимствование методов для массивоподобных объектов.

Пример javascript
const arrayLike = { 0: 'a', 1: 'b', length: 2 };
// Попытка использовать метод массива напрямую не сработает:
// arrayLike.forEach(...) // Ошибка
// Решение с call:
Array.prototype.forEach.call(arrayLike, item => console.log(item));
a
b

Пример 2: Цепочка вызовов конструкторов (наследование).

Пример javascript
function Product(name, price) {
  this.name = name;
  this.price = price;
}

function Food(name, price, category) {
  Product.call(this, name, price); // Вызов родительского конструктора
  this.category = category;
}

const bread = new Food('Хлеб', 30, 'Выпечка');
console.log(bread);
Food { name: 'Хлеб', price: 30, category: 'Выпечка' }

Пример 3: Оптимизация производительности с использованием call для вызова встроенных методов.

Пример javascript
const arr = [5, 1, 9];
// Вместо Math.max.apply(null, arr):
const max = Math.max.call(null, ...arr); // С spread оператором
console.log(max);
9

Пример 4: Динамический выбор контекста для функций высшего порядка.

Пример javascript
function mapWithContext(arr, fn, context) {
  const result = [];
  for (let i = 0; i < arr.length; i++) {
    result.push(fn.call(context, arr[i], i, arr));
  }
  return result;
}

const doubler = { factor: 2, multiply(x) { return x * this.factor; } };
console.log(mapWithContext([1, 2, 3], doubler.multiply, doubler));
[2, 4, 6]

Пример 5: Эмуляция bind с использованием call.

Пример javascript
function customBind(fn, context, ...boundArgs) {
  return function(...args) {
    return fn.call(context, ...boundArgs, ...args);
  };
}

const boundFunc = customBind(greet, person1, 'Привет');
boundFunc();
Привет, Анна!

JS call function comments

En
Call Calls a function with a given `this` value and arguments provided individually