6

I have written the following code to implement logging in a separate js file logger.js by using OOP.

var console;

function Logger() {
    init();
}

var init = function() {
    if(!window.console){ 
        console = {
            log: function(message){},
            info: function(message){},
            warn: function(message){},
            error: function(message){}
        }; 
    } else {
        console = window.console;
    }
};

Logger.prototype.log = function(message) {
    console.log(message);    
}

Logger.prototype.logInfo = function(message) {
    console.info(message);
}

Logger.prototype.logWarn = function(message) {
    console.warn(message);
}

Logger.prototype.logError = function(message) {
    console.error(message);
}

I am using it from another js file, site.js as:

var logger = new Logger(); //global variable

var getComponentById = function(id) {
    var component = null;

    if(id) {
        try {
            component = AdfPage.PAGE.findComponentByAbsoluteId(id);
        }catch(e){
            logger.logError(e);
        }
    }

    return component;
}

I was wondering

  • If I have implemented the Logger class in proper way, by maintaining OOP of JavaScript.
  • Will it handle the scenario where the browser don't have any console?
  • How can I make init() method inaccessible from other js file or method? I mean how can I make it private?

Any pointer would be very helpful to me.

Update

From another SO thread I found information about private method and I changed my approach:

function Logger() {
    init();
}

Logger.prototype = (function() {
    var console;

    var init = function() {
        if(!window.console){ 
            this.console = {
                log: function(message){},
                info: function(message){},
                warn: function(message){},
                error: function(message){}
            }; 
        } else {
            this.console = window.console;
        }
    };

    return {
        constructor: Logger,

        log: function(message) {
            this.console.log(message);    
        },

        logInfo: function(message) {
            this.console.info(message);
        },

        logWarn: function(message) {
            this.console.warn(message);
        },

        logError: function(message) {
            this.console.error(message);
        }
    };
})();

But in this case I am getting error that init is not defined.

Community
  • 1
  • 1
Tapas Bose
  • 28,796
  • 74
  • 215
  • 331
  • "in a separate JS file.." it sounds like you are saying you are actually to commit the log entries into a JS file via JS – Ryan Jul 14 '13 at 17:48
  • 1
    rename all instances of "console" to something else such as "console2" to test the browser behavior if it wasn't defined – Thalaivar Jul 14 '13 at 17:50

1 Answers1

3

To answer your questions:

  • your implementation of the class is a bit odd. You're accessing the console variable with a closure, having it as a property on the Logger makes more sense.
  • if the browser has no console, you wont get an error (but the logger wont do anything)
  • To make you init function private you could wrap it in an IIFE (immediately invoked function expression)

I took your code and changed it slightly to come up with this:

// Create the Logger function with an IIFE, this keeps all of the private
// variables out of the global scope, the only thing in the global scope
// is the function returned by the IIFE.
var Logger = (function (w) {
    var Logger,
        DummyConsole;

    DummyConsole = function () {
        this.log = function (message) {
            alert(message);
        };
        this.info = function (message) {
            // Implement however you want.
        };
        this.warn = function (message) {
            // ... 
        };
        this.error= function (message) {
            // ...
        };
    };

    Logger = function () {
        if (!w.console) {
            this.console = new DummyConsole();
        } else {
            this.console = w.console;
        }
    };

    Logger.prototype.log = function(message) {
        this.console.log(message);    
    };

    Logger.prototype.logInfo = function(message) {
        this.console.info(message);
    };

    Logger.prototype.logWarn = function(message) {
        this.console.warn(message);
    };

    Logger.prototype.logError = function(message) {
        this.console.error(message);
    };

    return Logger;
}(window));

// create a logger instance to check that the Logger class logs to the console.
var a = new Logger();
a.log("hello");

// Remove the console.
window.console = null;

// Create a new logger checking that it falls back to the dummy console implementation.
var b = new Logger();

// An (annoying) alert is shown.
b.log("Hi");

Code is available as a JSFiddle here: http://jsfiddle.net/mtufW/

RobH
  • 3,604
  • 1
  • 23
  • 46
  • Thanks Rob, I have one doubt. You have used this.console, but this console field is not declared anywhere. I have seen the working example. I was wondering how this console is working? – Tapas Bose Jul 14 '13 at 17:51
  • window object is passed to the function. Check this piece of code Logger = function () { if (!w.console) { this.console = new DummyConsole(); } else { this.console = w.console; } }; – Thalaivar Jul 14 '13 at 17:57
  • Yes I have seen this code before. My concern is, if you use `this.console`, then there would be something `var console;` defined in the class scope. But it is not, although the code is working fine. My question is how? We don't need to declare `var console` somewhere? – Tapas Bose Jul 14 '13 at 18:04
  • 2
    You are defining `console` on the `this` object. Which in the context of a constructor is the newly created object. Similar to `var a = {}; a.someProp = "hello";`. You don't need to `var someProp` as you are defining it on an object. – RobH Jul 14 '13 at 18:07
  • Another small doubt, do I have to instantiate `Logger` everytime whenever I use it, or I can define it once, in my site.js as global variable and access it from any method as needed? I saw in you example that you have instantiated it twice, is the reason that before the second instance you are setting `window.console=null`? – Tapas Bose Jul 14 '13 at 18:15
  • 1
    @TapasBose - yes you're right: you can just define it once, the second time was just to prove that if the console wasn't available it would use the internal implementation instead. – RobH Jul 14 '13 at 18:56