54 votes

Instanciation d’un objet JavaScript en appelant prototype.constructor.apply

Permettez-moi de commencer avec un exemple précis de ce que je suis en train de faire.

J'ai un tableau de l'année, le mois, le jour, l'heure, les minutes, les secondes et les millisecondes composants sous la forme [ 2008, 10, 8, 00, 16, 34, 254 ]. Je tiens à instancier un objet Date en utilisant la norme suivante constructeur:

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

Comment puis-je passer mon tableau de ce constructeur pour obtenir une nouvelle instance de Date? [ Mise à jour: Ma question s'étend au-delà de cet exemple précis. J'aimerais une solution générale pour JavaScript intégrés classes comme la Date, le Tableau, RegExp, etc. dont les constructeurs sont au-delà de ma portée. ]

Je suis en train de faire quelque chose comme ce qui suit:

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

J'ai probablement besoin d'un "new", quelque part. Le ci-dessus retourne l'heure courante comme si je l'avais appelé "(new Date()).toString()". Je reconnais aussi que je suis peut-être complètement dans la mauvaise direction avec la ci-dessus :)

Remarque: Non eval() et pas d'accès au tableau les éléments un par un, s'il vous plaît. Je suis assez sûr que je devrais être en mesure d'utiliser le tableau comme est.


Mise À Jour: D'Autres Expériences

Depuis, personne n'a été en mesure de venir avec un travail encore de réponse, j'ai fait plus de jouer autour. Voici une nouvelle découverte.

Je peux le faire avec mon propre classe:

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!

Mais il ne fonctionne pas avec les intrinsèque classe Date:

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

Ne s'agit-il de travailler avec Nombre:

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

Peut-être que ce n'est tout simplement pas possible avec des objets intrinsèques? Je me suis mise à l'essai avec Firefox BTW.

64voto

Ates Goral Points 47670

J'ai fait plus une enquête de mes propres et est venu à la conclusion que c'est un exploit impossible, en raison de la façon dont la classe Date est mise en œuvre.

J'ai inspecté le SpiderMonkey code source pour voir comment Date a été mis en œuvre. Je pense que tout se résume à la suite de quelques lignes:

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)

Lorsque la Date est utilisée comme une fonction ( Date() ou Date.prototype.constructor(), ce qui est exactement la même chose), la valeur par défaut est de retour à l'heure actuelle comme une chaîne de caractères dans les paramètres régionaux de format. Et ce, indépendamment de tous les arguments qui sont passés dans l':

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

Je ne pense pas qu'il ya quelque chose qui peut être fait à la JS niveau de contourner cela. Et c'est probablement la fin de ma quête dans cette rubrique.

J'ai aussi remarqué quelque chose d'intéressant:

    /* 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 est une instance de Date avec la valeur interne de l' NaN et, par conséquent,

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

IE n'est pas nous décevoir. Il fait les choses un peu différemment et probablement définit la valeur interne à l' -1 , de sorte que la Date.prototype retourne toujours une date d'un peu avant l'époque.


Mise à jour

J'ai enfin creusé dans ECMA-262 lui-même et il se trouve que je suis en train de réaliser (avec l'objet Date) est, par définition, -- pas possible:

15.9.2 La Date Constructeur Appelle la Fonction

Lorsque la Date est appelé comme un la fonction plutôt que comme un constructeur, elle renvoie une chaîne de caractères représentant la heure actuelle (UTC).

REMARQUE La fonction appelez Date(…) n'est pas équivalente à la création de l'objet de l'expression new Date(…) avec les mêmes arguments.

15.9.2.1 Date ( [[, mois [, date [, les heures, [de minutes [, secondes [, ms ] ] ] ] ] ] ] )

Tous les les arguments sont facultatifs; tous les arguments fournis sont acceptés mais sont complètement ignoré. Une chaîne est créé et retourné comme si par la l'expression (new Date()).toString().

14voto

Cowboy Ben Alman Points 1211

J'appellerais à peine cela élégant, mais lors de mes tests (FF3, Saf4, IE8), cela fonctionne:

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

Au lieu de cela:

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

Essaye ça:

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

8voto

AnthonyWJones Points 122520

Voici comment vous pourriez résoudre le cas spécifique: -

 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)
 

Cependant, vous recherchez une solution plus générale. Le code recommandé pour créer une méthode constructeur consiste à l'avoir return this .

Par conséquent:-

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

pourrait être construit: -

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

Cependant, toutes les implémentations ne fonctionnent pas de cette manière, notamment parce que la chaîne de prototypes serait fausse:

 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
 

3voto

kybernetikos Points 3127

C'est comment vous le faites:

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]);

Il fonctionne avec n'importe quel constructeur, et pas seulement built-ins ou les constructeurs qui peut doubler comme des fonctions (comme la Date).

Cependant, elle nécessite l'Ecmascript 5 .fonction de liaison. Les cales ne sera probablement pas fonctionner correctement.

Par ailleurs, l'une de l'autre les réponses propose de revenir this d'un constructeur. Qui font qu'il est très difficile d'étendre l'objet en utilisant la classique héritage, donc je pense qu'il serait un antipattern.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X