Стрелочные функции и ключевое слово `this` в JavaScript

Последнее обновление: 02/10/2026
  • Стрелочные функции обеспечивают лаконичный синтаксис и позволяют осуществлять захват this лексически исходя из окружающего контекста, вместо того чтобы создавать собственную связь.
  • Значение this В обычных функциях это зависит от способа их вызова, что влияет на функции, методы, конструкторы, классы и коллбэки.
  • Стрелочные функции идеально подходят для обратных вызовов и методов массивов, но плохо подходят для методов объектов, обработчиков событий DOM и конструкторов.
  • Понимание того, когда this Разница между динамическим и лексическим подходами имеет важное значение для предотвращения скрытых ошибок и выбора между стрелочными и традиционными функциями.

Стрелочные функции и это в JavaScript

Если вы когда-либо входили в систему this Если вы использовали разные функции JavaScript и получили совершенно разные результаты, вы не одиноки. Многие разработчики сталкиваются с ситуациями, когда метод выводит ожидаемый объект, а стрелочная функция — нет. windowИ вот, вложенная стрелка внезапно «волшебным образом» указывает обратно на окружающий объект. Понимание причин этого явления — ключ к написанию предсказуемого кода без ошибок.

Стрелочные функции и this Использование ключевых слов в качестве формы — одна из важнейших (и часто неправильно понимаемых) комбинаций в современном JavaScript. Стрелочные функции кажутся просто укороченным синтаксисом, но на самом деле они меняют принцип работы. this Как обрабатывается код, как работают коллбэки, и даже когда их следует или не следует использовать в качестве методов. Давайте разберем все шаг за шагом, от синтаксиса до контекста выполнения, используя простой язык и множество практических примеров.

Синтаксис стрелочных функций без путаницы

Стрелочные функции — это функциональные выражения, записанные с помощью => синтаксис вместо function ключевое слово. Концептуально, их можно рассматривать как компактный способ записи: «взять эти параметры, вычислить это выражение или блок кода и вернуть значение». По сути, это по-прежнему функции, но они ведут себя иначе в нескольких важных аспектах.

Простейшая стрелочная функция напрямую соответствует регулярному функциональному выражению. Например, вот это классическое функциональное выражение:

const multiplyByTwo = function (value) { return value * 2; };

Её можно переписать в виде стрелочной функции следующим образом:

const multiplyByTwo = (value) => { return value * 2; };

Функции стрелок проявляют себя наилучшим образом, когда тело представляет собой единое выражение. Если тело запроса состоит всего из одного оператора, возвращающего результат, можно опустить как фигурные скобки, так и явное указание. return, что позволяет осуществить неявный возврат:

const multiplyByTwo = value => value * 2;

Если параметр всего один, то окружающие скобки можно опустить, но только в этом конкретном случае. So x => x * 2 Это допустимо, но если у вас ноль или несколько параметров, необходимо сохранить скобки:

  • Нулевые параметры: () => 42
  • Один параметр: x => x * 2 or (x) => x * 2
  • Два или более параметров: (x, y) => x + y

Если в теле документа требуется более одного утверждения, необходимо использовать фигурные скобки и явное указание. return. В такой ситуации стрелочные функции ведут себя как обычные функции в отношении возвращаемых значений: нет. returnЗначение не возвращено.

const feedCat = (status) => {
if (status === 'hungry') {
return 'Feed the cat';
} else {
return 'Do not feed the cat';
}
};

Будьте осторожны при возврате литералов объектов из стрелочных функций, поскольку фигурные скобки объекта могут быть ошибочно приняты за тело функции. Чтобы избежать этой неоднозначности, заключите литерал объекта в скобки, чтобы JavaScript понимал, что это выражение, которое должно быть возвращено:

const toObject = value => ({ result: value });

И ещё одно: стрелочные функции всегда являются выражениями, а не объявлениями. Это означает, что они должны быть присвоены переменной, свойству или переданы в качестве аргумента; они не могут существовать самостоятельно, как, например, function myFunc() {}И они не поднимаются так же, как объявления функций, поэтому вы не можете вызвать их до того, как они будут определены.

Что такое this на JavaScript?

Ключевое слово this Это динамическая привязка, которую JavaScript создает для вас при выполнении функции или метода класса. Можно рассматривать это как невидимый параметр, значение которого зависит от того, как и где вызывается функция. Это делает её мощной и гибкой, но также и источником большой путаницы.

В нестрогой функции, this всегда возвращает какой-либо объект; в строгом режиме это может быть буквально любое значение, включая undefined. В JavaScript значение определяется контекстом выполнения: обычная функция, вызов метода, вызов конструктора, класс, глобальная область видимости или стрелочная функция.

