54

私がやろうとしていることの具体例から始めましょう。

の形式で、年、月、日、時、分、秒、ミリ秒のコンポーネントの配列があります[ 2008, 10, 8, 00, 16, 34, 254 ]。次の標準コンストラクターを使用してDateオブジェクトをインスタンス化したいと思います。

new Date(year, month, date [, hour, minute, second, millisecond ])

配列をこのコンストラクターに渡して、新しいDateインスタンスを取得するにはどうすればよいですか?[更新:私の質問は実際にはこの特定の例を超えています。Date、Array、RegExpなどのコンストラクターが私の手の届かないところにある組み込みのJavaScriptクラスの一般的なソリューションが欲しいのですが。]

私は次のようなことをしようとしています:

var comps = [ 2008, 10, 8, 00, 16, 34, 254 ];
var d = Date.prototype.constructor.apply(this, comps);

おそらくnewどこかに「」が必要です。上記は、「(new Date()).toString()」と呼んだかのように現在の時刻を返すだけです。私はまた、私が上記で完全に間違った方向にいる可能性があることを認めます:)

eval()配列項目に1つずつアクセスすることはできません。アレイをそのまま使用できるはずです。


更新:さらなる実験

まだ誰も実用的な答えを思い付くことができていないので、私はもっと遊んでいます。これが新しい発見です。

私は自分のクラスでこれを行うことができます:

function Foo(a, b) {
    this.a = a;
    this.b = b;

    this.toString = function () {
        return this.a + this.b;
    };
}

var foo = new Foo(1, 2);
Foo.prototype.constructor.apply(foo, [4, 8]);
document.write(foo); // Returns 12 -- yay!

ただし、組み込みのDateクラスでは機能しません。

var d = new Date();
Date.prototype.constructor.call(d, 1000);
document.write(d); // Still returns current time :(

Numberでも機能しません:

var n = new Number(42);
Number.prototype.constructor.call(n, 666);
document.write(n); // Returns 42

たぶん、これは組み込みオブジェクトでは不可能ですか?FirefoxBTWでテストしています。

4

13 に答える 13

64

私は自分自身でさらに調査を行い、 Date クラスの実装方法のために、これは不可能な偉業であるという結論に達しました。

SpiderMonkeyのソース コードを調べて、Date がどのように実装されているかを確認しました。それはすべて、次の数行に要約されると思います。

static JSBool
Date(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    jsdouble *date;
    JSString *str;
    jsdouble d;

    /* Date called as function. */
    if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) {
        int64 us, ms, us2ms;
        jsdouble msec_time;

        /* NSPR 2.0 docs say 'We do not support PRMJ_NowMS and PRMJ_NowS',
         * so compute ms from PRMJ_Now.
         */
        us = PRMJ_Now();
        JSLL_UI2L(us2ms, PRMJ_USEC_PER_MSEC);
        JSLL_DIV(ms, us, us2ms);
        JSLL_L2D(msec_time, ms);

        return date_format(cx, msec_time, FORMATSPEC_FULL, rval);
    }

    /* Date called as constructor. */
    // ... (from here on it checks the arg count to decide how to create the date)

Date が関数として (Date()またはDate.prototype.constructor()のいずれかで、まったく同じものとして) 使用される場合、デフォルトでは現在の時刻がロケール形式の文字列として返されます。これは、渡される引数に関係なく次のようになります。

alert(Date()); // Returns "Thu Oct 09 2008 23:15:54 ..."
alert(typeof Date()); // Returns "string"

alert(Date(42)); // Same thing, "Thu Oct 09 2008 23:15:54 ..."
alert(Date(2008, 10, 10)); // Ditto
alert(Date(null)); // Just doesn't care

これを回避するためにJSレベルでできることは何もないと思います。そして、これはおそらく、このトピックに関する私の追求の終わりです。

また、興味深いことに気付きました。

    /* Set the value of the Date.prototype date to NaN */
    proto_date = date_constructor(cx, proto);
    if (!proto_date)
        return NULL;
    *proto_date = *cx->runtime->jsNaN;

Date.prototypeの内部値を持つ Date インスタンスであるNaNため、

alert(Date.prototype); // Always returns "Invalid Date"
                       // on Firefox, Opera, Safari, Chrome
                       // but not Internet Explorer

IE は私たちを失望させません。動作が少し異なり、おそらく内部値を に設定し-1て、Date.prototype が常にエポックの少し前の日付を返すようにします。


アップデート

最終的にECMA-262自体を掘り下げたところ、(Dateオブジェクトを使用して)達成しようとしていることは、定義上、不可能であることが判明しました:

15.9.2 関数として呼び出される日付コンストラクタ

Date がコンストラクターではなく関数として呼び出されると、現在の時刻 (UTC) を表す文字列が返されます。

注意関数呼び出しは 、同じ引数を持つDate(…)オブジェクト作成式と同等ではありません。new Date(…)

