78 votes

Y a-t-il un moyen de Object.freeze() une JavaScript Date ?

Selon le MDN Object.freeze() documentation:

L' Object.freeze() méthode se fige un objet: c'est, empêche les nouvelles propriétés de l'être ajouté; empêche propriétés existantes d'être retiré; et l'empêche de propriétés existantes, ou de leurs enumerability, reconfigurabilité, ou writability, d'être changé. Dans l'essence de l'objet est effectuée de manière efficace immuable. La méthode retourne un objet surgelés.

Je m'attendais à ce que l'appel de gel sur une date de prévenir les changements à cette date, mais il ne semble pas fonctionner. Voici ce que j'ai fais (en cours d'exécution Node.js v5.3.0):

let d = new Date()
Object.freeze(d)
d.setTime(0)
console.log(d) // Wed Dec 31 1969 16:00:00 GMT-0800 (PST)

Je me serais attendu à l'appel d' setTime d'échouer ou de ne rien faire. Toutes les idées sur la façon de fixer une date?

63voto

T.J. Crowder Points 285826

Est-il un moyen de l'Objet.freeze() JavaScript Date?

Je ne le pense pas. Vous pouvez obtenir près, cependant, de voir sous la ligne ci-dessous. Mais d'abord, voyons pourquoi juste Object.freeze ne fonctionne pas.

Je m'attendais à ce que l'appel de gel sur une date de prévenir les modifications qui date de...

Il serait si Date utilisé une propriété de l'objet à tenir son temps interne de la valeur, mais il ne le fait pas. Il utilise un [[DateValue]] logement interne à la place. Interne emplacements ne sont pas les propriétés:

Interne fentes correspondent à l'état interne qui est associé avec des objets et utilisés par les différents spécification ECMAScript algorithmes. Interne emplacements ne sont pas les propriétés de l'objet...

Donc le gel de l'objet n'a aucun effet sur sa capacité à muter son [[DateValue]] logement interne.


