5

I'm having trouble working out how to extend a static function (momentjs) so that I can override the methods, but without altering the original function.

To be clear I know how to extend an instance of moment to override the functions, but I want to extend the library directly so I get my own named instance of moment that I can use in the same way as momentjs.

As an example, I'd like to be able to do the following

extendedMoment().customFunction() //do something custom
extendedMoment().toString() //use customised toString() method
extendedMoment().format() //use the original momentjs method

I've tried a few options with copying the prototype etc, but editing the prototype of the new extendedMoment function seems to affect the original.

Update: answered below by @PatrickRoberts

Zak Henry
  • 2,075
  • 2
  • 25
  • 36

1 Answers1

4

After some digging into the source, you can't really directly extend the library because there are several scoped functions which are not exposed, and moment() is actually a wrapper of a wrapper of a wrapper of a wrapper of the constructor. So here's about the best you can do by reusing the same extended prototype rather than assigning scoped functions inside the factory extension:

function extendedMoment () {
  return Object.setPrototypeOf(moment(), extendedMoment.prototype);
}

Object.setPrototypeOf(extendedMoment.prototype, moment.prototype);
Object.setPrototypeOf(extendedMoment, moment);

extendedMoment.prototype.toString = function toString () {
  return this.format('YYYY-MM-DD');
};

console.log("Original: " + moment().toString());
console.log("Extended: " + extendedMoment().toString());
<script src="http://momentjs.com/downloads/moment.min.js"></script>

The way this works is it replaces the instance's prototype (which is initially Moment.prototype) with extendedMoment.prototype inside the factory extension, reusing the same toString function for all instances of extendedMoment.

EDIT

I caught myself using the term "constructor" for extendedMoment so I corrected myself. It is actually a factory extension, since it is a static function as @ZakHenry pointed out. Apologies for the misnomer.

Patrick Roberts
  • 49,224
  • 10
  • 102
  • 153
  • I tried this, see my updated question with snippet - I just get ` Uncaught TypeError: Cannot read property 'toString' of undefined` – Zak Henry Jan 06 '16 at 05:52
  • @ZakHenry my mistake, I forgot to make my constructor return the object. – Patrick Roberts Jan 06 '16 at 05:58
  • Updated, no error now but the override does not seem to have take effect. I suspect that momentjs has something sneaky implemented to prevent constructor extension maybe? – Zak Henry Jan 06 '16 at 06:01
  • 1
    Not to prevent, they just don't extend objects by using the prototype chain as javascript had intended. I'm working on an update. – Patrick Roberts Jan 06 '16 at 06:03
  • awesome work, thanks a lot. I'm kinda glad to see that it was more complex than first glance. Also note for others that might see this - earlier versions of momentjs (I had 2.10.6, current is 2.11.0) don't work with this answer. I guess they changed the api somewhere. I'd better add a unit test to check this override always works. – Zak Henry Jan 06 '16 at 06:55