Source: Instantiation.js

/**
 * Instantiation Feature
 */
/*global dessert, troop */
(function () {
    "use strict";

    var Memoization = troop.Memoization,
        Surrogate = troop.Surrogate,
        Base = troop.Base;

    troop.Base.addMethods(/** @lends troop.Base */{
        /**
         * Creates a new instance of the class it was called on. Arguments passed to .create will be handed over
         * to the user-defined .init method, which will decorate the new instance with properties.
         * Class must implement .init method in order to be instantiable.
         * Instantiation might return an existing instance of the same class if the class is memoized.
         * @see troop.Base.setInstanceMapper
         * Instantiation might create a new instance of a subclass if the current class has surrogates.
         * @see troop.Base.addSurrogate
         * @example
         * var MyClass = troop.extend({
         *         init: function (foo) {
         *            this.foo = 'bar';
         *         }
         *     }),
         *     myInstance = MyClass.create("bar");
         * myInstance.foo // "bar"
         * @returns {troop.Base}
         */
        create: function () {
            var isMemoized = this.instanceMapper,
                instanceKey,
                result;

            // attempting to fetch memoized instance
            if (isMemoized) {
                instanceKey = Memoization.mapInstance.apply(this, arguments);
                result = Memoization.getInstance.call(this, instanceKey);
                if (result) {
                    return result;
                }
            }

            // instantiating class or surrogate
            var self = this.surrogateInfo ?
                    Surrogate.getSurrogate.apply(this, arguments) :
                    this,
                that = Base.extend.call(self);

            // initializing instance properties
            if (typeof self.init === 'function') {
                // running instance initializer
                result = self.init.apply(that, arguments);

                if (typeof result === 'undefined') {
                    // initializer returned nothing, returning new instance
                    result = that;
                } else if (!(result !== self && self.isPrototypeOf(result))) {
                    // initializer did not return a valid instance
                    // (instance of the same or derived class)
                    dessert.assert(false, "Unrecognizable value returned by .init().", result);
                }
            } else {
                dessert.assert(false, "Class implements no .init() method.");
            }

            // storing instance for memoized class
            if (isMemoized) {
                Memoization.addInstance.call(self, instanceKey, result);
            }

            return result;
        }
    });
}());