JavaScript: декораторы и перенаправление вызовов функций
Декораторы JavaScript и методы call/apply необходимы для управления контекстом функций и расширения поведения классов. В этом руководстве объясняется, как эффективно их использовать на практических примерах.
Понимание декораторов в JavaScript
Декораторы предлагают динамичный способ наблюдения, изменения или замены объявлений классов и их членов. Предложение по декораторам в настоящее время находится на стадии 3. Хотя современный синтаксис стандартизирован, многие существующие кодовые базы и учебные материалы по-прежнему используют устаревший синтаксис, который требует транспайлера, такого как Babel.
Что такое декораторы?
По своей сути, декораторы — это функции, которые позволяют добавлять новую функциональность элементам класса (методам, геттерам/сеттерам, свойствам) в декларативном стиле. Во время выполнения они оборачивают исходный метод или свойство, перехватывая вызовы или изменяя их конфигурацию до создания экземпляра класса. Они применяются непосредственно перед определением элемента класса, предоставляя синтаксически выразительный способ изменения поведения.
Использование декораторов
Чтобы использовать декоратор, добавьте символ @ перед функцией декоратора и поместите его над элементом класса, который вы хотите изменить. Вот базовый пример:
Предупреждение: Устаревший синтаксис декораторов ниже признан устаревшим и требует транспайлера. Современный синтаксис стадии 3 стандартизирован и рекомендуется для новых проектов.
function log(target, name, descriptor) {
const original = descriptor.value;
if (typeof original === 'function') {
descriptor.value = function(...args) {
console.log(`Calling ${name} with`, args);
return original.apply(this, args);
}
}
return descriptor;
}
class MyClass {
@log
doSomething(arg) {
// Method body
}
}Этот декоратор log будет выводить сообщение в консоль каждый раз при вызове метода doSomething, включая переданные ему аргументы.
Вот современный аналог на стадии 3. В спецификации стадии 3 target для декораторов методов ссылается на исходную функцию метода в прототипе, поэтому original.apply(this, args) корректно вызывает её. Возврат новой функции заменяет исходный метод. Декораторы также могут применяться к полям и аксессорам классов, хотя их инициализация и обработка дескрипторов немного отличаются в зависимости от типа элемента.
function log(target, context) {
const original = target;
return function(...args) {
console.log(`Calling ${context.name} with`, args);
return original.apply(this, args);
};
}
class MyClass {
@log
doSomething(arg) {
// Method body
}
}Использование методов call и apply
Методы call и apply являются важнейшими инструментами в JavaScript для указания контекста this функции и её вызова. Хотя они выполняют схожие задачи, их использование немного отличается.
Метод call
Метод call вызывает функцию с заданным значением this и аргументами, переданными по отдельности.
Пример использования call:
Метод apply
Напротив, apply вызывает функцию с заданным значением this и аргументами, переданными в виде массива.
Пример использования apply:
Оба метода, call и apply, мощны в сценариях, где необходимо явно задать контекст this или при работе с функциями, принимающими переменное количество аргументов.
Примечание: В отличие от
callиapply, которые вызывают функцию немедленно,bindвозвращает новую функцию с навсегда установленным контекстомthis, что позволяет отложить выполнение.
const boundGreet = greet.bind(person);
boundGreet(); // Output: "Hello, John"Практическое применение
Декораторы упрощают добавление поведения к объектам без изменения исходного кода, способствуя созданию более чистого и модульного дизайна. Аналогично, call и apply обеспечивают гибкость при вызове функций, что критически важно для поддержания правильного контекста this или обработки функций с переменным числом аргументов.
Заключение
Декораторы и методы call/apply предоставляют мощные инструменты для написания лаконичного, гибкого и эффективного кода на JavaScript. Освоение этих концепций позволяет лучше переиспользовать код, создавать абстракции и управлять контекстом в сложных приложениях.
Практика
Какие утверждения точно описывают использование и различия между методами `call` и `apply` в JavaScript?