[英]How to extend the Javascript Date object?
我正在嘗試子類化/擴展本機 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: this is not a Date object”。)
有任何想法嗎? 還是我堅持擴展頂級 Date 對象?
查看 date.js 中的 v8 代碼:
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 子類化。
這可以在 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 進行子類化的唯一方法,因為Date()
構造函數做了一些魔法來設置內部[[Class]]
並且大多數日期方法都需要設置它。 這將適用於 Node、IE 9+ 和幾乎所有其他 JS 引擎。
類似的方法可用於子類化 Array。
具體查看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
= day of year?)
EcmaScript 規范的第 15.9.5 節說:
在作為 Date 原型對象屬性的函數的以下描述中,短語“this Date object”是指作為函數調用的 this 值的對象。 除非另有明確說明,否則這些函數都不是通用的; 如果 this 值不是
[[Class]]
內部屬性的值為"Date"
的對象,則會引發TypeError
異常。 此外,短語“此時間值”是指此 Date 對象表示的時間的 Number 值,即此 Date 對象的[[PrimitiveValue]]
內部屬性的值。
請特別注意“這些函數中沒有一個是通用的”,這與String
或Array
,這意味着這些方法不能應用於非Date
s。
某個東西是否是Date
取決於它的[[Class]]
是否是"Date"
。 對於您的子類, [[Class]]
是"Object"
。
幾天前我嘗試過這樣做,並認為我可以使用mixins 。
所以你可以這樣做:
var asSomethingDoable = (function () {
function doSomething () {
console.log('Doing something...');
}
return function () {
this.doSomething = doSomething;
return this;
}
})();
var date = new Date();
asSomethingDoable.call(date);
這是添加緩存后的變化,因此稍微復雜一些。 但我們的想法是動態地添加方法。
你也可以使用github.com/loganfsmyth/babel-plugin-transform-builtin-extend
例子:
import 'babel-polyfill'
export default class MyDate extends Date {
constructor () {
super(...arguments)
}
}
基於@sstur的回答
我們可以使用Function.prototype.bind()
使用傳入的參數動態構造 Date 對象。
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)
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 |上閱讀Date.call(this, ...arguments)
問題。 MDN (第一注)。
此解決方案是一種緊湊的解決方法,可在所有支持的瀏覽器中按預期工作。
我相信 Date 實際上是一個靜態函數,而不是一個真正的對象,因此不能從使用原型繼承,因此您需要創建一個外觀類來包裝您需要的任何 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;
...
(我不在一個可以測試的系統附近,但我希望你能明白。)
我知道這有點晚了,但是對於可能遇到此問題的其他人,我設法為 PhantomJS 所需的 polyfill 有效地將 Date 子類化。 該技術似乎也適用於其他瀏覽器。 還有一些額外的問題需要解決,但基本上我遵循了與 Rudu 相同的方法。
完整的注釋代碼位於https://github.com/kbaltrinic/PhantomJS-DatePolyfill 。
基於@sstur 的回答和@bucabay 的改進:
請注意, __proto__
正在這些答案中使用,它已被棄用,並且強烈建議不要使用它,至少根據MDN 文檔。
幸運的是,通過在我們的類中從Date.prototype
設置每個單獨的函數, Date.prototype
在不使用__proto__
的情況下完成所需的操作,這通過使用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 類的方法。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.