15.9.2.1 日付 ( [ 年 [, 月 [, 日 [, 時 [, 分 [, 秒 [, ミリ秒] ] ] ] ] )

すべての引数はオプションです。指定された引数はすべて受け入れられますが、完全に無視されます。文字列が作成され、式であるかのように返されます(new Date()).toString()

于 2008-10-19T21:58:38.630 に答える
14

私はこれをエレガントとは言いませんが、私のテスト (FF3、Saf4、IE8) では動作します:

var arr = [ 2009, 6, 22, 10, 30, 9 ];

これの代わりに:

var d = new Date( arr[0], arr[1], arr[2], arr[3], arr[4], arr[5] );

これを試して:

var d = new Date( Date.UTC.apply( window, arr ) + ( (new Date()).getTimezoneOffset() * 60000 ) );

于 2009-07-22T20:31:02.453 に答える
8

これは、特定のケースを解決する方法です:-

function writeLn(s)
{
    //your code to write a line to stdout
    WScript.Echo(s)
}

var a =  [ 2008, 10, 8, 00, 16, 34, 254 ]

var d = NewDate.apply(null, a)

function NewDate(year, month, date, hour, minute, second, millisecond)
{
    return new Date(year, month, date, hour, minute, second, millisecond);
}

writeLn(d)

ただし、より一般的なソリューションを探しています。コンストラクター メソッドを作成するための推奨されるコードは、それを使用することreturn thisです。

したがって: -

function Target(x , y) { this.x = x, this.y = y; return this; }

構築することができます:-

var x = Target.apply({}, [1, 2]);

ただし、すべての実装がこのように機能するわけではありません。プロトタイプ チェーンが間違っているためです。

var n = {};
Target.prototype = n;
var x = Target.apply({}, [1, 2]);
var b = n.isPrototypeOf(x); // returns false
var y = new Target(3, 4);
b = n.isPrototypeOf(y); // returns true
于 2008-10-08T07:30:49.787 に答える
4

エレガントではありませんが、解決策は次のとおりです。

function GeneratedConstructor (methodName, argumentCount) {
    var params = []

    for (var i = 0; i < argumentCount; i++) {
        params.push("arguments[" + i + "]")
    }

    var code = "return new " + methodName + "(" + params.join(",") +  ")"

    var ctor = new Function(code)

    this.createObject = function (params) {
        return ctor.apply(this, params)
    }
}

これが機能する方法はかなり明白なはずです。コード生成によって関数を作成します。この例では、作成するコンストラクターごとに固定数のパラメーターがありますが、とにかく便利です。ほとんどの場合、少なくとも引数の最大数を念頭に置いています。これは、コードを一度生成してから再利用できるため、ここにある他のいくつかの例よりも優れています。生成されたコードは、javascript の可変引数機能を利用しています。このようにして、各パラメーターに名前を付ける必要がなくなります (または、それらをリストで綴り、生成する関数に引数を渡します)。これが実際の例です:

var dateConstructor = new GeneratedConstructor("Date", 3)
dateConstructor.createObject( [ 1982, 03, 23 ] )

これにより、以下が返されます。

1982 年 4 月 23 日金曜日 00:00:00 GMT-0800 (PST)

それは確かにまだ...少し醜いです。しかし、少なくとも便利なことに混乱を隠し、コンパイルされたコード自体がガベージ コレクションを取得できるとは想定していません (実装に依存する可能性があり、バグの可能性が高い領域であるため)。

乾杯、スコット・S・マッコイ

于 2009-04-20T05:58:04.740 に答える
3

これがあなたのやり方です:

function applyToConstructor(constructor, argArray) {
    var args = [null].concat(argArray);
    var factoryFunction = constructor.bind.apply(constructor, args);
    return new factoryFunction();
}

var d = applyToConstructor(Date, [2008, 10, 8, 00, 16, 34, 254]);

組み込み関数や関数(Dateなど)を兼ねることができるコンストラクターだけでなく、任意のコンストラクターで機能します。

ただし、Ecmascript5.bind関数が必要です。シムはおそらく正しく機能しません。

ちなみに、他の答えの1つはthis、コンストラクターから戻ることを示唆しています。そのため、古典的な継承を使用してオブジェクトを拡張することは非常に困難になる可能性があるため、アンチパターンと見なします。

于 2013-01-17T09:55:01.887 に答える
-1

別の解決策は次のとおりです。

function createInstance(Constructor, args){
    var TempConstructor = function(){};
    TempConstructor.prototype = Constructor.prototype;
    var instance = new TempConstructor;
    var ret = Constructor.apply(instance, args);
    return ret instanceof Object ? ret : instance;
}

console.log( createInstance(Date, [2008, 10, 8, 00, 16, 34, 254]) )
于 2012-08-01T15:34:05.853 に答える
-1

長くなってしまいましたが、私はこの質問に対する本当の答えを持っています。これは決して不可能ではありません。一般的なソリューションについては、https://gist.github.com/747650を参照してください。

var F = function(){};
F.prototype = Date.prototype;
var d = new F();
Date.apply(d, comps);
于 2011-04-05T17:36:40.477 に答える
-2

編集済み

申し訳ありませんが、私は何年も前にそのようにしたと確信していましたが、今は固執します:

var d = new Date(comps[0],comps[1],comps[2],comps[3],comps[4],comps[5],comps[6]);

編集:

ただし、javascript の Date オブジェクトは月のインデックスを使用することを覚えておいてください。したがって、上記の配列は

2008 年 11 月 8 日 00:16:34:254

于 2008-10-08T07:50:53.367 に答える
-3
var comps = [ 2008, 10, 8, 00, 16, 34, 254 ];
var d = eval("new Date(" + comps.join(",") + ");");
于 2008-10-08T20:11:16.600 に答える