Selon documentation MDN Object.freeze()
:
La méthode
Object.freeze()
fige un objet: c'est-à-dire empêche d'y ajouter de nouvelles propriétés; empêche la suppression des propriétés existantes; et empêche les propriétés existantes, ou leur énumérabilité, configurabilité ou écriture, d'être modifiées. En substance, l'objet est rendu effectivement immuable. La méthode renvoie l'objet gelé.
Je m'attendais à ce que le gel des appels à une date empêche les modifications de cette date, mais cela ne semble pas fonctionner. Voici ce que je fais (en exécutant 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 m'attendais à ce que l'appel à setTime
échoue ou ne fasse rien. Des idées pour figer une date?
Existe-t-il un moyen de Object.freeze () une date JavaScript?
Je ne pense pas. Vous pouvez obtenir fermer, cependant, voir sous la ligne ci-dessous. Mais voyons d'abord pourquoi Object.freeze
Ne fonctionne pas.
Je m'attendais à ce que le gel des appels à une date empêche les modifications de cette date ...
Il serait si Date
utilisait une propriété d'objet pour conserver sa valeur de temps interne, mais ce n'est pas le cas. Il utilise un [[DateValue]]
slot interne à la place. Les emplacements internes ne sont pas des propriétés:
Les emplacements internes correspondent à l'état interne associé aux objets et utilisé par divers algorithmes de spécification ECMAScript. Les emplacements internes ne sont pas des propriétés d'objet ...
Le gel de l'objet n'a donc aucun effet sur sa capacité à muter son emplacement interne [[DateValue]]
.
Vous pouvez geler un Date
, ou effectivement de toute façon: remplacez toutes ses méthodes de mutation par des fonctions no-op (ou des fonctions qui génèrent une erreur) puis freeze
il. Mais comme observé par zzzzBov (Sympa!), cela n'empêche pas quelqu'un de faire Date.prototype.setTime.call(d, 0)
( dans une tentative délibérée de contourner l'objet gelé, ou en tant que sous-produit d'un code compliqué qu'ils utilisent). C'est donc fermer, mais pas de cigare.
Voici un exemple (j'utilise les fonctionnalités ES2015 ici, car j'ai vu que let
dans votre code, vous aurez donc besoin d'un navigateur récent pour l'exécuter; mais cela peut être fait avec des fonctionnalités uniquement ES5 également ):
"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>
I pensez toutes les méthodes de mutation de Date
commencent par set
, mais sinon, il est facile de modifier ce qui précède.
De documents de MDN sur Object.freeze
(soulignement le mien):
Les valeurs ne peuvent pas être modifiées pour les propriétés des données. Les propriétés des accesseurs (getters et setters) fonctionnent de la même manière (et donnent toujours l'illusion que vous modifiez la valeur). Notez que les valeurs qui sont des objets peuvent toujours être modifiés, sauf s'ils sont également gelés.
La méthode setTime
de l'objet Date ne modifie pas une propriété de l'objet Date, elle continue donc de fonctionner, malgré le blocage de l'instance.
Vous pouvez l'envelopper dans une structure de classe et définir des getters et setters personnalisés afin d'empêcher un changement indésirable
C'est une très bonne question!
Réponse de T.J. Crowder a une excellente solution, mais cela m'a fait réfléchir: que pouvons-nous faire d'autre? Comment contourner la Date.prototype.setTime.call(yourFrozenDate)
?
Une façon directe consiste à fournir une fonction AndrewDate
qui enveloppe une date. Il a tout ce qu'une date a moins les 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
Cela crée un objet qui possède toutes les propriétés d'un objet date réel et utilise Function.prototype.bind
pour définir leur this
.
Ce n'est pas une façon infaillible de rassembler autour des clés, mais j'espère que vous pouvez voir mon intention.
Mais attendez ... en regardant un peu plus loin ici et là, nous pouvons voir qu'il existe une meilleure façon de procéder.
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!
...sorte de. Voir, Firefox est le seul en ce moment à implémenter des proxys, et pour des raisons bizarres, les objets de date ne peuvent pas être mandatés. De plus, vous remarquerez que vous pouvez toujours faire des choses comme 'setDate' in proxyDate
et vous verrez les compléments dans la console. Pour surmonter ce problème, davantage de pièges doivent être fournis; spécifiquement, has
, enumerate
, ownKeys
, getOwnPropertyDescriptor
et qui sait quels sont les cas Edge étranges!
... Donc, à la réflexion, cette réponse est presque inutile. Mais au moins, nous nous sommes amusés, non?
La réponse acceptée est en fait imparfaite, je le crains. Vous pouvez réellement geler une instance de n'importe quel objet, y compris une instance de Date
. À l'appui de @ zzzzBov réponse, le gel d'une instance d'objet n'implique pas que l'état de l'objet devienne constant.
Une façon de prouver qu'une instance Date
est vraiment figée 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 le comportement que je suppose que vous désirez 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)