👋 Привет! Я Дастан, автор Hack Frontend. Всегда рад профессиональному нетворкингу => добавляйся в LinkedIn.
Стрелочные vs обычные функции в JavaScript: все отличия
В JavaScript существует несколько способов объявлять функции: Function Declaration, Function Expression и Arrow Function. Каждый из них имеет свои особенности в плане hoisting, использования this, возможности быть конструктором, а также нюансов в синтаксисе и области применения.
Краткое сравнение в таблице
| Характеристика | Function Declaration | Function Expression | Arrow Function |
|---|---|---|---|
| Синтаксис | function name() { ... } | const name = function() { ... } | const name = () => { ... } |
| Hoisting | Полное (функцию можно вызвать до объявления) | Сама функция не всплывает. var: до присваивания переменная равна undefined. const/let: до инициализации — TDZ (ReferenceError) | Как у expression: зависит от var, const или let, а не от стрелочного синтаксиса |
Собственный this | Да (зависит от вызова) | Да (зависит от вызова) | Нет (лексический this, наследуется из внешней области) |
arguments | Доступен | Доступен | Нет (использовать ...rest) |
Использование с new | Да (может выступать как конструктор) | Да (если объявлена как обычная функция) | Нет (Arrow Function не может быть конструктором) |
| Применение | Общие функции, конструкторы | Замыкания, колбэки | Однострочные функции, колбэки |
Детали и примеры
Hoisting (всплытие)
-
Function Declaration полностью всплывает. Это значит, что мы можем вызвать такую функцию даже до места её реального объявления в файле.
hello(); // "Hello from Declaration!" function hello() { console.log("Hello from Declaration!"); } -
Function Expression не всплывает как готовая функция. Поднимается только связывание переменной, и поведение до инициализации зависит от
var,letилиconst.С
varпеременная всплывает и до присваивания равнаundefined: вызов даётTypeError. Сconstилиletобращение до строки инициализации попадает в temporal dead zone и даётReferenceError.console.log(typeof sayHiVar); // "undefined" // sayHiVar(); // TypeError: sayHiVar is not a function var sayHiVar = function () { console.log("Hello from Expression!"); }; // sayHi(); // ReferenceError: Cannot access 'sayHi' before initialization const sayHi = function () { console.log("Hello from Expression!"); }; -
Arrow Function по hoisting совпадает с function expression: всплывает переменная, а не сама стрелка. При
const/letдо инициализации будет TDZ, приvar—undefined.// greet(); // ReferenceError: Cannot access 'greet' before initialization const greet = () => console.log("Hello from Arrow!");
Контекст this
-
У Function Declaration и Function Expression (если это не arrow) контекст
thisопределяется способом вызова (или привязкой .call, .apply, .bind). -
У Arrow Function нет собственного
this: он лексически заимствуется из области, в которой объявлена стрелочная функция.const user = { name: "Alice", sayHello: function() { console.log("Hello, I'm " + this.name); }, arrowHello: () => { console.log("Hello, I'm " + this.name); } }; user.sayHello(); // "Hello, I'm Alice" - this = user user.arrowHello(); // "Hello, I'm undefined" - this берётся из внешней области (глобальная / undefined)
arguments и оператор ...rest
-
Function Declaration и Function Expression имеют псевдомассив
arguments, содержащий все аргументы функции. -
Arrow Function не имеет
arguments, но можно использовать оператор...restдля получения всех аргументов.function sumAll() { let total = 0; for (const arg of arguments) { total += arg; } return total; } const sumAllArrow = (...args) => { return args.reduce((acc, curr) => acc + curr, 0); };
Использование с new
- Только Function Declaration и Function Expression (не в формате стрелки) могут быть конструкторами.
- Arrow Function не может использоваться с
new, так как у неё нет внутреннего механизма[[Construct]].