Как определить частные конструкторы в javascript?

Я определил чистые объекты в JS, которые выставляют определенные статические методы, которые должны использоваться для их построения вместо конструктора. Как я могу создать конструктор для моего класса private в Javascript?

var Score = (function () {

  // The private constructor
  var Score = function (score, hasPassed) {
      this.score = score;
      this.hasPassed = hasPassed;
  };

  // The preferred smart constructor
  Score.mkNewScore = function (score) {
      return new Score(score, score >= 33);
  };

  return Score;
})();

Обновить. Решение должно позволить мне протестировать x instanceof Score. В противном случае работает решение @user2864740 об экспонировании только статического конструктора.

Ответ 1

Есть ли решение, которое позволит мне сказать x instanceof Score?

Да. Понятно, что @user2864740 прав, но для instanceof для работы нам нужно выставить (return) функцию вместо простого объекта. Если эта функция имеет тот же .prototype, что и наш внутренний частный конструктор, оператор instanceof делает то, что ожидается:

var Score  = (function () {

  // the module API
  function PublicScore() {
    throw new Error('The constructor is private, please use Score.makeNewScore.');
  }

  // The private constructor
  var Score = function (score, hasPassed) {
      this.score = score;
      this.hasPassed = hasPassed;
  };

  // Now use either
  Score.prototype = PublicScore.prototype; // to make .constructor == PublicScore,
  PublicScore.prototype = Score.prototype; // to leak the hidden constructor
  PublicScore.prototype = Score.prototype = {…} // to inherit .constructor == Object, or
  PublicScore.prototype = Score.prototype = {constructor:null,…} // for total confusion :-)

  // The preferred smart constructor
  PublicScore.mkNewScore = function (score) {
      return new Score(score, score >= 33);
  };

  return PublicScore;
}());

> Score.mkNewScore(50) instanceof Score
true
> new Score
Error (…)

Ответ 2

Можно использовать переменную (initializing) внутри замыкания, которая может вызвать ошибку, если конструктор был вызван напрямую, а не через метод класса:

var Score = (function () {
  var initializing = false;

  var Score = function (score, hasPassed) {
      if (!initializing) {
         throw new Error('The constructor is private, please use mkNewScore.');
      }

      initializing = false;
      this.score = score;
      this.hasPassed = hasPassed;
  };

  Score.mkNewScore = function (score) {
      intializing = true;
      return new Score(score, score >= 33);
  };

  return Score;
})();

Ответ 3

Просто не выставляйте функцию конструктора. Основной проблемой с исходным кодом является "статический метод", который определяется как свойство конструктора (которое используется как "класс" ), в отличие от свойства модуля.

Рассмотрим:

return {
    mkNewScore: Score.mkNewScore
    // .. and other static/module functions
};

Доступ к конструктору можно получить через .constructor, но.. meh. На данный момент, возможно, просто позвольте "умному пользователю" получить доступ.

return {
    mkNewScore: function (score) {
        var s = new Score(score, score >= 33);
        /* Shadow [prototype]. Without sealing the object this can
           be trivially thwarted with `del s.constructor` .. meh.
           See Bergi comment for an alternative. */
        s.constructor = undefined;
        return s;
    }
};