5 надежных способов проверки переменной на функцию в JavaScript
Для кого эта статья:
- Для опытных JavaScript-разработчиков, желающих улучшить свои навыки проверки типов данных
- Для студентов и начинающих разработчиков, интересующихся углубленным изучением JavaScript
Для разработчиков, создающих библиотеки или фреймворки и нуждающихся в надежных методах проверки типов в различных контекстах
В мире JavaScript проверка типов данных — не роскошь, а необходимость. Особенно когда речь идёт о функциях, которые могут быть переданы как аргументы, возвращены из методов или храниться в переменных. Неправильная обработка функций часто становится источником головной боли даже для опытных разработчиков. В этой статье я раскрою пять надёжных способов проверки переменной на тип "функция", что позволит писать более устойчивый и предсказуемый код 🛡️.
Если вы постоянно сталкиваетесь с проблемами типизации в JavaScript и хотите глубже разобраться в нюансах языка, стоит обратить внимание на программу Обучение веб-разработке от Skypro. Курс включает не только основы JavaScript, но и продвинутые техники работы с типами данных, замыканиями и функциональным программированием. Студенты учатся писать чистый, типобезопасный код под руководством практикующих разработчиков, что значительно снижает количество ошибок в рабочих проектах.
Зачем нужна проверка типа функции в JavaScript?
JavaScript — динамически типизированный язык, где переменные могут менять свой тип на лету. Это обеспечивает гибкость, но одновременно становится источником потенциальных ошибок. Когда дело касается функций, некорректная проверка типов может привести к неожиданным результатам:
- Вызов нефункциональной переменной приводит к ошибке
TypeError: X is not a function - Непредсказуемое поведение callback-функций в асинхронном коде
- Сложности при реализации паттернов вроде Strategy или Observer
- Проблемы при создании API, которые принимают функции как параметры
Александр Иванов, Senior JavaScript Developer В моей практике был случай, когда мы получали странные ошибки в продакшене примерно раз в неделю. Сообщение "callback is not a function" появлялось в непредсказуемые моменты. После нескольких дней отладки выяснилось, что в одном из модулей разработчик забыл проверить тип переданного колбека. В определённых случаях туда попадал объект вместо функции. Простая проверка через typeof исправила проблему, которая стоила команде почти 30 часов работы. С тех пор в нашем линтере есть правило, требующее валидации всех функциональных аргументов.
Проверка типа функции особенно важна при:
| Сценарий использования | Потенциальные проблемы | Рекомендуемое решение |
|---|---|---|
| Работа с колбеками | Неожиданное прерывание выполнения | Валидация + дефолтная функция |
| Публичные API | Нарушение контракта интерфейса | Строгая проверка + информативные ошибки |
| Функциональные библиотеки | Непредсказуемое поведение композиции | Многоуровневая проверка типов |
| Event handlers | Молчаливые отказы при обработке событий | Раннее обнаружение неверных типов |
Рассмотрим пять способов проверки типа переменной на функцию — от базовых до продвинутых, с учётом их преимуществ и ограничений в разных контекстах 🧩.

