如何扩展 Javascript Date 对象?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/6075231/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to extend the Javascript Date object?
提问by evilcelery
I'm trying to subclass/extend the native Date object, without modifying the native object itself.
我正在尝试子类化/扩展本机 Date 对象,而不修改本机对象本身。
I've tried this:
我试过这个:
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());
and this:
还有这个:
function MyDate() {
}
MyDate.prototype = new Date();
MyDate.prototype.doSomething = function() {
console.log("DO");
}
var date = new MyDate();
date.doSomething();
console.log(date);
In both cases, the date.doSomething()
works, but when I call any of the native methods such as date.getHours()
or even console.log(date)
, I get 'TypeError: this is not a Date object.'
在这两种情况下,都date.doSomething()
有效,但是当我调用任何本机方法(例如date.getHours()
或 even )时console.log(date)
,我得到“类型错误:这不是日期对象”。
Any ideas? Or am I stuck to extending the top-level Date object?
有任何想法吗?或者我是否坚持扩展顶级 Date 对象?
采纳答案by Geoff Chappell
Looking at the v8 code, in date.js:
查看 date.js 中的 v8 代码:
function DateGetHours() {
var t = DATE_VALUE(this);
if (NUMBER_IS_NAN(t)) return t;
return HOUR_FROM_TIME(LocalTimeNoCheck(t));
}
And looks like DATE_VALUE is a macro that does this:
看起来 DATE_VALUE 是一个执行此操作的宏:
DATE_VALUE(arg) = (%_ClassOf(arg) === 'Date' ? %_ValueOf(arg) : ThrowDateTypeError());
So, seems like v8 won't let you subclass Date.
因此,似乎 v8 不允许您将 Date 子类化。
回答by Rudu
Check out the MDC docs on Datespecifically:
具体查看Date上的 MDC 文档:
Note: Note that Date objects can only be instantiated by calling Date or using it as a constructor; unlike other JavaScript object types, Date objects have no literal syntax.
注意:注意Date对象只能通过调用Date或作为构造函数来实例化;与其他 JavaScript 对象类型不同,Date 对象没有文字语法。
It seems like the Date
object isn't really a JS object at all. When I was writing an extension library, I ended up doing the following:
似乎该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");
}
}
At least I did that first. The limitations of the JS Date object in the end got the better of me and I switched to my own data storage approach (eg. why does getDate
=day of year?)
至少我是先这样做的。JS Date 对象的局限性最终让我变得更好,我切换到我自己的数据存储方法(例如为什么getDate
= day of year?)
回答by sstur
This can be done in ES5. It requires modifying the prototype chain directly. This is done using __proto__
or Object.setPrototypeOf()
. I'm using __proto__
in the sample code since that's most widely supported (although the standard is Object.setPrototypeOf
).
这可以在 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';
};
The trick is that we actually instantiate a Date
object (with the correct number of arguments) which gives us an object with it's internal [[Class]]
set correctly. Then we modify it's prototype chain to make it an instance of XDate.
诀窍是我们实际上实例化了一个Date
对象(具有正确数量的参数),它为我们提供了一个内部[[Class]]
设置正确的对象。然后我们修改它的原型链,使其成为 XDate 的实例。
So, we can verify all this by doing:
因此,我们可以通过执行以下操作来验证所有这些:
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)
This is the only way I know of to subclass date because the Date()
constructor does some magic to set the internal [[Class]]
and most date methods require that to be set. This will work in Node, IE 9+ and almost all other JS engines.
这是我所知道的将 date 子类化的唯一方法,因为Date()
构造函数做了一些魔法来设置内部[[Class]]
和大多数 date 方法需要设置它。这将适用于 Node、IE 9+ 和几乎所有其他 JS 引擎。
Similar approach can be used for subclassing Array.
类似的方法可用于子类化 Array。
回答by Sean Lynch
In ES6, it will be possible to subclass built-in constructors (Array
, Date
, and Error
) - reference
在 ES6 中,可以对内置构造函数 ( Array
, Date
, 和Error
)进行子类化-参考
Problem is there is no way to do this with current ES5 engines, as Babel indicatesand will require a browser with native ES6 support.
问题是目前的 ES5 引擎无法做到这一点,正如 Babel指出的那样,并且需要具有原生 ES6 支持的浏览器。
The current ES6 browser supportfor subclassing is pretty weak / non-existant as of today (2015-04-15).
回答by Mike Samuel
Section 15.9.5 of the EcmaScript spec says:
EcmaScript 规范的第 15.9.5 节说:
In following descriptions of functions that are properties of the Date prototype object, the phrase 'this Date object' refers to the object that is the this value for the invocation of the function. Unless explicitly noted otherwise, none of these functions are generic; a
TypeError
exception is thrown if the this value is not an object for which the value of the[[Class]]
internal property is"Date"
. Also, the phrase 'this time value' refers to the Number value for the time represented by this Date object, that is, the value of the[[PrimitiveValue]]
internal property of this Date object.
在作为 Date 原型对象属性的函数的以下描述中,短语“this Date object”是指作为函数调用的 this 值的对象。除非另有明确说明,否则这些函数都不是通用的;一个
TypeError
,如果该值不是一个对象的量,值则抛出异常[[Class]]
内部属性是"Date"
。此外,短语“此时间值”是指此 Date 对象表示的时间的 Number 值,即此 Date 对象的[[PrimitiveValue]]
内部属性的值。
Note specifically the bit that says "none of these functions are generic" which, unlike for String
or Array
, means that the methods cannot be applied to non-Date
s.
请特别注意“这些函数中没有一个是通用的”,这与 forString
或不同Array
,这意味着这些方法不能应用于 non- Date
s。
Whether something is a Date
depends on whether its [[Class]]
is "Date"
. For your subclass the [[Class]]
is "Object"
.
某物是否为 aDate
取决于它是否[[Class]]
为"Date"
。对于您的子类,[[Class]]
is "Object"
。
回答by Rob Raisch
I believe Date is actually a static function, not a true object, and as such cannot be inherited from using prototypes, so you'll need to create a fa?ade class to wrap any Date functionality you need.
我相信 Date 实际上是一个静态函数,而不是一个真正的对象,因此不能从使用原型继承,所以你需要创建一个 fa?ade 类来包装你需要的任何 Date 功能。
I'd try constructing your new date object as:
我会尝试将您的新日期对象构建为:
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;
...
(I'm not near a system I can test this on, but I hope you get the idea.)
(我不在一个可以测试的系统附近,但我希望你能明白。)
回答by adius
You can also use github.com/loganfsmyth/babel-plugin-transform-builtin-extend
你也可以使用github.com/loganfsmyth/babel-plugin-transform-builtin-extend
Example:
例子:
import 'babel-polyfill'
export default class MyDate extends Date {
constructor () {
super(...arguments)
}
}
回答by bucabay
Based on answer by @sstur
基于@sstur 的回答
We can use Function.prototype.bind()
to construct the Date object dynamically with the passed in arguments.
我们可以使用Function.prototype.bind()
传入的参数动态构造 Date 对象。
See: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';
};
Verification:
确认:
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)
回答by Neni
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
The problem with the Date
constructor function is already explained in the other answers. You can read about the Date.call(this, ...arguments)
problem on Date | MDN(first Note).
Date
构造函数的问题已经在其他答案中解释过了。您可以Date.call(this, ...arguments)
在Date |上阅读有关该问题的信息。MDN(第一个注释)。
This solution is a compact workaround which works as intended in all supporting browsers.
此解决方案是一种紧凑的解决方法,可在所有支持的浏览器中按预期工作。
回答by Marcos Garcia
I've tried to do this some days ago and thought I could use mixins.
几天前我尝试过这样做,并认为我可以使用mixins。
So you could do something like:
所以你可以这样做:
var asSomethingDoable = (function () {
function doSomething () {
console.log('Doing something...');
}
return function () {
this.doSomething = doSomething;
return this;
}
})();
var date = new Date();
asSomethingDoable.call(date);
This is the variation with cache added, so it's a little bit more complicated. But the idea is to add the methods dinamically.
这是添加缓存后的变化,因此稍微复杂一些。但我们的想法是动态地添加方法。