Question

I have my own jquery plugin for a date picker control that is rendered with a MVC HtmlHelper extension method. Its for dates in the format dd/mm/yyyy (Australian date).

To make it easier construct a javascript date object from the input <input type=text .../>, I overrode the constructor as follows:

// Save the default constructor 
var _DefaultDateConstructor = Date.prototype.constructor;
// Constructor
Date = function () {
  var date = new _DefaultDateConstructor();
  if (arguments === undefined || arguments.length === 0) {
    // Ensure an invalid date is returned
    date.setDate(Number.NaN);
    return date;
  }
  // Get the arguments
  var args = arguments[0].toString().split(/[\/ ]/);
  // Set date properties
  date.setMilliseconds(0);
  date.setSeconds(0);
  date.setMinutes(0);
  date.setHours(0);
  date.setDate(parseInt(args[0], 10));
  date.setMonth(parseInt(args[1], 10) - 1);
  date.setFullYear(parseInt(args[2], 10));
  return date;
}

and then extended the prototype

Date.prototype.isValid = function () {
  return !isNaN(this.getTime());
}

Date.prototype.clone = function () {
  return new _DefaultDateConstructor(this.getTime());
}

// More methods to return day name, month name etc. for formatting the display.

Which allows me do do this:

this.selectedDate = new Date(this.input.val());

where this.selectedDate is a valid javascript date I can work with, and this.input posts back correctly to my controller.

When working in a test js file, all worked as intended, but when I copied the code into the plugin file, it all crashed and burned. After much hair pulling, I realised that I had cut and pasted the constructor before the methods. Once I put the constructor after the methods (as per the test file) it started working again.

My first question is why was I getting undefined is not a function errors? Surely (but obviously not!) the overridden constructor is returning a javascript date object so the prototype methods should work if declared after the constructor.

My second related question is, is there a way to have the overridden constructor 'private' to the plugin. If the plugin is loaded and you enter myDate = new Date() in the console, it returns Invalid Date (which is what I want for the plugin) but would be confusing for another user of the plugin.

Was it helpful?

Solution

If you want a different behavior just for use in your plugin, then rather than replace the Date constructor, why don't you inherit from the Date() object with your own constructor and just use that constructor when you want the different behavior.

So, you'd leave the Date() constructor unchanged so everyone else would get the normal Date behavior. And, then you'd have your own constructor (call it ValidDate() or whatever you want) that you would use when you want the special behavior.

This is the more normal object oriented behavior. Inherit from an existing object when you want a derived/special behavior while still leaving the original object untouched for normal usage.


Also, it looks like your constructor only works if you pass it a / separated string. If any of the other forms of legal arguments to the Date() constructor are used such as what you use in your .clone() method, then you will try to process it as a string. It looks to me like you should test if the constructor argument is a string and only give it special processing if it is. That way, other legal constructor arguments such as passing in the getTime() result from another Date object will still continue to work.


Here's one way to do it that deals with the limitations/oddities of the Date() object:

function ValidDate(a,b,c,d,e,f,g) {
    var date;
    if (arguments.length === 0) {
        // your special behavior here
        date = new Date();
    }
    else if (arguments.length === 1) {
        if (typeof a === "string") {
           // add your own constructor string parsing here
        } else {
            // normal default processing of single constructor argument
            date = new Date(a);
        }
    } else {
        // must be this form: new Date(year, month, day, hour, minute, second, millisecond);
        // the picky Date() constructor doesn't handle undefined
        // for an argument so we pass zero for any missing argument
        c = c || 0;
        d = d || 0;
        e = e || 0;
        f = f || 0;
        g = g || 0;
        date = new Date(a,b,c,d,e,f,g);
    }

   // add custom methods to this Date object
   date.isValid = function() {
       return !isNaN(this.getTime());
   }

   date.clone = function() {
       return ValidDate(this.getTime());
   }

   return date;
}

Working example: http://jsfiddle.net/jfriend00/B25LX/

OTHER TIPS

My first question is why was I getting undefined is not a function errors? Surely (but obviously not!) the overridden constructor is returning a javascript date object so the prototype methods should work if declared after the constructor.

If you override the constructor first, the prototype methods are added to the new constructor's prototype. But the constructor itself returns a regular Date object, which has a different prototype without those methods.

My second related question is, is there a way to have the overridden constructor 'private' to the plugin. If the plugin is loaded and you enter myDate = new Date() in the console, it returns Invalid Date (which is what I want for the plugin) but would be confusing for another user of the plugin.

No, there isn't. Overriding the Date constructor means overriding a global variable.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top