На верхнем уровне классического скрипта (а не модуля), this относится к globalThis, которая обычно является значением браузера. window объект. Таким образом, следующее сравнение в браузере будет верным:

console.log(this === window); // true

В функциях, которые не являются стрелочными, this Это полностью определяется местом совершения звонка. Если вы позвоните obj.method()затем внутри method Значение this is objЕсли взять ту же самую функцию и назвать её автономной, то получится следующее: fn() в строгом режиме, this становится undefinedВ нестрогом режиме JavaScript "заменяет" this с globalThis.

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

Существуют также инструменты для управления. this в явном виде: call, apply, bind и Reflect.apply. Они позволяют «вводить» желаемый препарат. this значение: fn.call(obj, arg1, arg2) выполнит fn с this установлен в objВ нестрогом режиме действуют те же правила подстановки: если вы прошли null or undefined as thisих заменяют на globalThisПримитивы заключаются в свои объекты-оболочки.

Обратные вызовы добавляют еще один уровень косвенности, потому что this контролируется тем, кто вызывает ваш обратный вызов. Методы итерации по массивам, Promise Конструктор и аналогичные API обычно вызывают коллбэки с помощью this установлен в undefined (или глобальный объект в режиме неаккуратного доступа). Некоторые API, например: Array.prototype.forEach or Set.prototype.forEachпринять отдельный thisArg параметр, который можно использовать для установки параметров функции обратного вызова. this.

Другие API намеренно вызывают обратные вызовы с пользовательскими настройками. this значения. Так, например, reviver аргумент JSON.parse и replacer для JSON.stringify Получать this Обработчики событий в DOM привязываются к объекту, которому принадлежит обрабатываемое в данный момент свойство. При использовании «классического» способа обработки событий обработчики событий привязываются к элементу, к которому они прикреплены.

Основная идея: стрелочные функции не создают свои собственные this

Отличительной чертой стрелочных функций является то, что они никогда не создают новый объект. this связывание. Вместо этого они смыкаются (или «захватывают») this из окружающей лексической среды в момент их создания. Когда стрелка выполняется позже, она просто повторно использует захваченное значение, независимо от того, как вы его вызываете.

На практике стрелочная функция ведёт себя так, как если бы она была постоянно автоматически привязана к чему-либо. this в пределах его внешней сферы влияния. Вот почему используются такие методы, как... call, apply и bind невозможно изменить this для стрелочной функции: thisArg Аргумент просто игнорируется. Вы по-прежнему можете передавать через них обычные параметры, но... this Значение заблокировано.

Рассмотрим этот фрагмент кода в глобальной области видимости файла сценария:

const arrow = () => console.log(this);
arrow();

