我正在尝试子类化/扩展本机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());
和此:
function MyDate() {
}
MyDate.prototype = new Date();
MyDate.prototype.doSomething = function() {
console.log("DO");
}
var date = new MyDate();
date.doSomething();
console.log(date);
在这两种情况下,date.doSomething()
都有效,但当我调用date.getHours()
甚至console.log(date)
等任何本机方法时,我得到'TypeError:这不是Date对象。“
有什么想法吗?还是我坚持扩展顶级Date对象?
答案 0 :(得分:19)
查看v8代码,在date.js中:
function DateGetHours() {
var t = DATE_VALUE(this);
if (NUMBER_IS_NAN(t)) return t;
return HOUR_FROM_TIME(LocalTimeNoCheck(t));
}
看起来DATE_VALUE是一个执行此操作的宏:
DATE_VALUE(arg) = (%_ClassOf(arg) === 'Date' ? %_ValueOf(arg) : ThrowDateTypeError());
所以,似乎v8不会让你继承Date。
答案 1 :(得分:8)
专门查看Date上的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");
}
}
至少我先这样做了。 JS Date对象的局限性最终得到了改善,我改用自己的数据存储方法(例如,为什么getDate
=一年中的某一天?)
答案 2 :(得分:7)
这可以在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';
};
诀窍是我们实际实例化一个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)
这是我知道子类化日期的唯一方法,因为Date()
构造函数可以设置内部[[Class]]
并且大多数日期方法需要设置。这将适用于Node,IE 9+和几乎所有其他JS引擎。
类似的方法可用于子类化Array。
答案 3 :(得分:3)
在ES6中,可以子类化内置构造函数(Array
,Date
和Error
) - reference
问题是当前的ES5引擎无法做到这一点,因为Babel indicates并且需要一个支持原生ES6的浏览器。
截至今日(2015-04-15),目前用于子类化的ES6 browser support非常弱/不存在。
答案 4 :(得分:2)
EcmaScript规范的第15.9.5节说:
在以下对作为Date原型对象属性的函数的描述中,短语'this Date object'指的是调用该函数的该值的对象。除非另有明确说明,否则这些功能都不是通用的;如果此值不是
TypeError
内部属性的值为[[Class]]
的对象,则抛出"Date"
异常。此外,短语“this time value”指的是此Date对象表示的时间的Number值,即此Date对象的[[PrimitiveValue]]
内部属性的值。
请特别注意“这些函数都不是通用的”这一位,与String
或Array
不同,这意味着这些方法无法应用于非Date
。
某些内容是否为Date
取决于其[[Class]]
是否为"Date"
。对于您的子类,[[Class]]
为"Object"
。
答案 5 :(得分:1)
我相信Date实际上是一个静态函数,而不是一个真正的对象,因此不能继承使用原型,所以你需要创建一个façade类来包装你需要的任何Date功能。
我会尝试将您的新日期对象构建为:
function MyDate(value) {
this.value=new Date(value);
// add operations that operate on this.value
this.prototype.addDays=function(num){
...
};
this.prototype.toString=function() {
return value.toString();
};
}
// add static methods from Date
MyDate.now=Date.now;
MyDate.getTime=Date.getTime;
...
(我不在我可以测试的系统附近,但我希望你能得到这个想法。)
答案 6 :(得分:1)
我几天前尝试过这样做,并认为我可以使用mixins。
所以你可以这样做:
var asSomethingDoable = (function () {
function doSomething () {
console.log('Doing something...');
}
return function () {
this.doSomething = doSomething;
return this;
}
})();
var date = new Date();
asSomethingDoable.call(date);
这是添加了缓存的变体,因此它有点复杂。但这个想法是在方法上添加方法。
答案 7 :(得分:1)
您也可以使用github.com/loganfsmyth/babel-plugin-transform-builtin-extend
示例:
import 'babel-polyfill'
export default class MyDate extends Date {
constructor () {
super(...arguments)
}
}
答案 8 :(得分:1)
var SubDate = function() {
var dateInst = new Date(...arguments); // spread arguments object
/* Object.getPrototypeOf(dateInst) === Date.prototype */
Object.setPrototypeOf(dateInst, SubDate.prototype); // redirectionA
return dateInst; // now instanceof SubDate
};
Object.setPrototypeOf(SubDate.prototype, Date.prototype); // redirectionB
// do something useful
Object.defineProperty(SubDate.prototype, 'year', {
get: function() {return this.getFullYear();},
set: function(y) {this.setFullYear(y);}
});
var subDate = new SubDate();
subDate.year; // now
subDate.year = 2050; subDate.getFullYear(); // 2050
Date
构造函数的问题已在其他答案中解释。您可以在Date | MDN(第一个注释)上了解Date.call(this, ...arguments)
问题。
此解决方案是一种紧凑的解决方法,可在所有支持的浏览器中使用。
答案 9 :(得分:0)
我知道这有点晚了,但是对于可能遇到这个问题的其他人,我有效地将日期替换为我为PhantomJS所需的polyfill的Date。该技术似乎也适用于其他浏览器。还有一些其他问题需要解决,但基本上我采用了与Rudu相同的方法。
完整的评论代码位于https://github.com/kbaltrinic/PhantomJS-DatePolyfill。
答案 10 :(得分:0)
根据@sstur的回答
我们可以使用Function.prototype.bind()
使用传入的参数动态构造Date对象。
请参阅: Mozilla Developer Network: bind() method
function XDate() {
var x = new (Function.prototype.bind.apply(Date, [Date].concat(Array.prototype.slice.call(arguments))))
x.__proto__ = XDate.prototype;
return x;
}
XDate.prototype.__proto__ = Date.prototype;
XDate.prototype.foo = function() {
return 'bar';
};
<强>验证强>
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-0500 (CDT)
答案 11 :(得分:0)
基于@sstur的答案以及@bucabay的改进:
请注意,至少在MDN docs中,这些答案已使用__proto__
,因此已弃用,强烈建议不要使用。
幸运的是,可以通过设置类中__proto__
中每个单独的函数来执行所需的操作,而无需使用Date.prototype
,这可以通过使用Object.getOwnPropertyNames()
来简化。
function XDate() {
var x = new (Function.prototype.bind.apply(Date, [Date].concat(Array.prototype.slice.call(arguments))));
Object.getOwnPropertyNames(Date.prototype).forEach(function(func) {
this[func] = function() {
return x[func].apply(x, Array.prototype.slice.call(arguments));
};
}.bind(this));
this.foo = function() {
return 'bar';
};
}
此方法的一个次要缺点是XDate
实际上不是Date
的子类。支票xdateobj instanceof Date
是false
。但这不必担心,因为无论如何您都可以使用Date类的方法。