В чём разница между классической функцией и стрелочной
В JavaScript существуют два основных способа объявления функций: классическая (обычная) функция и стрелочная функция (arrow function). Оба синтаксиса позволяют создавать функции, но имеют различия в поведении, синтаксисе, работе с контекстом (this), аргументами и объектами, а также в возможности использования как конструкторов. Эти различия критически важны для правильного понимания и использования функций в различных ситуациях.
1. Синтаксис
Классическая функция:
function add(a, b) {
return a + b;
}
Или функциональное выражение:
const add = function(a, b) {
return a + b;
};
Стрелочная функция:
const add = (a, b) => a + b;
Стрелочные функции компактнее и особенно удобны при использовании в колбэках, функциональных операциях (map, filter, reduce) и в React-компонентах.
2. Поведение this
Классическая функция:
-
this зависит от контекста вызова.
-
Может быть переопределено через call, apply, bind.
const user = {
name: "Аня",
greet: function() {
console.log(\`Привет, ${this.name}\`);
}
};
user.greet(); // Привет, Аня
Стрелочная функция:
-
Не имеет собственного this.
-
Наследует this из внешнего лексического контекста.
const user = {
name: "Аня",
greet: () => {
console.log(\`Привет, ${this.name}\`);
}
};
user.greet(); // Привет, undefined (или ошибка)
Внутри стрелочной функции this не ссылается на объект user, а на this из места, где функция была определена (например, глобальный объект).
3. Поведение с bind, call, apply
Классическая функция:
- Можно явно привязать this.
function show() {
console.log(this.value);
}
const obj = { value: 42 };
show.call(obj); // 42
Стрелочная функция:
- Невозможно переопределить this, даже через bind.
const show = () => console.log(this.value);
const obj = { value: 42 };
show.call(obj); // undefined
Стрелочная функция захватывает this один раз из окружающей области и не поддаётся переназначению.
4. Использование в методах объектов
Стрелочные функции не подходят в роли методов объекта, где требуется this, указывающий на сам объект.
const counter = {
count: 0,
inc: function() {
this.count++;
}
};
counter.inc(); // count = 1
При использовании стрелочной функции:
const counter = {
count: 0,
inc: () => {
this.count++;
}
};
counter.inc(); // this = глобальный объект → undefined или ошибка
5. Возможность быть конструктором (new)
Классическая функция:
- Может использоваться как конструктор с new.
function Person(name) {
this.name = name;
}
const p = new Person("Антон");
console.log(p.name); // Антон
Стрелочная функция:
-
Не может быть вызвана с new.
-
Попытка вызвать вызовет ошибку.
const Person = (name) => {
this.name = name;
};
const p = new Person("Антон"); // TypeError: Person is not a constructor
Это связано с тем, что у стрелочной функции нет прототипа и собственного this.
6. Аргументы (arguments)
Классическая функция:
- Имеет встроенный объект arguments, содержащий все переданные аргументы.
function test() {
console.log(arguments);
}
test(1, 2, 3); // \[1, 2, 3\]
Стрелочная функция:
-
Не имеет собственного arguments.
-
Можно получить arguments только из внешней (родительской) функции.
const test = () => {
console.log(arguments);
};
test(1, 2); // ReferenceError: arguments is not defined
Если стрелочная функция вложена в обычную, она может использовать arguments из неё:
function outer() {
const inner = () => {
console.log(arguments);
};
inner(1, 2, 3); // покажет аргументы outer()
}
outer(5, 6); // \[5, 6\]
7. Поддержка super и new.target
Классическая функция:
-
Поддерживает super в методах классов.
-
Поддерживает new.target — указывает, была ли функция вызвана через new.
function Foo() {
console.log(new.target);
}
new Foo(); // \[Function: Foo\]
Стрелочная функция:
- Не имеет super и new.target.
const Foo = () => {
console.log(new.target);
};
new Foo(); // TypeError
8. Поведение в таймерах и callback-функциях
Классическая функция:
function Timer() {
this.seconds = 0;
setInterval(function () {
this.seconds++;
console.log(this.seconds); // NaN или ошибка
}, 1000);
}
this в функции внутри setInterval указывает на глобальный объект.
Решения:
-
использовать bind(this)
-
сохранить this в замыкании (const self = this)
-
использовать стрелочную функцию
Стрелочная функция:
function Timer() {
this.seconds = 0;
setInterval(() => {
this.seconds++;
console.log(this.seconds);
}, 1000);
}
В этом случае this внутри стрелочной функции указывает на экземпляр Timer.
9. Сокращённая запись return
Стрелочная функция:
Если тело функции состоит из одного выражения, можно опустить фигурные скобки и ключевое слово return:
const double = x => x \* 2;
Классическая функция:
Такой возможности нет — нужно писать явно:
function double(x) {
return x \* 2;
}
10. Использование в функциональном стиле
Стрелочные функции активно применяются в методах массивов и функциональных выражениях:
const arr = \[1, 2, 3\];
const squared = arr.map(x => x \*\* 2); // \[1, 4, 9\]
Это делает код короче и читабельнее по сравнению с обычными функциями:
const squared = arr.map(function(x) {
return x \*\* 2;
});
11. Объект как возвращаемое значение
При возвращении объекта из стрелочной функции без фигурных скобок нужно использовать круглые скобки:
const getUser = () => ({ name: "Лена", age: 25 });
Если написать:
const getUser = () => { name: "Лена" };
То функция вернёт undefined, потому что фигурные скобки интерпретируются как блок, а не как объект.
12. Переопределение this внутри метода класса
Классические методы:
class User {
constructor(name) {
this.name = name;
}
greet() {
console.log(\`Привет, ${this.name}\`);
}
}
Если метод используется как callback, контекст может потеряться:
const u = new User("Игорь");
setTimeout(u.greet, 1000); // Привет, undefined
Решение — использовать стрелочную функцию:
class User {
constructor(name) {
this.name = name;
this.greet = () => {
console.log(\`Привет, ${this.name}\`);
};
}
}
Теперь метод greet всегда будет вызываться с правильным this.
13. Перечисляемость (enumerability)
Стрелочные функции — всегда неперечисляемые свойства, если объявлены в объекте:
const obj = {
arrow: () => {},
regular: function () {}
};
console.log(Object.keys(obj)); // \["arrow", "regular"\]
Обе функции перечисляемы, но их поведение при переопределении прототипов различается в классах и конструкциях.