How to “properly” create a custom object in JavaScript?

后端 未结 15 1969
被撕碎了的回忆
被撕碎了的回忆 2020-11-21 08:07

I wonder about what the best way is to create an JavaScript object that has properties and methods.

I have seen examples where the person used var self = this<

15条回答
  •  礼貌的吻别
    2020-11-21 08:32

    A Pattern That Serves Me Well

    var Klass = function Klass() {
        var thus = this;
        var somePublicVariable = x
          , somePublicVariable2 = x
          ;
        var somePrivateVariable = x
          , somePrivateVariable2 = x
          ;
    
        var privateMethod = (function p() {...}).bind(this);
    
        function publicMethod() {...}
    
        // export precepts
        this.var1 = somePublicVariable;
        this.method = publicMethod;
    
        return this;
    };
    

    First, you may change your preference of adding methods to the instance instead of the constructor's prototype object. I almost always declare methods inside of the constructor because I use Constructor Hijacking very often for purposes regarding Inheritance & Decorators.

    Here's how I decide where which declarations are writ:

    • Never declare a method directly on the context object (this)
    • Let var declarations take precedence over function declarations
    • Let primitives take precedence over objects ({} and [])
    • Let public declarations take precedence over private declarations
    • Prefer Function.prototype.bind over thus, self, vm, etc
    • Avoid declaring a Class within another Class, unless:
      • It should be obvious that the two are inseparable
      • The Inner class implements The Command Pattern
      • The Inner class implements The Singleton Pattern
      • The Inner class implements The State Pattern
      • The Inner Class implements another Design Pattern that warrants this
    • Always return this from within the Lexical Scope of the Closure Space.

    Here's why these help:

    Constructor Hijacking
    var Super = function Super() {
        ...
        this.inherited = true;
        ...
    };
    var Klass = function Klass() {
        ...
        // export precepts
        Super.apply(this);  // extends this with property `inherited`
        ...
    };
    
    Model Design
    var Model = function Model(options) {
        var options = options || {};
    
        this.id = options.id || this.id || -1;
        this.string = options.string || this.string || "";
        // ...
    
        return this;
    };
    var model = new Model({...});
    var updated = Model.call(model, { string: 'modified' });
    (model === updated === true);  // > true
    
    Design Patterns
    var Singleton = new (function Singleton() {
        var INSTANCE = null;
    
        return function Klass() {
            ...
            // export precepts
            ...
    
            if (!INSTANCE) INSTANCE = this;
            return INSTANCE;
        };
    })();
    var a = new Singleton();
    var b = new Singleton();
    (a === b === true);  // > true
    

    As you can see, I really have no need for thus since I prefer Function.prototype.bind (or .call or .apply) over thus. In our Singleton class, we don't even name it thus because INSTANCE conveys more information. For Model, we return this so that we can invoke the Constructor using .call to return the instance we passed into it. Redundantly, we assigned it to the variable updated, though it is useful in other scenarios.

    Alongside, I prefer constructing object-literals using the new keyword over {brackets}:

    Preferred
    var klass = new (function Klass(Base) {
        ...
        // export precepts
        Base.apply(this);  //
        this.override = x;
        ...
    })(Super);
    
    Not Preferred
    var klass = Super.apply({
        override: x
    });
    

    As you can see, the latter has no ability to override its Superclass's "override" property.

    If I do add methods to the Class's prototype object, I prefer an object literal -- with or without using the new keyword:

    Preferred
    Klass.prototype = new Super();
    // OR
    Klass.prototype = new (function Base() {
        ...
        // export precepts
        Base.apply(this);
        ...
    })(Super);
    // OR
    Klass.prototype = Super.apply({...});
    // OR
    Klass.prototype = {
        method: function m() {...}
    };
    
    Not Preferred
    Klass.prototype.method = function m() {...};
    

提交回复
热议问题