instanceof и примеси

instanceof и примеси #

instanceof проверяет, есть ли в цепочке прототипов объекта prototype указанного конструктора (или класса). Примеси (mixins) — способ добавить нескольким классам общее поведение через копирование методов, когда единственное наследование extends не хватает.


Как работает instanceof #

class Lesson {
  constructor(title) {
    this.title = title
  }
}

class VideoLesson extends Lesson {
  constructor(title, sec) {
    super(title)
    this.sec = sec
  }
}

const v = new VideoLesson('Таймеры', 900)

v instanceof VideoLesson // true
v instanceof Lesson // true
v instanceof Object // true
v instanceof Array // false

Выражение obj instanceof C ищет C.prototype в цепочке [[Prototype]] объекта obj.


С функциями-конструкторами #

До class проверка та же:

function Tag(name) {
  this.name = name
}

const t = new Tag('javascript')

t instanceof Tag // true
t instanceof Object // true

instanceof привязан к функции/классу, с которым связывали прототип при создании, а не к форме литерала объекта.


Ограничения instanceof #

Проверка идёт по цепочке прототипов, не по «форме» данных:

const plain = { title: 'Plain', complete() {} }
plain instanceof Object // true

const lesson = new Lesson('OOP')
plain instanceof Lesson // false

Объекты из другого окна iframe или Realm могут дать ложный false для того же класса — разные глобальные конструкторы.

Для плоских структур данных чаще проверяют поля вручную или используют TypeScript на этапе сборки, а не instanceof.


Symbol.hasInstance #

Класс может переопределить поведение instanceof:

class Box {
  static [Symbol.hasInstance](value) {
    return (
      typeof value === 'object' &&
      value !== null &&
      'width' in value &&
      'height' in value
    )
  }
}

const slot = { width: 100, height: 50 }
slot instanceof Box // true

Используйте редко — усложняет чтение кода; уместно для библиотечных абстракций с нестандартными экземплярами.


Примеси: идея #

JavaScript допускает одно наследование через extends. Примесь копирует методы в прототип или в класс, чтобы «подмешать» возможности:

const Timestamped = {
  touch() {
    this.updatedAt = Date.now()
    return this
  },
}

const Taggable = {
  addTag(tag) {
    if (!this.tags) this.tags = []
    this.tags.push(tag)
    return this
  },
}

function applyMixins(target, ...sources) {
  for (const source of sources) {
    Object.assign(target.prototype, source)
  }
}

class Note {
  constructor(text) {
    this.text = text
    this.createdAt = Date.now()
  }
}

applyMixins(Note, Timestamped, Taggable)

const n = new Note('повторить замыкания')
n.addTag('homework').touch()
n.tags // ['homework']
n.updatedAt // число, мс

Это композиция поведения без цепочки extends на каждый чих.


Object.assign и классы #

Для современных классов миксины часто оформляют как функции от базового класса:

function Timestamped(Base) {
  return class extends Base {
    touch() {
      this.updatedAt = Date.now()
      return this
    }
  }
}

class Article {
  constructor(title) {
    this.title = title
  }
}

const TimestampedArticle = Timestamped(Article)
const a = new TimestampedArticle('Прототипы')
a.touch()
a instanceof Article // true

Паттерн «миксин как функция от суперкласса» лучше сочетается с extends и super, чем слепое копирование в prototype.


instanceof vs typeof #

typeofinstanceof
примитивыданет (не объект)
обычные объекты"object"нужен конструктор
массивы"object"Array лучше через Array.isArray
классыне различаетда, по прототипу
typeof 'jsflow' // "string"
'jsflow' instanceof String // false (примитив)

typeof [] // "object"
[] instanceof Array // true
Array.isArray([]) // true — предпочтительно для массивов

Частые ошибки #

  1. instanceof для примитивов — всегда false, кроме обёрток new String().
  2. Мутация чужого prototype через миксин — коллизии имён методов у разных примесей; проверяйте пересечения.
  3. Смешение duck typing и instanceof — объект с теми же полями не пройдёт instanceof Lesson.
  4. Проверка плоского JSONinstanceof бессмысленен после JSON.parse; там только plain objects.

Итог #

  • instanceof смотрит на цепочку прототипов, а не на сходство полей.
  • Наследование extends даёт естественный true для предков класса.
  • Примеси добавляют общие методы нескольким типам без глубокого дерева наследования.
  • Для массивов — Array.isArray; для примитивов — typeof; для доменных объектов — классы и осознанный instanceof.

См. также #