如何扩展Javascript Date对象?

evi*_*ery 22 javascript inheritance date node.js

我正在尝试子类化/扩展本机Date对象,而不修改本机对象本身.

我试过这个:

    var util = require('util');

    function MyDate() {
        Date.call(this);
    }
    util.inherits(MyDate, Date);

    MyDate.prototype.doSomething = function() {
        console.log('Doing something...');
    };        

    var date = new MyDate();
    date.doSomething();

    console.log(date);
    console.log(date.getHours());
Run Code Online (Sandbox Code Playgroud)

还有这个:

function MyDate() {

    }

    MyDate.prototype = new Date();

    MyDate.prototype.doSomething = function() {
        console.log("DO");
    }

    var date = new MyDate();
    date.doSomething();
    console.log(date);
Run Code Online (Sandbox Code Playgroud)

在这两种情况下,date.doSomething()工作,但当我调用任何本地方法,如date.getHours()甚至console.log(date),我得到'TypeError:这不是一个Date对象.

有任何想法吗?还是我坚持扩展顶级Date对象?

Geo*_*ell 19

查看v8代码,在date.js中:

function DateGetHours() {
  var t = DATE_VALUE(this);
  if (NUMBER_IS_NAN(t)) return t;
  return HOUR_FROM_TIME(LocalTimeNoCheck(t));
}
Run Code Online (Sandbox Code Playgroud)

看起来像DATE_VALUE是一个执行此操作的宏:

DATE_VALUE(arg) = (%_ClassOf(arg) === 'Date' ? %_ValueOf(arg) : ThrowDateTypeError());
Run Code Online (Sandbox Code Playgroud)

所以,似乎v8不会让你继承Date.


Rud*_*udu 8

具体来看日期的MDC文档:

注意:请注意,Date对象只能通过调用Date或将其用作构造函数来实例化; 与其他JavaScript对象类型不同,Date对象没有文字语法.

看起来这个Date对象根本就不是JS对象.当我写一个扩展库时,我最终做了以下事情:

function MyDate() {
   var _d=new Date();
   function init(that) {
      var i;
      var which=['getDate','getDay','getFullYear','getHours',/*...*/,'toString'];
      for (i=0;i<which.length;i++) {
         that[which[i]]=_d[which[i]]; 
      }
   }
   init(this);
   this.doSomething=function() {
    console.log("DO");
   }
}
Run Code Online (Sandbox Code Playgroud)

至少我先做了那个.最后JS Date对象的局限性让我变得更好,我改用自己的数据存储方法(例如,为什么getDate=一年中的某一天?)


sst*_*tur 8

这可以在ES5中完成.它需要直接修改原型链.这是使用__proto__或完成的Object.setPrototypeOf().我__proto__在示例代码中使用,因为它得到了最广泛的支持(尽管标准是Object.setPrototypeOf).

function XDate(a, b, c, d, e, f, g) {
  var x;
  switch (arguments.length) {
    case 0:
      x = new Date();
      break;
    case 1:
      x = new Date(a);
      break;
    case 2:
      x = new Date(a, b);
      break;
    case 3:
      x = new Date(a, b, c);
      break;
    case 4:
      x = new Date(a, b, c, d);
      break;
    case 5:
      x = new Date(a, b, c, d, e);
      break;
    case 6:
      x = new Date(a, b, c, d, e, f);
      break;
    default:
      x = new Date(a, b, c, d, e, f, g);
  }
  x.__proto__ = XDate.prototype;
  return x;
}

XDate.prototype.__proto__ = Date.prototype;

XDate.prototype.foo = function() {
  return 'bar';
};
Run Code Online (Sandbox Code Playgroud)

诀窍是我们实际实例化一个Date对象(具有正确数量的参数),它为我们提供了一个[[Class]]正确内部设置的对象.然后我们修改它的原型链,使其成为XDate的一个实例.

因此,我们可以通过以下方式验证所有这些:

var date = new XDate(2015, 5, 18)
console.log(date instanceof Date) //true
console.log(date instanceof XDate) //true
console.log(Object.prototype.toString.call(date)) //[object Date]
console.log(date.foo()) //bar
console.log('' + date) //Thu Jun 18 2015 00:00:00 GMT-0700 (PDT)
Run Code Online (Sandbox Code Playgroud)

这是我知道子类日期的唯一方法,因为Date()构造函数做了一些魔术来设置内部[[Class]]和大多数日期方法需要设置.这将适用于Node,IE 9+和几乎所有其他JS引擎.

类似的方法可用于子类化Array.

  • 真的很有趣的方法.您可以使用Date.bind()消除switch语句.例如:`var x = new(Function.prototype.bind.apply(Date,[Date] .concat(Array.prototype.slice.call(arguments))))` (2认同)