Как стать автором
Обновить

Комментарии 32

Вывод от меня: когда вкручивали async/await, как обычно, не нашлось знающего JS человека, чтобы подсказать горе-стандартизаторам, что функции могут вызываться и через `new`, и неплохо бы предусмотреть асинхронность и в этом месте.

Если я не ошибаюсь, то асинхронных конструкторов нет и в других языках. По крайней мере поиск не дал результатов на язык C#.

Да и в остальные языки async/await вкручивали люди с тем же уровнем развития. Иначе бы не вкручивали эту чушь вовсе.

Есть.

await же просто ждёт резолва промиса. Возвращайте из конструктора промис, будет у вас асинхронный конструктор.

var A = class {
  constructor(foo) {
    this._foo = foo;
    return new Promise((resolve) => {
      setTimeout(() => {
        resolve(this);
      }, 1000);
    })
  }
  
  getFoo() {
    return this._foo;
  }
};

(async () => {
  var a = await new A(5);
  console.log(a.getFoo());
})();

А теперь попробуйте унаследоваться от этого класса и посмотрите какой this придёт в конструктор наследника...


Впрочем, в других реализациях наследование тоже не особо работает.

Да, это будет работать. Но выглядит ужасно. И IDE почти наверняка соврёт в подсказках.

А как все-таки связаны приватные и асинхронные конструкторы?

Если я где-то упоминал про асинхронные конструкторы, то процитируйте, пожалуйста, а-то, вроде, всю статью перечитал - не нашёл :D

Под "асинхронным созданием экземпляра класса" я имел ввиду, что может случится примерно следующий кейс:

class Payment {
  constructor(props) {
		this.#props = props;
  }
  
  #props;
  
  static async fromServer(link) {
    const data = await fetch(link).then(res => res.json());

    return new Payment(data);
  }
}

Я думал привести подобный пример, но чувство, что меня за это архитекторы ПО в подворотне испинают :D

Ага, значит тут мы получаем данные платежа с сервера и создаём на их основе объект.


А что если мы получили данные платежа из другого источника? Прочитали из файла? Забили константами, потому что у нас модульный тест? В чём проблема обратиться к конструктору когда у нас есть данные платежа?

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

const key = new Key( data )

И.. это не компилируется, так как функция импорта ключа - асинхронная. И приходится менять конструктор на фабрику:

const key = await Key.from( data )

Функция импорта ключа возвращает CryptoKey. И, скорее всего, именно этот CryptoKey и будет передан в конструктор.


Внимание, вопрос: есть ли какая-то действительно важная причина запрещать прямой вызов конструктора, если у вызывающей стороны уже каким-то чудом есть корректный CryptoKey?

Она принимает 100500 параметров. Собственно для того обёртка и нужна, чтобы не возиться с этими функциями напрямую. И подобное костылеварение ещё долго продолжаться будет, пока все методы высшего порядка и операторы не будут продублированы в асинхронном варианте.

И что дальше? Я всё ещё не понимаю как из 100500 параметров некоторой функции следует необходимость сделать конструктор класса приватным.

Ну а я не вижу причин что либо вообще приватным делать. При чём тут это вооще?

Смотрите, я написал что конструктор не обязательно делать приватным. Вы начали спорить.


Но если вы не видите причин делать что-либо вообще приватным, то о чём вообще спор-то? Какую мысль вы пытаетесь донести?

ВСЁ это — синтаксический сахар. Ничего нового, просто удобства.

Слишком сложно.


Основная проблема приватного конструктора — в том, что класс и его конструктор в js — это одно и то же, и нельзя выставить наружу первое не выставив второго.


Однако, зачем в принципе может понадобиться скрывать конструктор? Рассмотрим всё тот же класс Fahrenheit. Если вызвать его конструктор, то случится … что? Мы успешно преобразуем "фаренгейты" в "фаренгейты"? Звучит как совершенно корректная операция.


С асинхронными "конструкторами" та же самая история. Я ещё не видел ситуаций, когда бы действительно необходимо было скрывать конструктор.


Однако, если скрыть конструктор всё же нужно — почему бы не скрыть весь класс?


class Fahrenheit {
  constructor(value) {
    this.#value = value;
  }

  #value;
}
Fahrenheit.prototype.constructor = null;

export function fromCelsius(value) {
  return new Fahrenheit(value * 9/5 + 32);
}

Звучит на самом деле логично. Я, если честно, сам не встречался с тем, что где-то нужен был приватный конструктор. Может быть я не в правильном сообществе нахожусь (сейчас не про Хабр, не подумайте), но набирает популярность factory method pattern.

С асинхронными конструкторами тоже не встречался

factory method pattern вообще не про это.


Этот паттерн управляет созданием не того класса, в котором объявлен, а требуемых ему зависимостей.

Ой, хорошо, буду знать. Надо будет получше изучить шаблоны проектирования. Спасибо за уточнение

Зачем так сложно? Можно безо всяких декораторов.
class Fahrenheit {
  constructor(value, key) {
    if (Fahrenheit.#key !== key) throw new Error("Using of raw constroctor is not allowed. Use fromCelsius method instead.");
    this.#value = value;
  }
  
  static #key = new Object();
  #value;
  
  static fromCelsius(value) {
    return new Fahrenheit(value * 9/5 + 32, Fahrenheit.#key);
  }
}

А вообще, пример, конечно, притянут за уши. Обычно, приватные конструкторы нужны для реализации синглтонов. Но в JS можно реализовать синглтон и без этого.
Например, вот так
class Singleton {
  constructor(val) {
    if (Singleton.#instance != null) return Singleton.#instance;
    Singleton.#instance = this;
    this.Val = val;
  }

  Val;
  
  static #instance;
}

По вкусу, можно добавить выбрасывание ошибки при повторном явном вызове конструктора, статический метод getInstance, ну и т.д.

На вскидку, сложно сказать, насколько реально нужен приватный конструктор в JS, но он уж точно не является приоритетом. Вот чего не хватает, так это модификатора protected. Уж если начали модификаторы доступа добавлять, не стоило останавливаться на private.

Испытал диссонас. JS сообщество идёт по пути функционального программирования, а новый стандарт дал угла в сторону ООП или я чего-то не понимаю?! Разъясните для новичка пожалуйста.

Во-первых, когда стандарт принимали, так сильно сообщество в сторону ФП ещё не шло.
Во-вторых, что плохого в мультипарадигменности языка программирования?
В-третьих, куча функций — ещё не ФП, и сообщество зашло на путь ФП не так далеко как многие думают.

  1. Это только плюс, когда есть такие возможности. Я с этим солидарен. Однако бытует мнение, что на классах писать не солидно. Особенно это касается React-a. Тип, мол так сообщество не пишет. Хотя моё видение на этот счёт, что надо использовать все возможности языка. Пусть даже код будет чуть длиннее, но зато более читаемый. (речь о классах).

  2. Много читал про это. По этому просто выражу свою благодарность за ваш ответ.

Не уверен что это именно проблема. Отслеживать устаревающие данные нужно в любом случае, и функциональные компоненты хорошо справляются лишь в простейших случаях. Подробности — в комментариях по вашей же ссылке.

Проблема классовых компонентов только одна - они хуже декомпозируются и имеют склонность превращаться в год-обжекты того или иного размера, с довольно низким cohesion. Да и реактовская центральная идея "ui как значение от стейта" более естественно вписывается в функции.

Но, как уже говорил, это относится только к компонентам. Писать логику может быть удобнее в духе ООП (хотя тут субъективно).

И что же тут сложного в декомпозиции классов?

Ну как тут не вспомнить

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории