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

Конструктор и оператор new

Реальная проблема: фабрика однотипных объектов

В Авито тысячи объявлений. Каждое — объект с полями: название, цена, город, дата. Если описывать каждый объект вручную { title: ..., price: ..., city: ... } — это дублирование кода и риск ошибиться в названии поля. Конструктор — это фабрика: описываешь структуру один раз, создаёшь сколько угодно одинаково устроенных объектов.

Что решает конструктор

  • Гарантирует что все объекты одного типа имеют одинаковую структуру
  • Избавляет от дублирования при создании похожих объектов
  • Позволяет добавить методы через прототип (один раз для всех экземпляров)
  • На основе предыдущих уроков

  • «Объекты» — создание объектов, свойства
  • «this» — this внутри конструктора ссылается на создаваемый объект
  • «Функции» — конструктор это обычная функция, вызванная через new
  • Синтаксис

    По соглашению конструкторы называются с заглавной буквы — чтобы отличать от обычных функций:

    function Product(name, price, category) {
      // При вызове через new:
      // 1. Создаётся пустой объект {}
      // 2. this = этот объект
      this.name     = name
      this.price    = price
      this.category = category
      this.inStock  = true
      // 3. return this (автоматически)
    }
    
    const laptop = new Product('Ноутбук', 75000, 'tech')
    const mouse  = new Product('Мышь',    1500,  'tech')
    
    console.log(laptop.name)   // 'Ноутбук'
    console.log(mouse.price)   // 1500

    Что делает new пошагово

    function User(name) {
      // Шаг 1: движок делает this = {}
      // Шаг 2: выполняется код функции
      this.name = name
      this.createdAt = new Date().toISOString()
      // Шаг 3: движок делает return this
    }

    Если вызвать без new — this будет undefined (в strict mode) или глобальным объектом:

    const u = User('Иван')  // забыли new!
    console.log(u)          // undefined

    Методы в конструкторе

    function BankAccount(owner, initialBalance) {
      this.owner   = owner
      this.balance = initialBalance
    
      this.deposit = function(amount) {
        this.balance += amount
        return this  // для цепочки
      }
    
      this.getBalance = function() {
        return `${this.owner}: ${this.balance} ₽`
      }
    }
    
    const acc = new BankAccount('Алексей', 10000)
    acc.deposit(5000)
    console.log(acc.getBalance())  // 'Алексей: 15000 ₽'

    > Примечание: методы в конструкторе создаются заново для каждого экземпляра. Эффективнее добавлять их через прототип или использовать классы.

    instanceof — проверка типа

    console.log(laptop instanceof Product)  // true
    console.log(laptop instanceof Array)    // false

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

    Ошибка 1: вызов без new

    // Сломано:
    function User(name) { this.name = name }
    const u = User('Иван')   // this = window или undefined!
    console.log(u)           // undefined
    
    // Исправлено:
    const u = new User('Иван')

    Ошибка 2: явный return объекта из конструктора

    // Неожиданное поведение:
    function User(name) {
      this.name = name
      return { surprise: true }  // new вернёт этот объект, а не this!
    }
    
    const u = new User('Иван')
    console.log(u.name)     // undefined — вернулся { surprise: true }
    
    // Правило: если в конструкторе нужен return — возвращай только примитивы
    // (они игнорируются, new вернёт this)

    Ошибка 3: забыть сохранить значение

    // Сломано:
    function Product(name, price) {
      name  = name    // присваивает локальной переменной, не this!
      price = price
    }
    
    // Исправлено:
    function Product(name, price) {
      this.name  = name
      this.price = price
    }

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

  • До ES6 конструкторы были основным способом создания объектов в ООП-стиле
  • Современный эквивалент — классы, которые работают через тот же механизм
  • В Node.js встречаются встроенные конструкторы: new Error(), new Map(), new Promise()
  • В DOM: new XMLHttpRequest(), new FormData(), new URL()
  • Примеры

    Конструктор Listing — объявления на доске объявлений

    function Listing(title, price, city, category) {
      this.id        = Math.random().toString(36).slice(2, 8)
      this.title     = title
      this.price     = price
      this.city      = city
      this.category  = category
      this.createdAt = new Date().toISOString()
      this.views     = 0
    
      this.view = function() {
        this.views++
        return this
      }
    
      this.applyDiscount = function(percent) {
        return new Listing(
          this.title + ' (скидка ' + percent + '%)',
          Math.round(this.price * (1 - percent / 100)),
          this.city,
          this.category
        )
      }
    
      this.toString = function() {
        return `[${this.category}] ${this.title} — ${this.price} ₽ (${this.city})`
      }
    }
    
    const bike   = new Listing('Велосипед Trek', 25000, 'Москва', 'sport')
    const laptop = new Listing('Ноутбук Lenovo', 45000, 'СПб',   'tech')
    const desk   = new Listing('Стол IKEA',      8000,  'Москва', 'furniture')
    
    bike.view().view().view()
    console.log(bike.views)         // 3
    console.log(laptop.toString())  // '[tech] Ноутбук Lenovo — 45000 ₽ (СПб)'
    
    const saleLaptop = laptop.applyDiscount(15)
    console.log(saleLaptop.price)   // 38250
    console.log(laptop.price)       // 45000 — оригинал не изменился
    
    const listings = [bike, laptop, desk]
    const moscow = listings.filter(l => l.city === 'Москва')
    console.log(moscow.map(l => l.title))
    // ['Велосипед Trek', 'Стол IKEA']
    
    console.log(bike instanceof Listing)  // true

    Конструктор и оператор new

    Реальная проблема: фабрика однотипных объектов

    В Авито тысячи объявлений. Каждое — объект с полями: название, цена, город, дата. Если описывать каждый объект вручную { title: ..., price: ..., city: ... } — это дублирование кода и риск ошибиться в названии поля. Конструктор — это фабрика: описываешь структуру один раз, создаёшь сколько угодно одинаково устроенных объектов.

    Что решает конструктор

  • Гарантирует что все объекты одного типа имеют одинаковую структуру
  • Избавляет от дублирования при создании похожих объектов
  • Позволяет добавить методы через прототип (один раз для всех экземпляров)
  • На основе предыдущих уроков

  • «Объекты» — создание объектов, свойства
  • «this» — this внутри конструктора ссылается на создаваемый объект
  • «Функции» — конструктор это обычная функция, вызванная через new
  • Синтаксис

    По соглашению конструкторы называются с заглавной буквы — чтобы отличать от обычных функций:

    function Product(name, price, category) {
      // При вызове через new:
      // 1. Создаётся пустой объект {}
      // 2. this = этот объект
      this.name     = name
      this.price    = price
      this.category = category
      this.inStock  = true
      // 3. return this (автоматически)
    }
    
    const laptop = new Product('Ноутбук', 75000, 'tech')
    const mouse  = new Product('Мышь',    1500,  'tech')
    
    console.log(laptop.name)   // 'Ноутбук'
    console.log(mouse.price)   // 1500

    Что делает new пошагово

    function User(name) {
      // Шаг 1: движок делает this = {}
      // Шаг 2: выполняется код функции
      this.name = name
      this.createdAt = new Date().toISOString()
      // Шаг 3: движок делает return this
    }

    Если вызвать без new — this будет undefined (в strict mode) или глобальным объектом:

    const u = User('Иван')  // забыли new!
    console.log(u)          // undefined

    Методы в конструкторе

    function BankAccount(owner, initialBalance) {
      this.owner   = owner
      this.balance = initialBalance
    
      this.deposit = function(amount) {
        this.balance += amount
        return this  // для цепочки
      }
    
      this.getBalance = function() {
        return `${this.owner}: ${this.balance} ₽`
      }
    }
    
    const acc = new BankAccount('Алексей', 10000)
    acc.deposit(5000)
    console.log(acc.getBalance())  // 'Алексей: 15000 ₽'

    > Примечание: методы в конструкторе создаются заново для каждого экземпляра. Эффективнее добавлять их через прототип или использовать классы.

    instanceof — проверка типа

    console.log(laptop instanceof Product)  // true
    console.log(laptop instanceof Array)    // false

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

    Ошибка 1: вызов без new

    // Сломано:
    function User(name) { this.name = name }
    const u = User('Иван')   // this = window или undefined!
    console.log(u)           // undefined
    
    // Исправлено:
    const u = new User('Иван')

    Ошибка 2: явный return объекта из конструктора

    // Неожиданное поведение:
    function User(name) {
      this.name = name
      return { surprise: true }  // new вернёт этот объект, а не this!
    }
    
    const u = new User('Иван')
    console.log(u.name)     // undefined — вернулся { surprise: true }
    
    // Правило: если в конструкторе нужен return — возвращай только примитивы
    // (они игнорируются, new вернёт this)

    Ошибка 3: забыть сохранить значение

    // Сломано:
    function Product(name, price) {
      name  = name    // присваивает локальной переменной, не this!
      price = price
    }
    
    // Исправлено:
    function Product(name, price) {
      this.name  = name
      this.price = price
    }

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

  • До ES6 конструкторы были основным способом создания объектов в ООП-стиле
  • Современный эквивалент — классы, которые работают через тот же механизм
  • В Node.js встречаются встроенные конструкторы: new Error(), new Map(), new Promise()
  • В DOM: new XMLHttpRequest(), new FormData(), new URL()
  • Примеры

    Конструктор Listing — объявления на доске объявлений

    function Listing(title, price, city, category) {
      this.id        = Math.random().toString(36).slice(2, 8)
      this.title     = title
      this.price     = price
      this.city      = city
      this.category  = category
      this.createdAt = new Date().toISOString()
      this.views     = 0
    
      this.view = function() {
        this.views++
        return this
      }
    
      this.applyDiscount = function(percent) {
        return new Listing(
          this.title + ' (скидка ' + percent + '%)',
          Math.round(this.price * (1 - percent / 100)),
          this.city,
          this.category
        )
      }
    
      this.toString = function() {
        return `[${this.category}] ${this.title} — ${this.price} ₽ (${this.city})`
      }
    }
    
    const bike   = new Listing('Велосипед Trek', 25000, 'Москва', 'sport')
    const laptop = new Listing('Ноутбук Lenovo', 45000, 'СПб',   'tech')
    const desk   = new Listing('Стол IKEA',      8000,  'Москва', 'furniture')
    
    bike.view().view().view()
    console.log(bike.views)         // 3
    console.log(laptop.toString())  // '[tech] Ноутбук Lenovo — 45000 ₽ (СПб)'
    
    const saleLaptop = laptop.applyDiscount(15)
    console.log(saleLaptop.price)   // 38250
    console.log(laptop.price)       // 45000 — оригинал не изменился
    
    const listings = [bike, laptop, desk]
    const moscow = listings.filter(l => l.city === 'Москва')
    console.log(moscow.map(l => l.title))
    // ['Велосипед Trek', 'Стол IKEA']
    
    console.log(bike instanceof Listing)  // true

    Задание

    Напиши конструктор Message(from, text) для мессенджера. Каждое сообщение имеет: id (случайный), from, text, timestamp (текущее время ISO), isRead: false. Добавь методы: markAsRead() — помечает прочитанным и возвращает this, reply(text) — создаёт и возвращает новое сообщение от 'Bot' с указанным текстом, toString() — возвращает строку вида "[от кого] текст".

    Подсказка

    id: Math.random().toString(36).slice(2, 8). timestamp: new Date().toISOString(). reply возвращает new Message("Bot", replyText). toString: `[${this.from}] ${this.text}`

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