Vous pouvez congeler une Date, ou de fait, de toute façon: Remplacer toutes ses mutateur méthodes non-op fonctions ou des fonctions qui renvoient une erreur) et ensuite, freeze . Mais comme observé par zzzzBov (nice!), qui n'a pas empêcher quelqu'un de faire Date.prototype.setTime.call(d, 0) (dans une tentative délibérée de contourner la gelée objet, ou comme un sous-produit de certains compliqué code qu'ils utilisent). Donc, c'est proche, mais pas de cigare.

Voici un exemple (je suis en utilisant ES2015 fonctionnalités ici, car j'ai vu qu' let dans votre code, vous aurez besoin d'un navigateur récent pour l'exécuter; mais cela peut être fait avec ES5-ne représente plus que bien):

"use strict";

let d = new Date();
freezeDate(d);
d.setTime(0);
snippet.log(d);

function nop() {
}

function freezeDate(d) {
  allNames(d).forEach(name => {
    if (name.startsWith("set") && typeof d[name] === "function") {
      d[name] = nop;
    }
  });
  Object.freeze(d);
  return d;
}
function allNames(obj) {
  var names = Object.create(null); // Or use Map here
  var thisObj;
  
  for (thisObj = obj; thisObj; thisObj = Object.getPrototypeOf(thisObj)) {
    Object.getOwnPropertyNames(thisObj).forEach(name => {
      names[name] = 1;
    });
  }
  return Object.keys(names);
}
<!-- Script provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
<script src="//tjcrowder.github.io/simple-snippets-console/snippet.js"></script>

Je pense que tous le mutateur méthodes d' Date démarrer avec set, mais si non il est facile de tordre le ci-dessus.

8voto

zzzzBov Points 62084

À partir de MDN docs sur Object.freeze (l'emphase est mienne):

Les valeurs ne peuvent pas être changés pour des propriétés des données. Propriétés d'accesseur (getters et setters) fonctionnent de la même (et encore donner l'illusion que vous êtes à la modification de la valeur). Notez que les valeurs qui sont les objets peuvent encore être modifiés, sauf s'ils sont également gelés.

La Date de l'objet setTime méthode n'est pas la modification d'une propriété de l'objet Date, alors qu'il continue à travailler, malgré le fait d'avoir gelé l'instance.

8voto

Zirak Points 13656

C'est une très bonne question!

T. J. Crowder, en réponse a une excellente solution, mais ça m'a fait penser: Que pouvons-nous faire? Comment pouvons-nous aller autour de la Date.prototype.setTime.call(yourFrozenDate)?

1ère tentative: "Wrapper"

D'une façon directe, est de donner un AndrewDate fonction qui encapsule une date. Elle a tout d'une date a moins le setters:

function AndrewDate(realDate) {
    var proto = Date.prototype;
    var propNames = Object.getOwnPropertyNames(proto)
        .filter(propName => !propName.startsWith('set'));

    return propNames.reduce((ret, propName) => {
        ret[propName] = proto[propName].bind(realDate);
        return ret;
    }, {});
}

var date = AndrewDate(new Date());
date.setMonth(2); // TypeError: d.setMonth is not a function

Ce que ce n'est de créer un objet qui a toutes les propriétés d'une date réelle de l'objet a et utilise Function.prototype.bind pour définir leur this.

Ce n'est pas un imbécile preuve façon de rassembler autour de ces clés, mais j'espère que vous pouvez voir mon intention.

Mais attendez...y regarder d'un peu plus ici et là, nous pouvons voir qu'il y a une meilleure manière de faire ceci.

2ème tentative: les Procurations

function SuperAndrewDate(realDate) {
    return new Proxy(realDate, {
        get(target, prop) {
            if (!prop.startsWith('set')) {
                return Reflect.get(target, prop);
            }
        }
    });
}

var proxyDate = SuperAndrewDate(new Date());

Et nous l'avons résolu!

...en quelque sorte. Voir, Firefox est le seul droit qui met en œuvre des procurations, et pour certains bizarre raisons de la date d'objets ne peuvent pas être mandaté. En outre, vous remarquerez que vous pouvez toujours faire des choses comme 'setDate' in proxyDate et vous verrez le nombre de diplômés en console. Pour surmonter que plus de pièges doivent être fournis; plus précisément, has, enumerate, ownKeys, getOwnPropertyDescriptor et qui sait quelle étrange bord des cas, il y en a!

...Donc, à la réflexion, cette réponse est presque inutile. Mais au moins nous avons eu le plaisir, droit?

6voto

Mensur Grišević Points 161

Vous pourriez envelopper dans une classe comme structure et définir personnalisés getters et setters pour éviter un changement non voulu

2voto

IGwe Points 457

L'on a accepté la réponse est en fait erronée, je le crains. Vous pouvez congeler une instance de n'importe quel objet, y compris une instance d' Date. À l'appui de @zzzzBov's réponse, le gel d'une instance d'objet n'implique pas l'état de l'objet devient constant.

Une façon de prouver qu'un Date exemple est vraiment gelé est en suivant les étapes ci-dessous:

var date = new Date();
date.x = 4;
console.log(date.x); // 4
Object.freeze(date);
date.x = 20; // this assignment fails silently, freezing has made property x to be non-writable
date.y = 5; // this also fails silently, freezing ensures you can't add new properties to an object
console.log(date.x); // 4, unchanged
console.log(date.y); // undefined

Mais vous pouvez obtenir des comportements je suppose que vous le désir comme suit:

var date = (function() {
    var actualDate = new Date();

    return Object.defineProperty({}, "value", {
        get: function() {
            return new Date(actualDate.getTime())
        },
        enumerable: true
    });
})();

console.log(date.value); // Fri Jan 29 2016 00:01:20 GMT+0100 (CET)
date.value.setTime(0);
console.log(date.value); // Fri Jan 29 2016 00:01:20 GMT+0100 (CET)
date.value = null;       // fails silently
console.log(date.value); // Fri Jan 29 2016 00:01:20 GMT+0100 (CET)

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