Take the 2-minute tour ×
Code Review Stack Exchange is a question and answer site for peer programmer code reviews. It's 100% free, no registration required.

For the sake of learning javascript better and getting used to the Google Chrome Extension Api, I'm currently writing a little Chrome Extension.

To keep things simple and being able to make use of prototypal inheritance I decided to write a little Instantiation/Inheritance Helper before I get started.

But as I am still deep in the process of learning javascript, I would greatly appreciate if anyone could afford the time and take a quick look at the code and clarify some points

  • Are there any Pitfalls I could run into with some of the appraches
  • Is the code, as is, or parts of it considered bad practice some weird constructions or similar
  • Did I miss some important aspects regarding inheritance itself

And of course I would greatly appreciate any kind of feedback that helps me getting a better understanding of javascript/ writing cleaner code.


/* Inheritance Helper*/
var base = (function baseConstructor() {
    'use strict';
    var obj = {
        create: function instantiation() {
            if (this === base) {
                throw new SyntaxError("You can't create instances of base");
            } else if (!this.hasOwnProperty("initclosure")) {
                throw new SyntaxError("Cannot create instances without an constructor");
            } else if (this.singleton && this.instances.length !== 0) {
                throw new SyntaxError("You can't create more than one Instance of a Singleton Class");
            } else {
                var instance = Object.create(this.pub);
                this.init.apply(instance, arguments);
                this.instances.push(instance);
                return instance;
            }
        },
        inherit: function inheritation(specsOpt) {
            specsOpt = specsOpt || {};
            applyDefaults(specsOpt, {
                singleton: false,
                anonymous: false
            });
            var sub = Object.create(this);
            sub.pub = Object.create(this.pub);
            sub.instances = [];
            sub.anonymous = specsOpt.anonymous;
            sub.sup = this;

            if (specsOpt.singleton) {
                sub.singleton = specsOpt.singleton;
                sub.getSingleton = getSingleton;
                protect.call(sub, {
                    singleton: {
                        writable: false,
                        configurable: false,
                        enumerable: false
                    },
                    getSingleton: {
                        writable: false,
                        configurable: false
                    }
                });

            }

            return sub;
        },
        initclosure: function Base() {},
        instances: [],
        pub: {
            instanceOf: function (obj) {
                if (!obj) return this.className;
                return obj.pub.isPrototypeOf(this);
            }
        }

    };

    /* Helper Functions. --- Use function expressions instead of declarations to get JSHint/Lint strict mode violations
     *
     * TODO: Maybe add an obj.helper Propertie with usefull functions
     */

    var applyDefaults = function (target, obj) {
        for (var prop in obj) {
            if (obj.hasOwnProperty(prop)) {
                target[prop] = target[prop] || obj[prop];
            }
        }
    };

    var getSingleton = function () { //To get past the strict violation
        return this.instances[0];
    };
    var protect = function (props, desc) { //Maybe change it a little
        for (var prop in props) {
            if (props.hasOwnProperty) {
                Object.defineProperty(this, prop, props[prop] || desc);
            }
        }
        return this;
    };

    /*  End Helpers
     * 
     *  Protecting
     */

    Object.defineProperty(obj, "init", {
        set: function (fn) {
            if (typeof fn !== "function") {
                throw new Error("Expected typeof init to be 'function'");
            } else if (Boolean(fn.name) === this.anonymous) {
                throw new Error("Expected the constructor " + (!this.anonymous ? "not " : "") + "to be Anonymous");
            }
            if (!this.hasOwnProperty("initclosure")) {
                this.initclosure = fn;
                this.pub.constructor = this.init;
                this.pub.className = fn.name;
                protect.call(this.pub, {
                    constructor: false,
                    className: false
                }, {
                    enumerable: false
                });
            }
        },
        get: function () {
            var that = this;
            var init = function init() {
                if (that.pub.isPrototypeOf(this)) {
                    that.initclosure.apply(this, arguments);
                } else {
                    throw new Error("init can't be called directly");
                }
            };

            init.toString = function () {
                return that.initclosure.toString();
            };
            return init;
        }

    });

    obj.toString = function () {
        return "[class " + (this.initclosure.name || "Class") + "]";
    };

    obj.pub.toString = function () {
        return "[instance " + (this.className || "Anonymous") + "]";
    };

    protect.call(obj, {
        create: false,
        inherit: false,
        toString: false,
        initclosure: {
            enumerable: false
        }
    }, {
        writable: false,
        configurable: false
    });
    protect.call(obj.pub, {
        instanceOf: false,
        toString: false
    }, {
        writable: false,
        configurable: false,
        enumerable: false
    });


    return obj;
})();

Here are some example console outputs as well as a JSBin

var Test = base.inherit();
Test.create(); //Uncaught SyntaxError: Cannot create instances without an constructor



var Test2 = base.inherit();
Test2.init = function () { //Uncaught Error: Expected the constructor not to be Anonymous
  this.is = "a test";
};