Поскольку стрелка определена в глобальном коде, она... this это глобальный this (как правило, window (в скрипте браузера), и это никогда не меняется. призвание arrow В данном контексте, при присваивании функции свойству или передаче её другим пользователям, в лог всегда будет выводиться один и тот же глобальный объект.

Наиболее интересное поведение наблюдается при вложении стрелочных функций внутрь обычных функций или методов. Поскольку стрелка указывает на внешнюю функцию thisТаким образом, он становится мощным инструментом для обратных вызовов, которым необходимо ссылаться на содержащий их объект без обычного использования механизма обратного вызова. .bind(this) церемония.

const counter = {
id: 42,
start() {
setTimeout(() => {
console.log(this.id); // uses counter.id
}, 1000);
},
};

If start внутри использовали традиционную анонимную функцию setTimeoutВам потребуется выполнить привязку вручную. this или сохранить это в переменную. При использовании стрелок функция обратного вызова естественным образом наследует this от start, Которая является counter, так this.id печать 42 как предполагалось.

Эта лексическая связь также объясняет классический вопрос «почему?» this Вопрос о внесении изменений при использовании стрелок в литералах объектов. Посмотрите на эти два предмета:

const obj1 = {
speak() {
console.log(this);
}
};

const obj2 = {
speak: () => {
console.log(this);
}
};

призвание obj1.speak() печать obj1, потому как speak является обычным методом и this устанавливается в зависимости от места вызова. С другой стороны, obj2.speak() регистрирует внешний this (довольно часто window (в браузерах), потому что стрелка не использует объект в качестве своего thisСам по себе литерал объекта не создает новый объект. this Область видимости; это делает только тело функции, а стрелочные функции этот шаг пропускают.

Теперь рассмотрим метод объекта, который создает внутреннюю стрелку и немедленно вызывает ее:

const obj3 = {
speak() {
(() => {
console.log(this);
})();
}
};

obj3.speak();

В этой ситуации внутренняя стрелочная функция наследует this от speak, Которая является obj3 когда называется как obj3.speak(). Несмотря на то, что стрелка представляет собой вложенную, немедленно вызываемую функцию, она всё равно указывает на obj3а не глобальный объект. В этом и заключается суть лексического подхода. this: оно следует за окружающей областью видимости, а не за местом вызова самой стрелки.

this в рамках функций, объектов и конструкторов

Чтобы по-настоящему освоить стрелочные функции и thisЭто помогает понять, как this Работает во всех основных контекстах: обычные функции, методы, конструкторы, классы и глобальная область видимости. Как только эти правила станут ясны, логику поведения стрелок станет гораздо проще объяснить.

В простой функции (не в виде стрелки), this Зависит на 100% от способа вызова функции. Если вы позвоните fn() в строгом режиме, this is undefined; в небрежном режиме замена приводит к this стала globalThis, Если вы позвоните obj.fn(), то this is obj. Двигаться fn к другому объекту или к переменной и значению this будет двигаться соответственно.

В методе, определенном для литерала объекта, this Это объект, к которому осуществляется доступ с помощью метода, а не обязательно тот, где этот метод был первоначально определен. If obj.__proto__ содержит метод, и вы его вызываете. obj.method()затем внутри method, this is objне прототип.

Конструкторы — это ещё один частный случай: когда вы вызываете функцию с помощью new, this привязан к только что созданному экземпляру объекта. Например, в function User(name) { this.name = name; }звонит new User('Alex') Наборы this к новому User объект. Если конструктор явно возвращает не примитивный объект, то этот возвращенный объект заменяет this в качестве конечного значения new выражение.

Синтаксис классов строится на основе этих правил и включает два основных контекста: экземпляр и статический объект. Внутри конструктора или метода экземпляра, this указывает на экземпляр класса, с которым вы работаете. Внутри статических методов или статических блоков инициализации, this Относится к самому классу (или к производному классу при вызове через наследование). Поля экземпляра оцениваются с помощью this привязано к новому экземпляру; статические поля см. this в качестве конструктора класса.

Конструкторы производных классов ведут себя несколько иначе: до тех пор, пока вы их не вызовете. super(), пригодного для использования нет this. Вызов super() инициализирует this путем делегирования в конструктор базового класса; возврат до выполнения этой операции в конструкторе производного класса допускается только в том случае, если вы явно возвращаете другой объект.

В глобальном контексте, this Это зависит от того, как среда JavaScript оборачивает и выполняет ваш код. В классическом браузерном скрипте — верхнего уровня. this является глобальным объектом; в модуле ES — объектом верхнего уровня. this Всегда undefinedМодули Node.js CommonJS внутренне обернуты и обычно выполняются с помощью this установлен в module.exportsАтрибуты обработчиков событий, встроенные в HTML, выполняются с помощью this установить на элемент, к которому они прикреплены.

Одна тонкая, но важная деталь: сами литералы объектов не вводят ничего нового. this объем. Writing const obj = { value: this }; внутри скрипта будет сделано obj.value равный внешнему thisа не сам объект. Только тела функций (и тела классов) создают выделенный объект. this связывание; стрелки намеренно пропускают этот шаг и наследуют.

Почему стрелочные функции отлично подходят для обратных вызовов (и когда они не подходят)

Поскольку стрелочные функции замкнуты thisОни идеально подходят для многих сценариев обратного вызова, где необходимо, чтобы функция обратного вызова продолжала ссылаться на окружающий объект или контекст. Это особенно удобно при работе с таймерами, промисами и методами массивов, такими как map, filter и reduce.

Представьте себе метод, которому необходимо многократно обновлять некоторое свойство с помощью setInterval. Используя традиционную функцию, this Внутри функции обратного вызова по умолчанию будет использоваться глобальный объект (или будет undefined в строгом режиме), поэтому this.count не будет указывать на ваш экземпляр. При использовании стрелочной функции функция обратного вызова естественным образом использует this внешнего метода.

function Counter() {
this.count = 0;

setInterval(() => {
this.count++;
}, 1000);
}

Благодаря стреле, this внутри функции обратного вызова интервала используется Counter например, а не window. Если бы этот обратный вызов был обычной функцией, вам потребовалось бы либо .bind(this) или промежуточная переменная, например const self = this; чтобы сохранить ссылку.

Стрелочные функции также упрощают код, используя методы массивов, где вам часто не нужно учитывать this на всех. Когда вы передаете традиционную функцию в качестве функции обратного вызова, неявный this Обычно undefinedИ вы можете об этом забыть. Стрелками наглядно показано, что функция представляет собой простое отображение входных данных на выходные.

const numbers = [1, 2, 3];
const doubled = numbers.map(n => n * 2);

Однако существуют важные случаи, когда стрелочные функции — неподходящий выбор, особенно когда требуется динамический ввод. this. Два классических антипаттерна — это использование стрелочных функций в качестве методов объектов и в качестве обработчиков событий DOM, которые зависят от this являясь элементом.

Рассмотрим объект, отслеживающий жизнь кошки:

const cat = {
lives: 9,
jump: () => {
this.lives--; // bug: this is not cat
},
};

cat.jump();

С jump это стрела, this не относится к cat но к чему бы то ни было this Именно здесь создавался литерал объекта (часто это глобальный объект). Предполагаемый this.lives-- Либо генерирует исключение (в строгом режиме), либо незаметно изменяет что-то несвязанное. Использование обычного синтаксиса метода здесь — правильный шаг.

Обработчики событий DOM устроены аналогично: стандартный шаблон. this.classList.toggle('on') внутри функции обратного вызова события используется this являющийся элементом, который инициировал событие. С помощью стрелочной функции, this Указатель больше не указывает на элемент, поэтому код перестаёт работать.

const button = document.getElementById('press');

button.addEventListener('click', () => {
this.classList.toggle('on'); // this is not button
});

В этой ситуации обработчик должен быть обычной функцией, чтобы this браузер привязывает элемент к кнопке. Стрелочные функции просто не подходят в качестве прямой замены, если ваша логика предполагает следующее: this быть динамическим целевым событием.

Ещё один неочевидный недостаток заключается в том, что стрелочные функции синтаксически анонимны. Обычно у них нет собственного имени (кроме любой переменной, которой они присвоены), что может сделать трассировку стека менее информативной и немного усложнить рекурсию. В большинстве реальных случаев это приемлемый компромисс, но об этом стоит помнить.

Особые случаи: геттеры, сеттеры, методы ограничения и нестандартные ситуации.

Геттеры и сеттеры подчиняются одному и тому же правилу «места вызова»: this Это объект, к которому осуществляется доступ, а не тот, где это свойство было первоначально определено. Если геттер наследуется от прототипа, и вы вызываете его для производного объекта, this Внутри геттера используется ссылка на производный объект.

Связанные методы, созданные с помощью Function.prototype.bind Они обеспечивают поведение, в некоторой степени схожее с стрелочными функциями, но на уровне обычных функций. Когда вы звоните f.bind(obj)вы создаете новую функцию, чья this постоянно прикреплен к obj, независимо от способа вызова. Это может быть полезно в классах, когда необходимо сохранить this даже если метод отсоединен.

class Example {
constructor() {
this.handleClick = this.handleClick.bind(this);
}

handleClick() {
console.log(this); // always the instance
}
}

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

Существуют также некоторые устаревшие частные случаи, когда this ведет себя по-разному, например, внутри устаревшего with заявление. Внутри with (obj) { ... } блок, вызывающий функцию, которая является свойством obj По сути, ведет себя так, как если бы вы написали obj.method(), так this связан с objСовременный код должен избегать withОднако понимание этого исключения проясняет, что this В принципе, это по-прежнему зависит от того, как формируется вызов функции.

Встроенные обработчики событий в HTML также подчиняются особому правилу: окружающий код встроенного обработчика видит this как элемент, но внутренние функции, определенные внутри этого обработчика, возвращаются к обычному методу. this правила. Таким образом, внутренняя традиционная функция, ничем не ограниченная, обычно будет выглядеть следующим образом: this as globalThis (или undefined (в строгом режиме), а не элемент.

Наконец, помните, что стрелочные функции не имеют prototype свойство и не могут использоваться в качестве конструкторов с new. Попытка new MyArrow() Это вызовет ошибку TypeError. Если вам нужна функция, которая может выступать в качестве конструктора, вы должны использовать обычную функцию или класс.

Учитывая эти детали, выбор между стрелочными и традиционными функциями становится намного проще. Используйте стрелки там, где вам нужен лексический указатель. this и лаконичный синтаксис, а также возможность переключения на обычные функции, когда требуется динамический режим, управляемый местом вызова. this Семантика поведения или конструктора.

Как только вы это поймете, как this В каждой конкретной ситуации стрелочные функции становятся мощным союзником, а не неожиданным источником ошибок. Они упрощают распространенные шаблоны, такие как обратные вызовы и простые преобразования, в то время как обычные функции продолжают выполнять роли, зависящие от них самих. this привязка, например, методов, конструкторов и динамических обработчиков событий.

Похожие посты: