← JavaScript/Объект функции: name, length, custom properties#134 из 383← ПредыдущийСледующий →+20 XP
Полезно по теме:Гайд: как учить JavaScriptПрактика: JS базаПрактика: async и сетьТермин: Closure

Объект функции: name, length, custom properties

Вы пишете систему мониторинга API: нужно считать сколько раз каждый эндпоинт был вызван, без изменения оригинальных функций. Решение — функции как объекты: можно навешивать свойства прямо на функцию, как на любой объект.

Что решает эта тема

В JavaScript функции — это объекты первого класса. У них есть встроенные свойства (name, length) и возможность добавлять свои. Это открывает паттерны, недоступные в других языках.

typeof function() {}   // 'function'
function f() {} instanceof Object  // true

На основе предыдущих уроков

  • функции: базовое объявление
  • объекты: функция — это объект, у неё есть свойства
  • замыкания: ключевое отличие от свойств функции
  • new Function: ещё один способ создания функций динамически
  • function.name

    Каждая функция имеет свойство name — её имя:

    // Function Declaration — имя очевидно
    function greet(name) { return 'Привет, ' + name }
    console.log(greet.name)  // 'greet'
    
    // Function Expression — JS выводит имя из переменной
    const sayBye = function(name) { return 'Пока, ' + name }
    console.log(sayBye.name)  // 'sayBye'
    
    // Arrow function — то же самое
    const add = (a, b) => a + b
    console.log(add.name)  // 'add'
    
    // Метод объекта
    const obj = { greet() {} }
    console.log(obj.greet.name)  // 'greet'
    
    // Без имени
    console.log(function() {}.name)  // '' (пустая строка)

    function.length

    Свойство length возвращает количество параметров до первого rest-параметра или параметра со значением по умолчанию:

    function f1(a, b, c) {}
    console.log(f1.length)  // 3
    
    function f2(a, b = 0, c) {}
    console.log(f2.length)  // 1 (только a, до первого default)
    
    function f3(a, ...rest) {}
    console.log(f3.length)  // 1 (только a, rest не считается)
    
    // Полезно для метапрограммирования:
    // можно узнать сколько аргументов ожидает функция

    Пользовательские свойства функции

    Так как функция — объект, на неё можно навесить любые свойства:

    function processOrder(order) {
      processOrder.callCount++
      // ... обработка заказа
      return order
    }
    processOrder.callCount = 0
    
    processOrder({ id: 1 })
    processOrder({ id: 2 })
    console.log(processOrder.callCount)  // 2

    Это не то же самое, что замыкание:

    // Замыкание — переменная скрыта, недоступна снаружи
    function makeCounter() {
      let count = 0
      return function() { return ++count }
    }
    
    // Свойство функции — данные открыты и доступны снаружи
    function counter() { return ++counter.count }
    counter.count = 0
    // Можно читать и менять снаружи:
    console.log(counter.count)  // 0
    counter.count = 100          // можно сбросить!

    NFE — Named Function Expression

    Named Function Expression (NFE) — это function expression с именем:

    // Обычный function expression (анонимный)
    const factorial = function(n) {
      return n <= 1 ? 1 : n * factorial(n - 1)  // проблема: зависит от внешней переменной!
    }
    
    // NFE — имя доступно внутри самой функции
    const factorial = function fact(n) {
      return n <= 1 ? 1 : n * fact(n - 1)  // внутри можно вызывать по имени fact
    }
    
    console.log(factorial(5))  // 120
    console.log(typeof fact)   // 'undefined' — имя недоступно снаружи!

    Зачем NFE?

  • Рекурсия внутри функции не зависит от имени переменной
  • Если переменную переприсвоят — рекурсия не сломается
  • Имя отображается в стектрейсах и отладчике
  • Кэш как свойство функции

    function fibonacci(n) {
      if (fibonacci.cache[n] !== undefined) return fibonacci.cache[n]
      if (n <= 1) return n
      fibonacci.cache[n] = fibonacci(n - 1) + fibonacci(n - 2)
      return fibonacci.cache[n]
    }
    fibonacci.cache = {}
    
    console.log(fibonacci(40))   // быстро, результаты кэшируются
    console.log(Object.keys(fibonacci.cache).length)  // количество кэшированных значений

    Типичные ошибки

    Ошибка 1: не инициализируют свойство функции до первого вызова

    // Сломано: counter.count не существует при первом вызове
    function counter() {
      counter.count++   // NaN: undefined + 1
      return counter.count
    }
    // counter.count = 0  ← забыли!
    
    // Исправлено:
    counter.count = 0
    function counter() {
      counter.count++
      return counter.count
    }

    Ошибка 2: NFE — имя доступно внутри, но не снаружи

    const factorial = function fact(n) {
      return n <= 1 ? 1 : n * fact(n - 1)
    }
    
    console.log(factorial(5))   // 120 — OK
    // console.log(fact(5))     // ReferenceError: fact is not defined
    // fact — видно ТОЛЬКО внутри тела функции

    Ошибка 3: свойство функции vs замыкание

    // Свойство функции — доступно снаружи, можно изменить
    function counter() { return ++counter.count }
    counter.count = 0
    counter.count = 100  // внешний код может сломать состояние!
    
    // Замыкание — скрыто, защищено
    function makeCounter() {
      let count = 0
      return { inc: () => ++count, get: () => count }
    }
    // count нельзя изменить снаружи

    В реальных проектах

  • Мониторинг: счётчик вызовов функций для метрик производительности
  • Мемоизация: кэш результатов как свойство функции (lodash.memoize делает именно это)
  • NFE: рекурсивные функции-обработчики, где имя переменной может измениться
  • function.length: DI-контейнеры используют его для определения количества зависимостей функции
  • Примеры

    Счётчик вызовов как свойство функции и NFE-рекурсия факториала

    // --- Пример 1: счётчик вызовов как свойство функции ---
    
    function sendEmail(to, subject) {
      sendEmail.callCount++
      sendEmail.lastRecipient = to
      // В реальном коде здесь была бы отправка письма
      console.log(`Отправка письма на ${to}: "${subject}"`)
    }
    sendEmail.callCount = 0
    sendEmail.lastRecipient = null
    
    sendEmail('ivan@example.ru', 'Ваш заказ подтверждён')
    sendEmail('maria@example.ru', 'Акция: скидка 20%')
    sendEmail('ivan@example.ru', 'Заказ доставлен')
    
    console.log('Отправлено писем:', sendEmail.callCount)      // 3
    console.log('Последний адресат:', sendEmail.lastRecipient) // 'ivan@example.ru'
    console.log('Имя функции:', sendEmail.name)                // 'sendEmail'
    console.log('Параметров:', sendEmail.length)               // 2
    
    // --- Пример 2: NFE для безопасной рекурсии ---
    
    // Проблема без NFE: если переменную переприсвоят — рекурсия сломается
    let badFactorial = function(n) {
      return n <= 1 ? 1 : n * badFactorial(n - 1)
    }
    const saved = badFactorial
    badFactorial = null
    try {
      saved(5)  // TypeError: badFactorial is not a function
    } catch (e) {
      console.log('Ошибка без NFE:', e.message)
    }
    
    // NFE решает проблему: имя 'fact' доступно внутри, не зависит от переменной
    const goodFactorial = function fact(n) {
      return n <= 1 ? 1 : n * fact(n - 1)
    }
    const savedGood = goodFactorial
    // goodFactorial = null — даже если переприсвоить, savedGood работает
    
    console.log('\n5! =', goodFactorial(5))   // 120
    console.log('10! =', goodFactorial(10))  // 3628800
    console.log('Имя NFE снаружи:', goodFactorial.name)  // 'fact'
    console.log('fact снаружи:', typeof fact)            // 'undefined'

    Объект функции: name, length, custom properties

    Вы пишете систему мониторинга API: нужно считать сколько раз каждый эндпоинт был вызван, без изменения оригинальных функций. Решение — функции как объекты: можно навешивать свойства прямо на функцию, как на любой объект.

    Что решает эта тема

    В JavaScript функции — это объекты первого класса. У них есть встроенные свойства (name, length) и возможность добавлять свои. Это открывает паттерны, недоступные в других языках.

    typeof function() {}   // 'function'
    function f() {} instanceof Object  // true

    На основе предыдущих уроков

  • функции: базовое объявление
  • объекты: функция — это объект, у неё есть свойства
  • замыкания: ключевое отличие от свойств функции
  • new Function: ещё один способ создания функций динамически
  • function.name

    Каждая функция имеет свойство name — её имя:

    // Function Declaration — имя очевидно
    function greet(name) { return 'Привет, ' + name }
    console.log(greet.name)  // 'greet'
    
    // Function Expression — JS выводит имя из переменной
    const sayBye = function(name) { return 'Пока, ' + name }
    console.log(sayBye.name)  // 'sayBye'
    
    // Arrow function — то же самое
    const add = (a, b) => a + b
    console.log(add.name)  // 'add'
    
    // Метод объекта
    const obj = { greet() {} }
    console.log(obj.greet.name)  // 'greet'
    
    // Без имени
    console.log(function() {}.name)  // '' (пустая строка)

    function.length

    Свойство length возвращает количество параметров до первого rest-параметра или параметра со значением по умолчанию:

    function f1(a, b, c) {}
    console.log(f1.length)  // 3
    
    function f2(a, b = 0, c) {}
    console.log(f2.length)  // 1 (только a, до первого default)
    
    function f3(a, ...rest) {}
    console.log(f3.length)  // 1 (только a, rest не считается)
    
    // Полезно для метапрограммирования:
    // можно узнать сколько аргументов ожидает функция

    Пользовательские свойства функции

    Так как функция — объект, на неё можно навесить любые свойства:

    function processOrder(order) {
      processOrder.callCount++
      // ... обработка заказа
      return order
    }
    processOrder.callCount = 0
    
    processOrder({ id: 1 })
    processOrder({ id: 2 })
    console.log(processOrder.callCount)  // 2

    Это не то же самое, что замыкание:

    // Замыкание — переменная скрыта, недоступна снаружи
    function makeCounter() {
      let count = 0
      return function() { return ++count }
    }
    
    // Свойство функции — данные открыты и доступны снаружи
    function counter() { return ++counter.count }
    counter.count = 0
    // Можно читать и менять снаружи:
    console.log(counter.count)  // 0
    counter.count = 100          // можно сбросить!

    NFE — Named Function Expression

    Named Function Expression (NFE) — это function expression с именем:

    // Обычный function expression (анонимный)
    const factorial = function(n) {
      return n <= 1 ? 1 : n * factorial(n - 1)  // проблема: зависит от внешней переменной!
    }
    
    // NFE — имя доступно внутри самой функции
    const factorial = function fact(n) {
      return n <= 1 ? 1 : n * fact(n - 1)  // внутри можно вызывать по имени fact
    }
    
    console.log(factorial(5))  // 120
    console.log(typeof fact)   // 'undefined' — имя недоступно снаружи!

    Зачем NFE?

  • Рекурсия внутри функции не зависит от имени переменной
  • Если переменную переприсвоят — рекурсия не сломается
  • Имя отображается в стектрейсах и отладчике
  • Кэш как свойство функции

    function fibonacci(n) {
      if (fibonacci.cache[n] !== undefined) return fibonacci.cache[n]
      if (n <= 1) return n
      fibonacci.cache[n] = fibonacci(n - 1) + fibonacci(n - 2)
      return fibonacci.cache[n]
    }
    fibonacci.cache = {}
    
    console.log(fibonacci(40))   // быстро, результаты кэшируются
    console.log(Object.keys(fibonacci.cache).length)  // количество кэшированных значений

    Типичные ошибки

    Ошибка 1: не инициализируют свойство функции до первого вызова

    // Сломано: counter.count не существует при первом вызове
    function counter() {
      counter.count++   // NaN: undefined + 1
      return counter.count
    }
    // counter.count = 0  ← забыли!
    
    // Исправлено:
    counter.count = 0
    function counter() {
      counter.count++
      return counter.count
    }

    Ошибка 2: NFE — имя доступно внутри, но не снаружи

    const factorial = function fact(n) {
      return n <= 1 ? 1 : n * fact(n - 1)
    }
    
    console.log(factorial(5))   // 120 — OK
    // console.log(fact(5))     // ReferenceError: fact is not defined
    // fact — видно ТОЛЬКО внутри тела функции

    Ошибка 3: свойство функции vs замыкание

    // Свойство функции — доступно снаружи, можно изменить
    function counter() { return ++counter.count }
    counter.count = 0
    counter.count = 100  // внешний код может сломать состояние!
    
    // Замыкание — скрыто, защищено
    function makeCounter() {
      let count = 0
      return { inc: () => ++count, get: () => count }
    }
    // count нельзя изменить снаружи

    В реальных проектах

  • Мониторинг: счётчик вызовов функций для метрик производительности
  • Мемоизация: кэш результатов как свойство функции (lodash.memoize делает именно это)
  • NFE: рекурсивные функции-обработчики, где имя переменной может измениться
  • function.length: DI-контейнеры используют его для определения количества зависимостей функции
  • Примеры

    Счётчик вызовов как свойство функции и NFE-рекурсия факториала

    // --- Пример 1: счётчик вызовов как свойство функции ---
    
    function sendEmail(to, subject) {
      sendEmail.callCount++
      sendEmail.lastRecipient = to
      // В реальном коде здесь была бы отправка письма
      console.log(`Отправка письма на ${to}: "${subject}"`)
    }
    sendEmail.callCount = 0
    sendEmail.lastRecipient = null
    
    sendEmail('ivan@example.ru', 'Ваш заказ подтверждён')
    sendEmail('maria@example.ru', 'Акция: скидка 20%')
    sendEmail('ivan@example.ru', 'Заказ доставлен')
    
    console.log('Отправлено писем:', sendEmail.callCount)      // 3
    console.log('Последний адресат:', sendEmail.lastRecipient) // 'ivan@example.ru'
    console.log('Имя функции:', sendEmail.name)                // 'sendEmail'
    console.log('Параметров:', sendEmail.length)               // 2
    
    // --- Пример 2: NFE для безопасной рекурсии ---
    
    // Проблема без NFE: если переменную переприсвоят — рекурсия сломается
    let badFactorial = function(n) {
      return n <= 1 ? 1 : n * badFactorial(n - 1)
    }
    const saved = badFactorial
    badFactorial = null
    try {
      saved(5)  // TypeError: badFactorial is not a function
    } catch (e) {
      console.log('Ошибка без NFE:', e.message)
    }
    
    // NFE решает проблему: имя 'fact' доступно внутри, не зависит от переменной
    const goodFactorial = function fact(n) {
      return n <= 1 ? 1 : n * fact(n - 1)
    }
    const savedGood = goodFactorial
    // goodFactorial = null — даже если переприсвоить, savedGood работает
    
    console.log('\n5! =', goodFactorial(5))   // 120
    console.log('10! =', goodFactorial(10))  // 3628800
    console.log('Имя NFE снаружи:', goodFactorial.name)  // 'fact'
    console.log('fact снаружи:', typeof fact)            // 'undefined'

    Задание

    Напиши функцию makeCounter(), которая возвращает функцию-счётчик. При каждом вызове счётчик увеличивает внутреннее значение на 1 и возвращает его. Функция-счётчик должна иметь два свойства: .reset() — сбрасывает счётчик в 0, и .getCount() — возвращает текущее значение без увеличения.

    Подсказка

    const counter = function() { counter.count++ }; counter.count = 0; counter.reset = () => { counter.count = 0 }; counter.getCount = () => counter.count

    Загружаем среду выполнения...
    Загружаем AI-помощника...