Что вернёт этот код: typeof (function(){})()

Дан блок кода:

console.log(typeof (function () {})())

Что выведет console.log?

Теория по задаче

Разбор синтаксиса

IIFE (Immediately Invoked Function Expression)

(function(){})()

Это немедленно вызываемое функциональное выражение (IIFE). Разберём его структуру:

(             // 1. Открывающая скобка для группировки
 function(){} // 2. Функциональное выражение - создаёт функцию
)             // 3. Закрывающая скобка группировки
()            // 4. Оператор вызова функции - немедленно вызывает то, что перед ним

Важные нюансы:

  • function(){} без оборачивающих скобок было бы объявлением функции, а не выражением
  • Скобки вокруг (function(){}) превращают объявление в выражение
  • К любому выражению, которое возвращает функцию, можно применить ()

Альтернативные формы IIFE

// Все эти формы эквивалентны
(function(){})()
(function(){}())
!function(){}()
+function(){}()

Возвращаемое значение функции

Что возвращает функция без return?

function test() {
  // Нет оператора return
}
console.log(test()); // undefined

Правило: Любая функция в JavaScript, которая не возвращает значение явно (через return), возвращает undefined.

const result = (function(){})();
console.log(result); // undefined
console.log(result === undefined); // true

Приоритет операторов

Таблица приоритетов (релевантная часть)

ПриоритетТип оператораАссоциативностьОператоры
17группировкаслева направо(...)
16вызов функциислева направо(...)
16typeofсправа налевоtypeof

Ключевой момент: У typeof и оператора вызова () одинаковый приоритет (16), но разная ассоциативность:

  • typeof — справа налево
  • () — слева направо

Как парсер разбирает выражение

typeof (function(){})()

Шаг 1: Парсер видит выражение и начинает разбор с самого высокого приоритета.

Шаг 2: Группирующие скобки (function(){}) имеют приоритет 17 — обрабатываются первыми:

// После обработки группировки
typeof (FunctionObject)()
// где FunctionObject = function(){}

Шаг 3: Далее есть два оператора с приоритетом 16:

  • typeof (ассоциативность справа налево)
  • () вызова функции (ассоциативность слева направо)

Из-за ассоциативности вызов функции () связывается теснее с тем, что слева от него, чем typeof:

// Фактический порядок выполнения
typeof ( (function(){})() )
// а не
(typeof (function{}))()

Шаг 4: Выполняется вызов функции:

(function(){})() // возвращает undefined

Шаг 5: К результату применяется typeof:

typeof undefined // возвращает "undefined"

Поведение оператора typeof

Возможные возвращаемые значения

console.log(typeof undefined);        // "undefined"
console.log(typeof null);             // "object" (историческая особенность)
console.log(typeof true);             // "boolean"
console.log(typeof 42);               // "number"
console.log(typeof "hello");          // "string"
console.log(typeof Symbol());         // "symbol"
console.log(typeof 123n);             // "bigint"
console.log(typeof function(){});     // "function"
console.log(typeof {});               // "object"
console.log(typeof []);               // "object"
console.log(typeof /regex/);          // "object"

Особенности typeof

Это оператор, а не функция, поэтому скобки не обязательны:

typeof x    // работает
typeof(x)   // тоже работает, но это не вызов функции

Всегда возвращает строку:

console.log(typeof typeof 42); // "string" (потому что typeof 42 → "number", typeof "number" → "string")

Безопасен с необъявленными переменными:

console.log(typeof undeclaredVar); // "undefined" (не вызывает ReferenceError)

Итого

Ключевые выводы:

  1. (function(){})() — это IIFE, возвращающая undefined (если нет return)
  2. Приоритет операторов заставляет сначала выполнить вызов функции, затем typeof
  3. typeof undefined всегда возвращает строку "undefined"
  4. Оператор typeof безопасен и всегда возвращает строку с именем типа

Правильный мысленный алгоритм:

  1. Определи, что это IIFE
  2. Пойми, что пустая функция возвращает undefined
  3. Запомни, что () вызова имеет приоритет над typeof в этом контексте
  4. Примени typeof к undefined → получи "undefined"

Эта задача прекрасно иллюстрирует важность понимания приоритетов операторов в JavaScript и поведения функций по умолчанию.