Function Expression
Функциональное выражение (Function Expression) — создание функции как части выражения, обычно через присваивание переменной. Не поднимается (no hoisting тела).
Зачем нужно
Function Expression позволяет создавать функции динамически, присваивать их переменным и передавать как значения. Это основа callback-паттерна и функционального стиля в JavaScript.
Где используется
Callback-функции, обработчики событий, условное создание функций, IIFE, фабричные функции.
Предпосылки
Function Declaration, Переменные
Синтаксис
Анонимное выражение
const greet = function(name) {
return `Привет, ${name}!`;
};
console.log(greet('Алиса')); // "Привет, Алиса!"
Именованное выражение (Named Function Expression)
const factorial = function fact(n) {
if (n <= 1) return 1;
return n * fact(n - 1); // имя fact доступно внутри
};
console.log(factorial(5)); // 120
// console.log(fact(5)); // ReferenceError — fact не видно снаружи
Отличие от Function Declaration
Hoisting
// Function Declaration — поднимается
console.log(declaredFn); // "Работаю!"
function declaredFn() {
return 'Работаю!';
}
// Function Expression — НЕ поднимается
// console.log(expressionFn); // TypeError: expressionFn is not a function
const expressionFn = function {
return 'Работаю!';
};
Сравнительная таблица
| Свойство | Declaration | Expression |
|---|---|---|
| Hoisting | Да (целиком) | Нет (только переменная) |
| Имя обязательно | Да | Нет |
| Точка с запятой | Не нужна | Нужна (часть выражения) |
| Условное создание | Ненадёжно | Безопасно |
Свойство .name |
Имя функции | Имя переменной или имя NFE |
Условное создание
// Безопасно — с Function Expression
let validator;
if (strictMode) {
validator = function(value) {
return value !== null && value !== undefined && value !== '';
};
} else {
validator = function(value) {
return value != null;
};
}
Named Function Expression (NFE)
NFE полезны для рекурсии и отладки:
// Имя доступно только внутри функции
const findNode = function search(tree, id) {
if (tree.id === id) return tree;
for (const child of tree.children || ) {
const found = search(child, id); // рекурсия через внутреннее имя
if (found) return found;
}
return null;
};
console.log(findNode.name); // "search" — видно в стеке
Свойство name
// Анонимная — имя берётся из переменной
const fn1 = function {};
console.log(fn1.name); // "fn1"
// Именованная — имя из NFE
const fn2 = function myName() {};
console.log(fn2.name); // "myName"
// В объекте — имя из ключа
const obj = {
method: function {}
};
console.log(obj.method.name); // "method"
Паттерны использования
Callback
const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map(function(n) {
return n * 2;
});
console.log(doubled); // [2, 4, 6, 8, 10]
// С именем для отладки
const filtered = numbers.filter(function isEven(n) {
return n % 2 === 0;
});
Немедленный вызов (IIFE)
const module = (function {
let counter = 0;
return {
increment { return ++counter; },
getCount { return counter; }
};
});
console.log(module.increment); // 1
console.log(module.increment); // 2
Фабрика функций
const createMultiplier = function(factor) {
return function(number) {
return number * factor;
};
};
const double = createMultiplier(2);
const triple = createMultiplier(3);
console.log(double(5)); // 10
console.log(triple(5)); // 15
Стратегия через объект
const strategies = {
add: function(a, b) { return a + b; },
subtract: function(a, b) { return a - b; },
multiply: function(a, b) { return a * b; },
};
function calculate(strategy, a, b) {
return strategies[strategy](a, b);
}
console.log(calculate('add', 5, 3)); // 8
console.log(calculate('multiply', 5, 3)); // 15
var vs let/const и hoisting
// var — переменная поднимается как undefined
console.log(typeof fnVar); // "undefined" (не ошибка!)
// fnVar; // TypeError: fnVar is not a function
var fnVar = function { return 42; };
// let/const — TDZ, ошибка при обращении
// console.log(fnLet); // ReferenceError
let fnLet = function { return 42; };
Частые ошибки
1. Забытая точка с запятой
const a = function { return 1; } // нет ;
// Следующая строка может быть интерпретирована как вызов!
(function { console.log('IIFE'); });
// Без ; движок может понять это как: a(function...)
2. Вызов до объявления
// Ожидаем hoisting, но его нет
// processData(data); // TypeError
const processData = function(data) {
return data.map(item => item.value);
};
3. Потеря this в методе
const obj = {
value: 42,
getValue: function {
return this.value;
}
};
const fn = obj.getValue;
console.log(fn); // undefined — this потерян
// Решение: bind или arrow function
Практика
- Создай Function Expression и попробуй вызвать её до объявления
- Напиши Named Function Expression с рекурсией (обход дерева)
- Создай фабрику функций, которая возвращает валидатор
- Используй Function Expression как callback в
Array.prototype.sort() - Сравни
.nameу анонимной и именованной Function Expression
Связанные темы
Ресурсы
🎓 Источник: Функции, стрелочные функции, контексты
- 📅 2018-09-27 · YouTube · ID:
pn5myCmpV2U - Тезисы:
- 4 способа объявить функцию в JS: function declaration, function expression, lambda (arrow),
new Function - Function expression видна только после присваивания — нет hoisting как у declaration
new Function('a', 'b', 'return a + b')— почтиeval, использовать не стоит- Если функция передаётся в аргумент или присваивается ключу объекта, и не имеет имени —
nameберётся из ключа/идентификатора
- 4 способа объявить функцию в JS: function declaration, function expression, lambda (arrow),
- Цитата: «Это чуть ли не eval, это неприлично» (про
new Function)