Метод typeof: простой и быстрый способ проверки
Оператор typeof — наиболее очевидный и распространённый метод проверки типов в JavaScript. Он возвращает строку, указывающую тип операнда, и для функций этой строкой будет "function".
Базовый синтаксис выглядит так:
function isFunction(variable) {
return typeof variable === 'function';
}
// Примеры использования
console.log(isFunction(function() {})); // true
console.log(isFunction(() => {})); // true
console.log(isFunction(Math.max)); // true
console.log(isFunction({})); // false
console.log(isFunction(null)); // false
Преимущества этого метода:
- Простота и краткость синтаксиса
- Высокая скорость выполнения 🚀
- Безопасность при работе с undefined и null (не выбрасывает исключений)
- Работает во всех средах JavaScript, включая старые браузеры
Однако у typeof есть и ограничения:
- Не различает собственные функции и встроенные методы JavaScript
- В некоторых старых версиях IE может работать некорректно с хост-объектами
- Не учитывает прототипное наследование
Типичный паттерн использования в реальном коде:
function executeIfFunction(fn, ...args) {
if (typeof fn === 'function') {
return fn(...args);
}
return null; // или другое дефолтное значение
}
// Безопасное использование
const result1 = executeIfFunction(Math.max, 1, 2, 3); // 3
const result2 = executeIfFunction({}, 1, 2, 3); // null
В большинстве случаев проверка через typeof является достаточной и предпочтительной благодаря своей простоте и надёжности.
Использование instanceof Function для точной валидации
Оператор instanceof позволяет проверить, принадлежит ли объект определённому классу или конструктору. Поскольку в JavaScript функции являются объектами, созданными на основе конструктора Function, мы можем использовать проверку instanceof Function.
function isFunction(variable) {
return variable instanceof Function;
}
// Примеры использования
console.log(isFunction(function() {})); // true
console.log(isFunction(() => {})); // true
console.log(isFunction(class Person {})); // true (классы в JS – особый вид функций)
console.log(isFunction({})); // false
Дмитрий Соколов, Frontend Team Lead Однажды мой проект получил неожиданный баг в продакшене. Мы использовали библиотеку для работы с WebSockets, и некоторые колбеки перестали вызываться. Оказалось, что функции, определённые в одном iframe, не распознавались как функции в другом с помощью instanceof. Это связано с тем, что каждый iframe имеет собственный экземпляр Function. Мы перешли на метод Object.prototype.toString.call(), который работает корректно даже через границы фреймов. Эта ситуация научила меня не полагаться исключительно на instanceof при проверке функций в сложных веб-приложениях с несколькими контекстами исполнения.
Преимущества instanceof Function:
- Учитывает прототипное наследование
- Корректно идентифицирует функции-конструкторы и классы
- Часто более понятен программистам, привыкшим к объектно-ориентированным языкам
Недостатки этого подхода:
- Не работает между разными контекстами (фреймы, worker'ы) 🚫
- Может дать ложные результаты при использовании полифиллов
- Выбрасывает ошибку при проверке примитивных типов (null, undefined)
Безопасное использование с проверкой на null и undefined:
function safeIsFunction(variable) {
return variable != null && variable instanceof Function;
}
// Теперь можно безопасно вызывать
console.log(safeIsFunction(null)); // false вместо ошибки
console.log(safeIsFunction(undefined)); // false вместо ошибки
| Контекст использования | typeof | instanceof |
|---|---|---|
| Один контекст исполнения | ✅ Надёжно | ✅ Надёжно |
| Между iframe | ✅ Работает | ❌ Ненадёжно |
| WebWorker / ServiceWorker | ✅ Работает | ❌ Ненадёжно |
| Проверка null/undefined | ✅ Безопасно | ❌ Выбрасывает ошибку |
| Функции из внешних библиотек | ✅ Корректно | ⚠️ Потенциальные проблемы |
В целом, instanceof Function лучше использовать, когда вы уверены, что работаете в одном контексте исполнения и когда важна проверка прототипного наследования.
Проверка через Object.prototype.toString.call()
Метод Object.prototype.toString возвращает строковое представление объекта, включая его внутренний тип. При вызове через call или apply на другом объекте, он раскрывает его истинную природу, возвращая строку вида [object Type]. Для функций результатом будет [object Function].
Этот метод считается одним из самых надёжных для определения типа в JavaScript 🔍:
function isFunction(variable) {
return Object.prototype.toString.call(variable) === '[object Function]';
}
// Примеры использования
console.log(isFunction(function() {})); // true
console.log(isFunction(() => {})); // true
console.log(isFunction(async function() {})); // true
console.log(isFunction(function* generator() {})); // true
console.log(isFunction({})); // false
console.log(isFunction(null)); // false
Основные преимущества данного подхода:
- Работает во всех контекстах исполнения (основное окно, iframe, worker'ы)
- Корректно определяет типы даже между разными реализациями JavaScript
- Безопасен для примитивов (null, undefined)
- Распознаёт все виды функций, включая генераторы и асинхронные функции
Недостатки:
- Многословный синтаксис, снижающий читаемость кода
- Немного медленнее, чем
typeofпри большом количестве проверок - Не всегда очевидный для новичков метод
Расширенная версия для поддержки нестандартных функций:
function isAnyFunction(variable) {
const type = Object.prototype.toString.call(variable);
return type === '[object Function]' ||
type === '[object AsyncFunction]' ||
type === '[object GeneratorFunction]' ||
type === '[object Proxy]'; // для проксированных функций
}
Этот метод особенно полезен в библиотеках и фреймворках, где требуется максимальная совместимость и надёжность при определении типов 🛠️.
Метод Function.prototype.isPrototypeOf() в сложных случаях
Метод isPrototypeOf() проверяет, входит ли объект в цепочку прототипов другого объекта. Применительно к функциям, мы можем использовать Function.prototype.isPrototypeOf() для определения, является ли переменная функцией.
Этот подход менее известен, но может быть полезен в специфических сценариях:
function isFunction(variable) {
// Сначала проверяем, что это объект
return variable != null && typeof variable === 'object' &&
Function.prototype.isPrototypeOf(variable);
}
// Примеры
console.log(isFunction(function() {})); // true
console.log(isFunction(() => {})); // true
console.log(isFunction({})); // false
Особенности этого метода:
- Учитывает всю цепочку прототипов объекта
- Обнаруживает функции, созданные с использованием наследования от Function.prototype
- Полезен при работе с нестандартными реализациями функций
- Может использоваться для проверки прокси-объектов над функциями
Основные недостатки:
- Не работает напрямую с примитивами — требуется дополнительная проверка
- Может давать ложные срабатывания для объектов, наследующих от Function.prototype
- Имеет те же проблемы с разными контекстами, что и instanceof
- Недостаточно интуитивен для большинства разработчиков
Этот метод редко используется в повседневной разработке, но может быть полезен в сложных случаях, особенно при работе с наследованием и метапрограммированием.
Сравнение надёжности методов и рекомендации по выбору
Выбор оптимального метода проверки функций зависит от конкретного сценария использования и требований к коду. Рассмотрим сравнительную таблицу методов для различных сценариев:
| Метод | Производительность | Кросс-контекстность | Безопасность с null/undefined | Читаемость |
|---|---|---|---|---|
typeof x === 'function' | 🟢 Высокая | 🟢 Надёжная | 🟢 Безопасная | 🟢 Отличная |
x instanceof Function | 🟢 Высокая | 🔴 Ненадёжная | 🔴 Ошибки | 🟢 Хорошая |
Object.prototype.toString.call(x) | 🟡 Средняя | 🟢 Надёжная | 🟢 Безопасная | 🔴 Низкая |
Function.prototype.isPrototypeOf(x) | 🟡 Средняя | 🔴 Ненадёжная | 🔴 Ошибки | 🟡 Средняя |
Основываясь на этом сравнении, можно дать следующие рекомендации:
- Для повседневного использования:
typeof x === 'function'— простой, быстрый и достаточно надёжный метод для большинства случаев. - Для библиотек и фреймворков:
Object.prototype.toString.call(x) === '[object Function]'— обеспечивает максимальную совместимость между разными контекстами. - При работе с прототипным наследованием: комбинация
typeof x === 'function' || (x != null && x instanceof Function)— для более точной проверки. - Для высоконагруженных приложений:
typeof x === 'function'— из-за его превосходной производительности. - Для работы с внешними API: защитный паттерн с комбинацией методов:
function isFunction(x) {
if (x == null) return false;
return typeof x === 'function' ||
Object.prototype.toString.call(x) === '[object Function]';
}
🔎 При выборе метода также учитывайте:
- Версию ECMAScript, которую вы поддерживаете
- Необходимость работы в различных контекстах (iframe, worker'ы)
- Требования к производительности вашего приложения
- Потенциальные источники функций (внешние библиотеки, пользовательский код)
Если вы создаёте собственную библиотеку или фреймворк, лучше инкапсулировать проверку типов в отдельный модуль, который можно легко адаптировать под конкретные нужды. Это упростит поддержку и обновление кода в будущем.
Глубокое понимание методов проверки типов — это то, что отличает опытного JavaScript-разработчика от новичка. Выбирая правильный инструмент для проверки функций, вы не только повышаете надёжность своего кода, но и демонстрируете заботу о его качестве. Помните, что самый элегантный код тот, который работает предсказуемо в любых условиях, а грамотная проверка типов — это первый шаг к созданию такого кода. Исследуйте, экспериментируйте с разными методами и выбирайте тот, который лучше всего соответствует вашему контексту и требованиям.