JavaScript — невероятно гибкий язык, предлагающий множество способов управления контекстом функции. Три из этих методов: bind, call и apply. Поначалу они могут показаться пугающими, но как только вы поймете их функцию и использование, вы обнаружите, что они являются мощными инструментами в вашем арсенале кодирования. В этой статье рассматривается, что делают эти методы, как их использовать и как создавать собственные версии. Мы также дадим несколько заданий, которые помогут закрепить ваше понимание.

Привязать, позвонить и подать заявку: что это такое?

Каждая функция в JavaScript имеет три метода: bind, call и apply. Эти методы позволяют вам установить значение this в функции, которое определяет контекст, в котором выполняется функция.

  • bind: возвращает новую функцию, позволяющую передать массив this и любое количество аргументов. Аргумент this используется в качестве контекста в функции.
  • call: вызывает функцию с заданным значением this и аргументами, предоставленными индивидуально.
  • apply: Аналогичен call, но принимает массив аргументов вместо отдельных.

Связать в действии

bind создает новую функцию с указанным значением this, что позволяет вам управлять контекстом, в котором выполняется функция.

let obj = {
  name: 'John',
};
function greet() {
  console.log(`Hello, my name is ${this.name}`);
}
let boundGreet = greet.bind(obj);
boundGreet();  // "Hello, my name is John"

В данном случае boundGreet — это новая функция с тем же телом, что и у greet, но this всегда связана с obj.

Звоните и оставляйте заявку в действии

Методы call и apply похожи на bind, но с одним существенным отличием: вместо создания новой функции они немедленно вызывают функцию с указанным значением this.

let obj = {
  name: 'John',
};

function greet(greeting, punctuation) {
  console.log(`${greeting}, my name is ${this.name}${punctuation}`);
}
greet.call(obj, 'Hello', '!');  // "Hello, my name is John!"
greet.apply(obj, ['Hello', '!']);  // "Hello, my name is John!"

И в call, и в apply this устанавливается на obj. Разница заключается в том, как передаются дополнительные аргументы: call принимает список аргументов, а apply принимает один массив аргументов.

Создайте свою собственную привязку, позвоните и подайте заявку

Вы также можете создавать свои собственные версии bind, call и apply, используя прототипную природу JavaScript.

Вы правы, мои извинения. Если вы хотите избежать использования .apply, .call или .bind в своей пользовательской реализации, все становится немного сложнее. Вот как вы можете это сделать:

Function.prototype.myBind = function(context) {
  var func = this;
  return function(...args) {
    var contextClone = Object.create(context);
    contextClone.func = func;
    return contextClone.func(...args);
  };
};
Function.prototype.myCall = function(context, ...args) {
  var contextClone = Object.create(context);
  contextClone.func = this;
  return contextClone.func(...args);
};
Function.prototype.myApply = function(context, args) {
  var contextClone = Object.create(context);
  contextClone.func = this;
  return contextClone.func(...args);
};

Каждый из этих методов работает, манипулируя контекстом функции и затем вызывая функцию с указанными аргументами.

Давайте поместим вышеуказанные функции для тестирования:

const name = 'Tom';

const obj = {
  name: 'Jerry',
};
function greet(greeting, punctuation) {
  console.log(`${greeting}, ${this.name}${punctuation}`);
}
const boundGreet = greet.myBind(obj);

boundGreet('Hello', '!');  // Should log: "Hello, Jerry!"
greet.myCall(obj, 'Hi', '.');  // Should log: "Hi, Jerry."
greet.myApply(obj, ['Hey', '!']);  // Should log: "Hey, Jerry!"

Задания для закрепления вашего понимания

Задача 1. Изменение контекста с помощью Bind
Создайте объект person с помощью методов name и introduce. Затем создайте еще один объект person2, используя только файл name. Используйте bind, чтобы создать новую функцию, в которой person2 может представиться, используя метод introduce из person1.

let person1 = {
  name: 'John',
  introduce: function() {
    console.log(`Hello, my name is ${this.name}`);
  }
};
let person2 = {
  name: 'Jane'
};
let introduceJane = person1.introduce.bind(person2);
introduceJane();  // "Hello, my name is Jane"

Задача 2. Вызов функций с помощью Call и Apply
Предположим, у вас есть объект car с методом describe. Создайте объект car2 с теми же свойствами, но без метода describe. Используйте call и apply для вызова метода describe из car1 для car2.

let car1 = {
  make: 'Toyota',
  model: 'Camry',
  describe: function() {
    console.log(`This car is a ${this.make} ${this.model}`);
  }
};
let car2 = {
  make: 'Honda',
  model: 'Civic'
};
car1.describe.call(car2);  // "This car is a Honda Civic"
car1.describe.apply(car2);  // "This car is a Honda Civic"

Задача 3. Реализация собственной функции Bind, Call и Apply
Используя определенные выше пользовательские функции myBind, myCall и myApply, повторите действия, описанные в Задаче 1 и Задаче 2. Убедитесь, что они работают должным образом.

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