var Test3 = base.inherit({singleton:true});
Test3.init = function Test() { 
  this.is = "a test";
};
var instance1 = Test3.create();
console.log(instance1); //{is: "a test"}
console.log(instance1.instanceOf()); //Test
console.log(instance1.instanceOf(Test3)); //true
alert(instance1); //[instance Test]
var instance2 = Test3.create(); //Uncaught SyntaxError: You can't create more than one Instance of a Singleton Class
var instance2 = new Test3.init(); //Uncaught Error: init can't be called directly

Thanks in advance for your effort =)

Edit: Here are a few more examples

var Test4 = base.inherit();

Test4.init = function testingErrorStack(x) {
    this.prop = x.error;
};

var instance1 = Test4.create({
    error: "none"
});
var instance2 = Test4.create({
    error: "alsonone"
});
try {
    var errorInstance = Test4.create();
} catch (e) {
    console.dir(e.stack);
    /*"TypeError: Cannot read property 'error' of undefined
    at init.testingErrorStack */
}

console.log(instance1, instance2, errorInstance); //{"prop": "none"},{"prop": "alsonone"},undefined

You can see the constructor name in the errors stackTrace And you can ofcourse create Multiple Instances

var Test5 = base.inherit({
    anonymous: true
});
Test5.init = function (anotherProp, to) {
    this.anotherProp = anotherProp;
    this.from = "an";
    this.to = to;
};

var Test5_1 = Test5.inherit();
Test5_1.init = function test5SubClass(someProp) {
    Test5_1.sup.init.call(this, "has been passed", "a super class");
    this.sub = someProp;
};

var instance5 = Test5_1.create('"Class"');

console.log(instance5); //{"anotherProp": "has been passed", "from": "an", "sub": "\"Class\"", "to": "a super class"}

As well as do multiple inheritance, and use anonymous constructors too if you want so.

I thought to add these as i got the feeling you might have misunderstood the purpose as an singleton Pattern only helper, don't know for sure if this is the case =)

Oh and isn't this still just a Prototypal approach? I just thought it would be nice to have the benefits of proper Prototypal inheritance through Object.create along with " constructors " to properly initialize the Objects

Here goes the according fiddle

share|improve this question
add comment

1 Answer

Did I miss some important aspects regarding inheritance itself

JavaScript uses prototypal inheritance, which is already quite simple. Here's an example:

function Animal() {
    this.name = 'Animal';
}

Animal.prototype.speak = function() {
    console.log('My name is ' + this.name);
};

var animal = new Animal();
animal.speak(); // My name is Animal

function Cat() {
    this.name = 'Cat';
}

Cat.prototype = new Animal();

var cat = new Cat();
cat.speak(); // My name is Cat

Are there any Pitfalls I could run into with some of the appraches

This whole approach feels quite complicated, and I'm not sure that I understand the benefits of using this over prototypal inheritance. My advice would be to stick with existing patterns which are widely used and understood (e.g. this Singleton pattern).

Is the code, as is, or parts of it considered bad practice some weird constructions or similar

Aside from feeling a little over-engineered, the only other feedback I have is that you don't need to name functions that are assigned to a variable. You could change

var obj = {
    create: function instantiation() {
    } 
};

To

var obj = {
    create: function() {
    } 
};
share|improve this answer
    
The problem with this Approach is that you have to set the prototype of the constructor to an instance of another constructor which could lead to Problems, when initializing more complex constructors which require more parameters to get initialized correctly. You could of course use some intermediate constructors like described e.g in (this)[3site.eu/doc/#intermediate] article, but thats not what makes my heart goes boom. –  C5H8NNaO4 Feb 13 '13 at 14:52
1  
And using named function expression gives you a huge benefit in debugging you applications as the stacktrace can provide you these names in case of errors, another good article to read btw =) Also does the Singleton pattern in the article you linked to use arguments.callee which is forbidden in ES5s strict mode. –  C5H8NNaO4 Feb 13 '13 at 15:06
    
Many thanks for you answer and efforts btw, i really appreciate that =) –  C5H8NNaO4 Feb 13 '13 at 15:26
    
I understand what you're saying, but it still seems to me like you're trying to bend and twist JavaScript into having classical inheritance. Once you "get" prototypal inheritance, you'll see that you can write much more simple code without the need for this sort of helper. If you do prefer classical inheritance though, you might want to check out CoffeeScript. –  WildlyInaccurate Feb 13 '13 at 18:33
    
=) It may be a kind of bending javascript. But to be honest, i don't really have an idea of what classical inheritance really is, i just started programming about 8 Month ago, and 90% of what i did until now is learning javascript through trial/error a little bit of reading articles and answering questions on SO I didn't knew the Answer to. The problem i had with the before ES5 prototypal inheritance, was the above mentioned, and Object.create alone couldn't construct Objects, so i started to kind of mix it. I wrote sth similar 8 month ago, and was being helped on SO. (many thx @Bergi) –  C5H8NNaO4 Feb 13 '13 at 19:28
show 3 more comments

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.