2

I am trying to extend a class in javascript and add some extra functions to that class.

const Pino = require('pino');

class Logger extends Pino {
  constructor(options = { name: 'isWorking' }) {
    super(options);
    this.check = "1234";
    this.ppp = () => {}
  }

  rrr() {}
}

let pp = new Logger();
console.log(pp.check)
console.log(pp.ppp)
console.log(pp.rrr)
console.log(pp instanceof Logger)
console.log(pp instanceof Pino)

This is the output

1234
[Function]
undefined
false
false

Why is that rrr function is not exposed ? i thought of overriding the base class. This should be working for normal class, what special about this Pino class ?

Note: Here pino base class is javascript module

Thanks,

Sathish
  • 2,056
  • 3
  • 26
  • 40

2 Answers2

4

Note: I am a maintainer of Pino.

The function returned by require('pino') is a factory function not a "constructor" function. As such, the class sugar syntax will not work for customizing the Pino prototype: we do not expose the prototype.

To customize Pino one would wrap an instance with their own functionality:

const instance = require('pino')()
Object.defineProperties(instance, {
  rrr: {
    value: function () {
      this.info('this is the rrr function')
    }
  }
})

instance.rrr()

I highly recommend learning about the way objects in Javascript really work instead of relying on the (in my opinion disastrous) class syntax. The Two Pillars of Javascript is an excellent set of articles on the subject. And the You Don't Know JS: this & Object Prototypes is a great in-depth book on the subject.

James Sumners
  • 14,485
  • 10
  • 59
  • 77
2

The reason is that the definition of Pino (pino in the module) does not return an instance of pino but a synthesised object -- it has a return instance statement at the end.

Although that returned object has received a specific prototype, it is not pino.prototype.

So when an instance of Logger is created, it also returns this "foreign" object while rrr remains "orphaned" on the unused Logger.prototype.

See this simplified demo:

function Pino() { return new Date(); }

class Logger extends Pino {
    rrr() { return 1; }
}

const a = new Pino;
const b = new Logger;

console.log(a instanceof Pino); // false
console.log(b instanceof Logger); // false
console.log(b instanceof Pino); // false
console.log(b instanceof Date); // true

console.log(typeof b.rrr); // undefined

If pino would just return a proper instance (this), then rrr would be available:

function Pino() {  } // returns `this`

class Logger extends Pino {
    rrr() { return 1; }
}

const a = new Pino;
const b = new Logger;

console.log(a instanceof Pino); // true
console.log(b instanceof Logger); // true
console.log(b instanceof Pino); // true
console.log(b instanceof Date); // false (obviously)

console.log(typeof b.rrr); // function

Ugly work around

If you really need the extension, then it can be done like this, but it would be better to ask the library author to make their classes extensible:

function Pino() { return new Date(); }

function PinoClass() {} // Don't define anything in this class
PinoClass.prototype = Object.getPrototypeOf(new Pino);

class Logger extends PinoClass {
    rrr() { return 1; }
}

const a = new Pino;
const b = new Logger;

console.log(a instanceof Pino); // false
console.log(b instanceof Logger); // true
console.log(b instanceof Pino); // false
console.log(b instanceof Date); // true

console.log(typeof b.rrr); // function
trincot
  • 317,000
  • 35
  • 244
  • 286
  • Hi trincot, can you please explain more about this or point me to alink or something. If possible may i know to extend that module in any other way ? – Sathish Jan 12 '19 at 11:11
  • 1
    Even if it was `pino.prototype` (which would help with `instanceof pino`), that wouldn't allow extending the "class". – Bergi Jan 12 '19 at 11:11
  • 1
    @Sathish I would recommend to open a Github issue with a feature request to make the library extensible. The authors will tell you how best to do it, tell you not to do it at all but use a different mechanism for customisation, or might release a new version of the library that does allow subclassing. – Bergi Jan 12 '19 at 11:13
  • I added some snippets. – trincot Jan 12 '19 at 11:18
  • 1
    Trincot thanks for the detailed example. Can you please also upvote the question. i will make sure create a issue on github based on this discussion. – Sathish Jan 12 '19 at 11:19
  • @Sathish, I added a work-around to my answer. But it's ugly. – trincot Jan 12 '19 at 12:06