Javascript. Авто инициализация объектов

Предположим, что нам необходимо после загрузки документа проинициализировать создаваемый объект. А вообще, зачем это может потребоваться? Если попытаться выполнить следующий участок кода, то мы обнаружим, что мы не можем напрямую обратиться к объекту App.

var App = (function() {
    (function(){
        console.log(App); // undefined
    })();
    return {
        version: 0
    };
})();


 Все правильно: пока объект не реализован, мы не можем обратиться к нему. Конечно, можно поступить проще, например, расположить метод инициализации ниже:

var App = (function() {
    return {
        version: 0
    }
})();

(function(){
    console.log(App); // App object
})();


Но в этом случае нарушается логическая связка с реализацией объекта, поэтому этот вариант мы отбрасываем. Далее рассмотрим возможные реализации инициализации на примере стека. Вот один из способов, правда он основан на использовании библиотеки jquery.

var Stack = (function(){

    var init = function() {
        try {
            for (var i = 0; i < 10; i++) {
                Stack.push(i);
            }
        } catch(e) {
            console.log(e.message);
        }
    };

    $(document).ready(function(){
        init();
    });
   
    return {
        size: 1024,
        pointer: 0,
        storage: [],
        push: function(item) {
            if (this.pointer + 1 > this.size) {
                throw new Error("Stack is full");
            }
            else {
                this.storage.push(item);
                this.pointer++;
            }
        },
        pop: function() {
            if (this.pointer == 0) {
                throw new Error("Stack is empty");
            }
            else {
                return this.storage[--this.pointer];
            }
        },
        reset: function() {
            this.storage = [];
            this.pointer = 0;
        }
    };
   
})()


Идея, заключается в том, что сама инициализация начинается только после полной загрузки документа, плюсом данной идеи также является и то, что у нас появляется уверенность в готовности и всех остальных объектов, которые так или иначе могут нам понадобиться. В случае, если мы не собираемся использовать jquery, тогда можно поступить следующим образом

var Stack = (function(){

    var init = function() {
        try {
            for (var i = 0; i < 1025; i++) {
                Stack.push(i);
            }
        } catch(e) {
            console.log(e.message); // Stack is full
        }
    };

    if (document.addEventListener) {
        document.addEventListener("DOMContentLoaded", init, false);
    }
    else if (document.attachEvent) {
        document.attachEvent("onreadystatechange", init);
    }
   
    return {
        size: 1024,
        pointer: 0,
        storage: [],
        push: function(item) {
            if (this.pointer + 1 > this.size) {
                throw new Error("Stack is full");
            }
            else {
                this.storage.push(item);
                this.pointer++;
            }
        },
        pop: function() {
            if (this.pointer == 0) {
                throw new Error("Stack is empty");
            }
            else {
                return this.storage[--this.pointer];
            }
        },
        reset: function() {
            this.storage = [];
            this.pointer = 0;
        }
    };
   
})()


Здесь мы воспользовались обработчиками событий загрузки документа для различных браузеров (document.addEventListener, document.attachEvent), эти же обработчики использованы в реализации самого метода ready в jquery. Есть еще один способ авто инициализации

var Stack = (function(){

    var init = function() {
        try {
            for (var i = 0; i < 10; i++) {
                Stack.push(i);
            }
        } catch(e) {
            console.log(e.message);
        }
    };

    setTimeout(function() {
        init();
    }, 0);
   
    return {
        size: 1024,
        pointer: 0,
        storage: [],
        push: function(item) {
            if (this.pointer + 1 > this.size) {
                throw new Error("Stack is full");
            }
            else {
                this.storage.push(item);
                this.pointer++;
            }
        },
        pop: function() {
            if (this.pointer == 0) {
                throw new Error("Stack is empty");
            }
            else {
                return this.storage[--this.pointer];
            }
        },
        reset: function() {
            this.storage = [];
            this.pointer = 0;
        }
    };
   
})()


или вот так

var Stack = {
    size: 1024,
    pointer: 0,
    storage: [],
    push: function(item) {
        if (this.pointer + 1 > this.size) {
            throw new Error("Stack is full");
        }
        else {
            this.storage.push(item);
            this.pointer++;
        }
    },
    pop: function() {
        if (this.pointer == 0) {
            throw new Error("Stack is empty");
        }
        else {
            return this.storage[--this.pointer];
        }
    },
    reset: function() {
        this.storage = [];
        this.pointer = 0;
    },
    init: (function() {
        var init = function() {
            try {
                for (var i = 0; i < 10; i++) {
                    Stack.push(i);
                }
            } catch(e) {
                console.log(e.message);
            }
            return 0;
        };
        setTimeout(function() {
            init();
        }, 0);
    })()
};


Использование таймера в авто инициализации также дает возможность обращаться к объекту. Для сравнения приведенных выше методов, приведу следующий качественный тест:

var start = new Date().getTime();
$(document).ready(function(){
    console.log('ready: ' + (new Date().getTime() - start) + ' ms');
});
setTimeout(function() {
    console.log('timer: ' + (new Date().getTime() - start) + ' ms');
}, 0);
console.log('first: ' + (new Date().getTime() - start) + ' ms');
console.log('last: ' + (new Date().getTime() - start) + ' ms');


Результат этого теста выглядит следующим образом:

// FF
first: 1 ms
last:  2 ms
timer: 9 ms
ready: 12 ms

// Chrome
first: 0 ms
last:  0 ms
ready: 4 ms
timer: 23 ms

// Opera
first: 0 ms
last:  0 ms
ready: 0 ms
timer: 23 ms

// IE
first: 0 ms
last:  0 ms
ready: 19 ms
timer: 26 ms


В целом по скорости быстрее начинает выполнение метод ready, в то время как setTimeout запускается позже, это справедливо для IE, Opera, Chrome, а FF показал другие результаты, видимо это зависит от их движка. У вас могут получиться другие значения, но в целом картина схожа.