|
[Определение функции на JavaScript]
Существует 6 основных способов определения функций в языке JavaScript. Понимание их различий критически важно, так как они влияют на объект this, возможность вызова до объявления (hoisting) и область видимости.
Вот полный обзор от самых старых до самых современных способов:
1. Function Declaration (объявление функции)
Классический способ. Такие функции поднимаются (hoisting) в начало своей области видимости, поэтому их можно вызывать до того, как они написаны в коде.
// Вызов ДО объявления работает, ошибки нет:
console.log(sum(5, 3)); // 8
function sum(a, b) { return a + b;
}
2. Function Expression (функциональное выражение)
Функция записывается как значение переменной. Не поднимается в начало модуля — вызвать её можно только после присваивания.
// Вызов ДО объявления вызовет ошибку (ReferenceError) const multiply = function(a, b) { return a * b;
};
console.log(multiply(2, 3)); // 6
3. Arrow Functions (стрелочные функции) — современный стандарт
Самый популярный способ в современном коде. Короткий синтаксис и, самое главное, не имеют своего this (берется его из внешнего контекста).
// Если тело в одну строку, return и фигурные скобки можно опустить const square = x => x * x;
// Если аргументов несколько или тело сложное const divide = (a, b) => { if (b === 0) return 'Ошибка'; return a / b;
};
console.log(square(4)); // 16
4. Function Constructor
Создает функцию из строки. Используется крайне редко (из-за проблем с безопасностью и производительностью), но полезен для динамической генерации кода.
const dynamicFunc = new Function('a', 'b', 'return a + b');
console.log(dynamicFunc(1, 2)); // 3
5. Метод объекта (shorthand method)
Краткая запись для функций внутри объектов.
const user = { name: 'Alex', // Старая запись: greet: function() { ... } greet() { console.log(`Hello, ${this.name}`); }
};
user.greet(); // Hello, Alex
6. Генераторы (function*)
Особый тип функций, которые можно приостанавливать и возвращать несколько значений за время выполнения (используются с yield).
function* idGenerator() { let id = 0; while (true) { yield id++; // Возвращает значение и останавливается }
}
const gen = idGenerator();
console.log(gen.next().value); // 0
console.log(gen.next().value); // 1
Ключевые отличия способов определения функций JavaScript:
| Характеристика |
Declaration |
Expression |
Arrow (=>) |
| Hoisting (подъем) |
✅ Да (можно вызвать до объявления) |
❌ Нет |
❌ Нет |
| Свой this |
✅ Есть (зависит от вызова) |
✅ Есть |
❌ Нет (берет у родителя) |
| Объект arguments |
✅ Есть |
✅ Есть |
❌ Нет (используйте оператор rest) |
| Как конструктор (new) |
✅ Можно |
✅ Можно |
❌ Нельзя |
Совет по выбору:
1. Используйте Function Declaration, когда нужно объявить глобальную утилиту, которая будет использоваться по всему скрипту. 2. В 99% остальных случаев используйте Arrow Functions — они лаконичны и избавляют от головной боли с потерей контекста this в обработчиках событий или таймерах. 3. Используйте Function Expression, только если вам осознанно нужно собственное поведение this или объект arguments.
[Способы возврата значений из функции]
В JavaScript функции всегда возвращают значение. Если вы явно ничего не пишете, они возвращают undefined.
Существует 3 основных способа вернуть значение, и один особый случай, который ломает шаблоны (генераторы). Давайте разберем их по порядку.
1. Явный возврат (ключевое слово return)
Самый очевидный способ возврата. Как только интерпретатор встречает return, выполнение функции немедленно прекращается, а значение из return отправляется наружу.
function greet(name) { return `Привет, ${name}!`; // Функция здесь заканчивается console.log('Этот код никогда не выполнится');
}
const message = greet('Алекс');
console.log(message); // "Привет, Алекс!"
Важный нюанс: если написать return без значения, то функция вернет undefined.
function test() { return; // Неявно возвращает undefined
}
console.log(test()); // undefined
2. Неявный возврат (только для стрелочных функций)
Это фишка стрелочных функций. Если убрать фигурные скобки `{ }`, то return ставится автоматически, а функция возвращает результат вычисления выражения.
// Короткая запись — неявный возврат const double = x => x * 2;
console.log(double(5)); // 10
// Если нужно вернуть объект, оберните его в круглые скобки, // иначе JavaScript спутает его с телом функции: const getObj = () => ({ name: 'Alex' });
console.log(getObj()); // { name: 'Alex' }
3. Возврат через Callback (асинхронный возврат)
В классическом JavaScript нельзя "вернуть" данные из асинхронной операции (например, запроса к серверу) через return, потому что код выполняется не по порядку. Вместо этого данные передаются в так называемую функцию обратного вызова (callback).
function fetchData(callback) { setTimeout(() => { const data = { id: 1 }; callback(data); // Мы не возвращаем, а вызываем callback с данными }, 1000);
}
// Использование:
fetchData((result) => { console.log(result); // { id: 1 } — данные приходят сюда через секунду
});
4. Особый случай: генераторы (yield вместо return)
Функции-генераторы (function*) используют оператор yield. Он не завершает функцию, а лишь приостанавливает её, возвращая промежуточное значение. Чтобы получить следующее значение, нужно вызвать next() снова.
function* counter() { yield 1; // Возвращаем 1, но не выходим из функции yield 2; // Возвращаем 2 return 3; // Завершаем функцию, возвращаем 3 (в свойстве done: true)
}
const gen = counter();
console.log(gen.next()); // { value: 1, done: false }
console.log(gen.next()); // { value: 2, done: false }
console.log(gen.next()); // { value: 3, done: true }
Что происходит, если ничего не возвращать? Любая функция в JS по умолчанию возвращает undefined. Это не ошибка, а стандартное поведение.
function doNothing() { let a = 5 + 5; // Тут нет return
}
console.log(doNothing()); // undefined
Главные правила, которые стоит запомнить:
| Ситуация |
Возвращает value |
| return; или нет return |
Возвращает undefined |
| Стрелочная функция без { } |
Возвращает результат выражения (неявный return) |
| Функция-генератор |
Возвращает объект-итератор, а значение выдается через yield |
| Асинхронный код (setTimeout, запросы) |
Возвращать бесполезно — используйте Promise или Callback |
Совет для современного кода. Сейчас принято использовать return + Promise (или async/await) вместо callback-функций. Это позволяет работать с асинхронными данными как с обычными:
async function fetchModern() { const response = await fetch('/api/data'); return response.json(); // Возвращаем Promise, который разрешится данными
}
[Параметры функции]
Ниже приведен обзор использования параметров функции JavaScript.
1. Базовый синтаксис
function sum(a, b) { return a + b;
}
sum(2, 3); // 5
2. Параметры по умолчанию (ES6)
function greet(name = "Гость") { console.log(`Привет, ${name}!`);
}
greet(); // Привет, Гость!
greet("Анна"); // Привет, Анна!
Можно использовать выражения:
function multiply(a, b = a * 2) { return a * b;
}
multiply(3); // 18 (3 * 6)
3. Остаточные параметры (rest parameters) — `...`
Следующее определение функции собирает все лишние аргументы в массив:
function sumAll(...numbers) { return numbers.reduce((acc, n) => acc + n, 0);
}
sumAll(1, 2, 3, 4); // 10
С другими параметрами:
function show(first, second, ...rest) { console.log(first); // 1 console.log(second); // 2 console.log(rest); // [3, 4, 5]
}
show(1, 2, 3, 4, 5);
4. Объект arguments (устаревший)
Доступен только внутри обычных функций (не стрелочных):
function oldSum() { let total = 0; for (let i = 0; i < arguments.length; i++) { total += arguments[i]; } return total;
}
oldSum(1, 2, 3); // 6
В современном коде используйте `...rest` вместо `arguments`.
5. Деструктуризация параметров
Объекты:
function showUser({ name, age, city = "Не указан" }) { console.log(`${name}, ${age} лет, г. ${city}`);
}
showUser({ name: "Иван", age: 30 }); // Иван, 30 лет, г. Не указан
Массивы:
function getFirst([first, second]) { return first;
}
getFirst([10, 20, 30]); // 10
6. Передача по значению vs по ссылке
Примитивы (числа, строки, булевы) — копируются:
function change(x) { x = 100;
}
let a = 5;
change(a);
console.log(a); // 5 (не изменилось)
Объекты и массивы — передаются по ссылке:
function changeObj(obj) { obj.name = "Изменено";
}
let user = { name: "Иван" };
changeObj(user);
console.log(user.name); // "Изменено"
Чтобы не мутировать исходный объект — создавайте копию:
function safeUpdate(obj) { return { ...obj, name: "Новое имя" };
}
7. Проверка наличия аргументов
function test(a, b) { if (a === undefined) { console.log("a не передан"); }
// Или if (arguments.length < 2) { console.log("Мало аргументов"); }
}
8. Стрелочные функции и параметры
// Один параметр — скобки можно опустить const double = x => x * 2;
// Нет параметров — обязательны пустые скобки const hello = () => "Привет";
// Несколько параметров — скобки обязательны const add = (a, b) => a + b;
9. Типизация параметров (JSDoc)
Для документации и подсказок в IDE:
/** * @param {number} a - первое число * @param {number} b - второе число * @returns {number} сумма */ function add(a, b) { return a + b;
}
10. Примеры распространённых паттернов
Функция с опциями:
function fetchData(url, options = {}) { const { method = "GET", timeout = 5000, headers = {} } = options; // ...
}
fetchData("/api/users", { timeout: 10000 });
Callback-параметры:
function process(items, callback) { for (const item of items) { callback(item); }
}
process([1, 2, 3], n => console.log(n * 2));
Шпаргалка:
| Конструкция |
Пример |
Назначение |
| param = default |
(a = 1) |
Значение по умолчанию |
| ...rest |
(...args) |
Сбор остаточных аргументов в массив |
| { prop } |
({ name }) |
Деструктуризация объекта |
| [first] |
([a, b]) |
Деструктуризация массива |
| arguments |
arguments[0] |
Устаревший доступ ко всем аргументам |
|