How to define private constructors in javascript?

前端 未结 4 1209
旧巷少年郎
旧巷少年郎 2020-12-25 15:09

I have defined pure objects in JS which expose certain static methods which should be used to construct them instead of the constructor. How can I make a constructor for my

相关标签:
4条回答
  • 2020-12-25 15:13

    One can use a variable (initializing) inside a closure which can throw an error if the constructor was called directly instead of via a class method:

    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;
    })();
    
    0 讨论(0)
  • 2020-12-25 15:19

    Simply don't expose the constructor function. The core issue with the original code is the "static method" is defined as a property of the constructor (which is used as a "class") as opposed a property of the module.

    Consider:

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

    The constructor can still be accessed via .constructor, but .. meh. At this point, might as well just let a "clever user" have access.

    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's comment for an alternative. */
            s.constructor = undefined;
            return s;
        }
    };
    
    0 讨论(0)
  • 2020-12-25 15:21

    Another possible simple approach is to use predicate function instead of instanceof. For typescript it can be a type guard and type synonym instead of a class can be exported:

    // class is private
    class _Score {
      constructor() {}
    }
    
    export type Score = _Score
    
    export function isScore(s): s is Score {
      return s instanceof _Score
    }
    
    0 讨论(0)
  • 2020-12-25 15:30

    Is there a solution which will allow me to say x instanceof Score?

    Yes. Conceptually, @user2864740 is right, but for instanceof to work we need to expose (return) a function instead of a plain object. If that function has the same .prototype as our internal, private constructor, the instanceof operator does what is expected:

    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 (…)
    
    0 讨论(0)
提交回复
热议问题