1

I typically see examples of the revealing prototype pattern as shown in syntax #2 below, but i find syntax #1 more consistent. Is there anything different about them other than syntax? Functionally, performance wise, or other?

syntax #1:

function MyClass1(name){
    this.name = name;
}
MyClass1.prototype = new function () {
    var static = 0;

    var getStatic = function () {
        return static;
    }
    this.incStatic = function () {
        static++;
        return getStatic.call(this);
    }
    this.constructor = MyClass1
};

exactly the same as this #2:

function MyClass2(name){
    this.name = name;
}
MyClass2.prototype = function () {
    var static = 0;

    function getStatic () {
        return static;
    }
    function incStatic() {
        static++;
        return getStatic.call(this);
    }
    return {
        incStatic:incStatic,
        constructor:MyClass2
    };
}();

Here is a fiddle demonstrating the exact same behaviors: http://jsfiddle.net/arctelix/FSk8z/

It appears that both syntax have exactly the same outcome. However, I have never seen an example as shown in #1, so i have to wonder why? For me # 1 is just a more constant syntax and i hate having to identify public members in a special return block.

Arctelix
  • 4,478
  • 3
  • 27
  • 38
  • 1
    Regarding performance, you test this yourself: http://jsperf.com/. Regarding outcome: In the first example, the resulting object doesn't directly inherit from `Object.prototype` but from the prototype of the anonymous function. – Felix Kling Mar 02 '14 at 18:18
  • @FelixKling You are absolutely correct about the prototype chain and thanks for turning me on to jsperf. Turns out #1 is a tad slower by 0% -25%. Any idea how to achieve the syntax of #1 without the performance hit? Do you think this is a bad idea? – Arctelix Mar 02 '14 at 19:30
  • possible duplicate of [Constructor function vs Factory functions](http://stackoverflow.com/questions/8698726/constructor-function-vs-factory-functions) – Qantas 94 Heavy Mar 03 '14 at 04:48

2 Answers2

0

Personally I consider it improper to reassign something.prototype. Instead, extend the prototype:

(function() {
    var static = 0;
    MyClass.prototype.getStatic() {return static;}
    MyClass.prototype.incStatic() {return static++;}
})();
Niet the Dark Absol
  • 320,036
  • 81
  • 464
  • 592
0

This whole thing came about because i am creating an MVC framework that utilizes a generic Class constructor. I ran these variations in the larger context of the Class constructor with two different implementation methods. Method 1 reassigns the prototype where method 2 extends it. Method 1 has a uniform prototype chain where method 2 adds a function prototype on top of the object prototype for syntax #1. The performance is about equal for both method 1 and 2.

Blue & Red = syntax #1.

Teal & Green = syntax #2.

Yellow & purple = a variation on syntax #2.

enter image description here

var Class = function (methods, options) {
    //allow for Proper class name to show up in browser devtools
    options = options || {}
    var debug = options.debug || false
    var protoTest = options.protoTest || 0
    var pInternal = options.pInternal || true
    var klassName = methods.constructor.name
    console.log('------protoTest =', protoTest, '/ debugClass =', debug, '/ pInternal =', pInternal, '/ klassName = ',klassName)

    //compile the constructor & internalMembers
    var Class = function () {
        //console.log('Class() is building:', !(init instanceof init))
        //provide inernal object for constructor
        if (pInternal) this.internal = {}
        this.constructor.apply(this, arguments);
        //remove internal from public scope
        if (pInternal){
            var int = this.internal
            delete this.internal
        }
        //populate self with this and internal vars
        if (pInternal){
            var self = {pub:this, int:{}};
            for (var v in int){
                self.int[v] = int[v];
            }
        }else var self = this
        // Instantiate internalMembers with self
        var include = methods.include;
        if (include) include.call(this, self);
    };

    //create constructor function with className (fixes class name in debugger)
    if (debug == true && klassName) {
        var klass = new Function("init", "return function " + klassName + "(){ init.apply(this,arguments) };")(Class);
    }else var klass = Class


    console.log('---type', typeof methods.prototype)
    if (typeof methods.prototype == 'object'){
        //must use traditional revealing prototype
        var prototype = methods.prototype;
        if (protoTest==0){
            //overides prototype
            if (prototype) klass.prototype = prototype;
        }else{
            //does not overide prototype
            for (var p in prototype) klass.prototype[p] = prototype[p]
        }
    }
    //create prototype from Class method
    //----------------test 0
    else if (protoTest==0){
        //overides prototype (new has extra proto in chain)
        var prototype = methods.prototype;
        if (prototype) klass.prototype = new prototype();
    }
    //----------------test 1
    else if (protoTest == 1){
        //does not overide prototype and has uniform chain
        var pms = new methods.prototype()
        for (var p in pms) klass.prototype[p] = pms[p]
    }
    //----------------end test

    //add other Class methods to prototype
    var exclude = ['include', 'initialize', 'prototype'];
    for (var property in methods) {
        if (exclude.indexOf(property) == -1) {
            klass.prototype[property] = methods[property];
        }
    }

    return klass; //return the class
};

All the tests: http://jsperf.com/revealing-proto-test/4

The fiddle: http://jsfiddle.net/arctelix/Cp4nG/

There is a debug mode with tests as well: http://jsperf.com/revealing-proto-test/3

BenMorel
  • 34,448
  • 50
  • 182
  • 322
Arctelix
  • 4,478
  • 3
  • 27
  • 38