Laut MDN- Object.freeze()
Dokumentation :
Die
Object.freeze()
Methode friert ein Objekt ein, dh es wird verhindert, dass neue Eigenschaften hinzugefügt werden. verhindert, dass vorhandene Eigenschaften entfernt werden; und verhindert, dass vorhandene Eigenschaften oder deren Aufzählbarkeit, Konfigurierbarkeit oder Beschreibbarkeit geändert werden. Im Wesentlichen wird das Objekt effektiv unveränderlich gemacht. Die Methode gibt das eingefrorene Objekt zurück.
Ich hatte erwartet, dass das Aufrufen des Einfrierens an einem Datum Änderungen an diesem Datum verhindern würde, aber es scheint nicht zu funktionieren. Folgendes mache ich (mit 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)
Ich hätte erwartet, dass der Anruf setTime
entweder fehlschlägt oder nichts tut. Irgendwelche Ideen, wie man ein Date einfriert?
quelle
Object.freeze()
das gesamte Objekt auf. Es scheint zu funktionieren, bis auf dieses kleine Date-Problem.Antworten:
Das glaube ich nicht. Sie können erhalten nah , aber unter der unten stehenden Zeile. Aber zuerst wollen wir sehen, warum es einfach
Object.freeze
nicht funktioniert.Es wäre , wenn
Date
eine Objekteigenschaft zu halten seine interne Zeitwert, aber es funktioniert nicht verwendet. Stattdessen wird ein[[DateValue]]
interner Steckplatz verwendet. Interne Slots sind keine Eigenschaften:Das Einfrieren des Objekts hat also keinen Einfluss auf seine Fähigkeit, seinen
[[DateValue]]
internen Steckplatz zu mutieren .Sie können a einfrieren
Date
, oder so effektiv: Ersetzen Sie alle seine Mutator-Methoden durch No-Op-Funktionen (oder Funktionen, die einen Fehler auslösen) und dannfreeze
. Aber wie von zzzzBov (nett!) Beobachtet , hindert das niemanden daranDate.prototype.setTime.call(d, 0)
(in einem absichtlichen Versuch, das eingefrorene Objekt zu , oder als Nebenprodukt eines komplizierten Codes, den sie verwenden). Also ist es nah , aber keine Zigarre.Hier ist ein Beispiel (ich verwende hier ES2015-Funktionen, da ich das
let
in Ihrem Code gesehen habe, daher benötigen Sie einen aktuellen Browser, um es auszuführen; dies kann jedoch auch nur mit ES5-Funktionen durchgeführt werden):Code-Snippet anzeigen
"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>
Ich denke, alle Mutator-Methoden
Date
beginnen damitset
, aber wenn nicht, ist es einfach, die oben genannten zu optimieren.quelle
You can freeze a Date, or effectively so anyway: Replace all its mutator methods with no-ops and then freeze it.
Großartige Idee!Date
" einfrieren - ... ish, denken Sie daran, dass das Überschreiben der Funktion mit einem No-Op umgangen werden kann, indem Sie die Funktion von einemDate.prototype
solchen aus aufrufen, wieDate.prototype.setTime.call(frozenDate, 0)
dies in der Praxis natürlich lächerlich wäre, aber lokale Tests zeigen, dass es funktioniert Zumindest in Chrome.valueOf
/ timestamp zu verwenden und diese Eigenschaft unveränderlich zu machen - und sie einfach nachDate
Bedarf in ein neues Objekt zu werfen ?Aus den Dokumenten
Object.freeze
von MDN (Schwerpunkt Mine):Die
setTime
Methode des Date-Objekts ändert keine Eigenschaft des Date-Objekts und funktioniert daher weiterhin, obwohl die Instanz eingefroren wurde.quelle
Das ist eine wirklich gute Frage!
Die Antwort von TJ Crowder hat eine ausgezeichnete Lösung, aber ich habe darüber nachgedacht: Was können wir noch tun? Wie können wir das umgehen
Date.prototype.setTime.call(yourFrozenDate)
?1. Versuch: "Wrapper"
Eine direkte Möglichkeit besteht darin, eine
AndrewDate
Funktion bereitzustellen , die ein Datum umschließt. Es hat alles, was ein Datum hat, abzüglich der Setter: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
Dadurch wird ein Objekt erstellt, das alle Eigenschaften eines tatsächlichen Datumsobjekts aufweist und
Function.prototype.bind
zum Festlegen seiner Objekte verwendetthis
.Dies ist keine narrensichere Art, sich um die Schlüssel zu versammeln, aber hoffentlich können Sie meine Absicht sehen.
Aber warte ... wenn wir es hier und da etwas weiter betrachten, können wir sehen, dass es einen besseren Weg gibt, dies zu tun.
2. Versuch: Proxies
function SuperAndrewDate(realDate) { return new Proxy(realDate, { get(target, prop) { if (!prop.startsWith('set')) { return Reflect.get(target, prop); } } }); } var proxyDate = SuperAndrewDate(new Date());
Und wir haben es gelöst!
... irgendwie. Siehe, Firefox ist derzeit die einzige, die Proxys implementiert, und aus bizarren Gründen können Datumsobjekte nicht als Proxys verwendet werden. Außerdem werden Sie feststellen, dass Sie immer noch Dinge wie tun können,
'setDate' in proxyDate
und Sie werden Vervollständigungen in der Konsole sehen. Um dies zu überwinden, müssen mehr Fallen bereitgestellt werden. Insbesonderehas
,enumerate
,ownKeys
,getOwnPropertyDescriptor
und wer weiß , was seltsam Rand Fällen gibt es!... Beim zweiten Gedanken ist diese Antwort fast sinnlos. Aber zumindest hatten wir Spaß, oder?
quelle
Sie können es in eine klassenähnliche Struktur einschließen und benutzerdefinierte Getter und Setter definieren, um unerwünschte Änderungen zu vermeiden
quelle
Die akzeptierte Antwort ist leider fehlerhaft. Sie können tatsächlich eine Instanz eines beliebigen Objekts einfrieren, einschließlich einer Instanz von
Date
. Zur Unterstützung der Antwort von @zzzzBov bedeutet das Einfrieren einer Objektinstanz nicht, dass der Status des Objekts konstant wird.Ein Weg, um zu beweisen, dass eine
Date
Instanz wirklich eingefroren ist, besteht darin, die folgenden Schritte auszuführen: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
Aber Sie können das Verhalten erreichen, das Sie vermutlich wie folgt wünschen:
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)
quelle