← JavaScript/Проверка типа: instanceof и typeof#96 из 383← ПредыдущийСледующий →+20 XP
Полезно по теме:Гайд: как учить JavaScriptПрактика: JS базаПрактика: async и сетьТермин: Closure

Проверка типа: instanceof и typeof

Представь: ты пишешь API-обработчик в Telegram-боте. Приходят сообщения разных типов — текст, фото, команда, ошибка. Как понять что именно пришло и как это обработать? Для этого нужна проверка типов.

Какую проблему решает

JavaScript — динамически типизированный язык. Функция может получить строку, массив, объект или null — и нужно понять что именно пришло, чтобы не вызвать метод, которого нет у этого типа.

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

  • Типы данных — примитивы и объекты
  • Классы — class и конструкторы
  • Наследование — цепочка прототипов
  • typeof — для примитивов

    Возвращает строку с именем типа:

    typeof 42           // 'number'
    typeof 'hello'      // 'string'
    typeof true         // 'boolean'
    typeof undefined    // 'undefined'
    typeof Symbol()     // 'symbol'
    typeof 9007n        // 'bigint'
    typeof function(){} // 'function'
    typeof {}           // 'object'
    typeof []           // 'object'  — массив тоже объект!
    typeof null         // 'object'  — исторический баг JS с 1995 года

    instanceof — для объектов и классов

    Проверяет, есть ли прототип класса в цепочке прототипов объекта. Учитывает наследование:

    class Animal {}
    class Dog extends Animal {}
    
    const dog = new Dog()
    dog instanceof Dog     // true
    dog instanceof Animal  // true — наследование учитывается
    dog instanceof Object  // true — всё объект
    
    [] instanceof Array    // true
    [] instanceof Object   // true

    Array.isArray — надёжная проверка массива

    Предпочти Array.isArray вместо instanceof Array — последний может дать false для массивов из другого iframe или realm:

    Array.isArray([])       // true
    Array.isArray({})       // false
    Array.isArray('hello')  // false
    Array.isArray(null)     // false

    Object.prototype.toString — точная диагностика типа

    const tag = v => Object.prototype.toString.call(v)
    
    tag(42)          // '[object Number]'
    tag('hi')        // '[object String]'
    tag(null)        // '[object Null]'
    tag(undefined)   // '[object Undefined]'
    tag([])          // '[object Array]'
    tag(new Date())  // '[object Date]'
    tag(/regex/)     // '[object RegExp]'

    Правильный порядок проверок

    function processInput(value) {
      if (value === null) return 'пусто'           // 1. null первым!
      if (Array.isArray(value)) return value.join(', ')  // 2. массив до 'object'
      if (value instanceof Date) return value.toLocaleDateString('ru-RU')  // 3. Date
      if (typeof value === 'string') return value.toUpperCase()
      if (typeof value === 'number') return value.toFixed(2)
      return String(value)
    }

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

    Ошибка 1: проверка null через typeof

    // Неправильно
    function process(val) {
      if (typeof val === 'object') {
        return val.name  // CRASH если val === null!
      }
    }
    
    // Правильно
    function process(val) {
      if (val === null) return 'пусто'
      if (typeof val === 'object') return val.name
    }

    Ошибка 2: instanceof Array вместо Array.isArray

    // Может сломаться в некоторых контекстах
    if (data instanceof Array) { ... }
    
    // Всегда работает
    if (Array.isArray(data)) { ... }

    Ошибка 3: неправильный порядок — массив после 'object'

    // Неправильно — массив попадёт в ветку 'object'
    if (typeof value === 'object') return 'объект'
    if (Array.isArray(value)) return 'массив'  // никогда не выполнится!
    
    // Правильно — массив проверяем первым
    if (Array.isArray(value)) return 'массив'
    if (typeof value === 'object') return 'объект'

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

  • API-обработчики: проверка типа входящих данных перед обработкой
  • Сериализация/десериализация: разная логика для Date, Array, Object
  • Библиотеки утилит: универсальные функции принимают любой тип
  • Валидация конфигурации: проверить что опция — массив, а не строка
  • Примеры

    Универсальная функция describe — определяет тип и форматирует значение

    function describe(value) {
      // null — первым, до typeof (typeof null === 'object' — это баг JS)
      if (value === null) return 'null'
    
      // Массив — до проверки typeof 'object'
      if (Array.isArray(value)) return `array[${value.length}]`
    
      // Date — до проверки typeof 'object'
      if (value instanceof Date) {
        return `date: ${value.toLocaleDateString('ru-RU')}`
      }
    
      const t = typeof value
      if (t === 'string')    return `string: "${value}"`
      if (t === 'number')    return `number: ${value}`
      if (t === 'boolean')   return `boolean: ${value}`
      if (t === 'undefined') return 'undefined'
      if (t === 'function')  return `function: ${value.name || 'anonymous'}`
      if (t === 'object')    return `object{keys: ${Object.keys(value).join(', ')}}`
      return t
    }
    
    console.log(describe(null))             // 'null'
    console.log(describe([1, 2, 3]))        // 'array[3]'
    console.log(describe(new Date('2024-06-20')))  // 'date: 20.06.2024'
    console.log(describe('привет'))         // 'string: "привет"'
    console.log(describe(42))              // 'number: 42'
    console.log(describe(true))            // 'boolean: true'
    console.log(describe(undefined))       // 'undefined'
    console.log(describe({ a: 1, b: 2 })) // 'object{keys: a, b}'
    console.log(describe(Math.max))        // 'function: max'
    
    // Класс с наследованием — instanceof учитывает цепочку
    class Vehicle {}
    class Car extends Vehicle {}
    
    const car = new Car()
    console.log(car instanceof Car)      // true
    console.log(car instanceof Vehicle)  // true — наследование!
    console.log(car instanceof Object)   // true

    Проверка типа: instanceof и typeof

    Представь: ты пишешь API-обработчик в Telegram-боте. Приходят сообщения разных типов — текст, фото, команда, ошибка. Как понять что именно пришло и как это обработать? Для этого нужна проверка типов.

    Какую проблему решает

    JavaScript — динамически типизированный язык. Функция может получить строку, массив, объект или null — и нужно понять что именно пришло, чтобы не вызвать метод, которого нет у этого типа.

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

  • Типы данных — примитивы и объекты
  • Классы — class и конструкторы
  • Наследование — цепочка прототипов
  • typeof — для примитивов

    Возвращает строку с именем типа:

    typeof 42           // 'number'
    typeof 'hello'      // 'string'
    typeof true         // 'boolean'
    typeof undefined    // 'undefined'
    typeof Symbol()     // 'symbol'
    typeof 9007n        // 'bigint'
    typeof function(){} // 'function'
    typeof {}           // 'object'
    typeof []           // 'object'  — массив тоже объект!
    typeof null         // 'object'  — исторический баг JS с 1995 года

    instanceof — для объектов и классов

    Проверяет, есть ли прототип класса в цепочке прототипов объекта. Учитывает наследование:

    class Animal {}
    class Dog extends Animal {}
    
    const dog = new Dog()
    dog instanceof Dog     // true
    dog instanceof Animal  // true — наследование учитывается
    dog instanceof Object  // true — всё объект
    
    [] instanceof Array    // true
    [] instanceof Object   // true

    Array.isArray — надёжная проверка массива

    Предпочти Array.isArray вместо instanceof Array — последний может дать false для массивов из другого iframe или realm:

    Array.isArray([])       // true
    Array.isArray({})       // false
    Array.isArray('hello')  // false
    Array.isArray(null)     // false

    Object.prototype.toString — точная диагностика типа

    const tag = v => Object.prototype.toString.call(v)
    
    tag(42)          // '[object Number]'
    tag('hi')        // '[object String]'
    tag(null)        // '[object Null]'
    tag(undefined)   // '[object Undefined]'
    tag([])          // '[object Array]'
    tag(new Date())  // '[object Date]'
    tag(/regex/)     // '[object RegExp]'

    Правильный порядок проверок

    function processInput(value) {
      if (value === null) return 'пусто'           // 1. null первым!
      if (Array.isArray(value)) return value.join(', ')  // 2. массив до 'object'
      if (value instanceof Date) return value.toLocaleDateString('ru-RU')  // 3. Date
      if (typeof value === 'string') return value.toUpperCase()
      if (typeof value === 'number') return value.toFixed(2)
      return String(value)
    }

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

    Ошибка 1: проверка null через typeof

    // Неправильно
    function process(val) {
      if (typeof val === 'object') {
        return val.name  // CRASH если val === null!
      }
    }
    
    // Правильно
    function process(val) {
      if (val === null) return 'пусто'
      if (typeof val === 'object') return val.name
    }

    Ошибка 2: instanceof Array вместо Array.isArray

    // Может сломаться в некоторых контекстах
    if (data instanceof Array) { ... }
    
    // Всегда работает
    if (Array.isArray(data)) { ... }

    Ошибка 3: неправильный порядок — массив после 'object'

    // Неправильно — массив попадёт в ветку 'object'
    if (typeof value === 'object') return 'объект'
    if (Array.isArray(value)) return 'массив'  // никогда не выполнится!
    
    // Правильно — массив проверяем первым
    if (Array.isArray(value)) return 'массив'
    if (typeof value === 'object') return 'объект'

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

  • API-обработчики: проверка типа входящих данных перед обработкой
  • Сериализация/десериализация: разная логика для Date, Array, Object
  • Библиотеки утилит: универсальные функции принимают любой тип
  • Валидация конфигурации: проверить что опция — массив, а не строка
  • Примеры

    Универсальная функция describe — определяет тип и форматирует значение

    function describe(value) {
      // null — первым, до typeof (typeof null === 'object' — это баг JS)
      if (value === null) return 'null'
    
      // Массив — до проверки typeof 'object'
      if (Array.isArray(value)) return `array[${value.length}]`
    
      // Date — до проверки typeof 'object'
      if (value instanceof Date) {
        return `date: ${value.toLocaleDateString('ru-RU')}`
      }
    
      const t = typeof value
      if (t === 'string')    return `string: "${value}"`
      if (t === 'number')    return `number: ${value}`
      if (t === 'boolean')   return `boolean: ${value}`
      if (t === 'undefined') return 'undefined'
      if (t === 'function')  return `function: ${value.name || 'anonymous'}`
      if (t === 'object')    return `object{keys: ${Object.keys(value).join(', ')}}`
      return t
    }
    
    console.log(describe(null))             // 'null'
    console.log(describe([1, 2, 3]))        // 'array[3]'
    console.log(describe(new Date('2024-06-20')))  // 'date: 20.06.2024'
    console.log(describe('привет'))         // 'string: "привет"'
    console.log(describe(42))              // 'number: 42'
    console.log(describe(true))            // 'boolean: true'
    console.log(describe(undefined))       // 'undefined'
    console.log(describe({ a: 1, b: 2 })) // 'object{keys: a, b}'
    console.log(describe(Math.max))        // 'function: max'
    
    // Класс с наследованием — instanceof учитывает цепочку
    class Vehicle {}
    class Car extends Vehicle {}
    
    const car = new Car()
    console.log(car instanceof Car)      // true
    console.log(car instanceof Vehicle)  // true — наследование!
    console.log(car instanceof Object)   // true

    Задание

    Напиши функцию getType(value) для системы логирования Sentry-подобного инструмента. Функция должна возвращать точный тип значения в виде строки: "null", "array", "date", "regexp", "object", "string", "number", "boolean", "undefined". Используй typeof, Array.isArray и instanceof.

    Подсказка

    Порядок проверок важен: null (=== null) → Array.isArray → instanceof Date → instanceof RegExp → typeof. typeof null === "object" и typeof [] === "object", поэтому специальные случаи идут первыми.

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