Search code examples
javascriptnode.jswinston

In a constructor, set the constructed object to another


To help in my logging, I created a convenience function that create a winston.Logger and set it with the correct transports, and return that logger.

Everything works fine, but ESLint is complaining I am calling what it see as a constructor without using new.

While I could just ignore the warning, I like to have strict rules, and seeing const logger = Logger('foo') is indeed confusing.

Renaming Logger to something like getLogger seems ugly to me, but ESLint don't complain.

What I thought was to make Logger a constructor, that directly point to the result of new winston.Logger.

Obviously, this = new winston.Logger didn't worked, but I am pretty sure there is a clean way to do this.

Current snippet of log.js:

module.exports = (file) => {
    let transports = getTransports(file),
        logger = new (winston.Logger)({
            rewriters: [
                (level, msg, meta) => {
                    meta.app = file + '.js';
                    return meta;
                }
            ],
            transports: transports
        });
    return logger;
    // this = logger;
};

Use:

'use strict';

const Logger = require('./log.js'),
    logger = Logger('foo');

logger.debug('foo');

Solution

  • As you probably know, the warning is there because functions with initially-capped names are normally constructor functions in JavaScript.

    If you want, you can call your existing Logger with new. That will create and throw away an object, but that's harmless. It works because if a constructor function returns a non-null object reference, the result of new is that object reference instead of the object created by new. Your function returns logger, which is a non-null object reference, so it'll work. (If it were me, I'd probably call it getLogger and just call it normally, to make it clear what it's doing, but that's a matter of style and you've said you don't want to, which is fair enough.)

    Here's a simpler example to demonstrate this behavior of new and constructor functions:

    var obj = {
      note: "I'm the one object Singleton always returns"
    };
    function Singleton() {
      this.note = "This object is thrown away, so you never see this object";
      return obj;
    }
    function Normal() {
      this.note = "This object isn't thrown away";
    }
    
    var o1 = Singleton();
    console.log("o1", o1);
    var o2 = new Singleton();
    console.log("o1 === o2? ",  o1 === o2);  // true
    console.log("o1 === obj?", o1 === obj); // true
    var n1 = new Normal();
    console.log("n1", n1);
    var n2 = new Normal();
    console.log("n1 === n2?", n1 === n2);   // false