entries - ¿Hay alguna manera de Object.freeze() una fecha de JavaScript?
object is js (5)
De acuerdo con la documentación de
MDN
Object.freeze()
:
El método
Object.freeze()
congela un objeto: es decir, evita que se le agreguen nuevas propiedades; evita que se eliminen las propiedades existentes; e impide que se modifiquen las propiedades existentes, o su enumerabilidad, configurabilidad o capacidad de escritura. En esencia, el objeto se hace efectivamente inmutable. El método devuelve el objeto que se congela.
Esperaba que llamar a congelar en una fecha evitaría cambios en esa fecha, pero no parece estar funcionando. Esto es lo que estoy haciendo (ejecutando 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)
Hubiera esperado que la llamada a
setTime
fallara o no hiciera nada.
¿Alguna idea de cómo congelar una cita?
¿Hay alguna manera de Object.freeze () una fecha de JavaScript?
No lo creo.
Sin embargo, puede acercarse, ver debajo de la línea a continuación.
Pero primero veamos por qué solo
Object.freeze
no funciona.
Esperaba que llamar a congelar en una fecha evitaría cambios en esa fecha ...
Lo haría
si
Date
usara una propiedad de objeto para mantener su valor de tiempo interno, pero no lo hace.
Utiliza una
ranura interna
[[DateValue]]
su
lugar.
Las ranuras internas
no son propiedades:
Las ranuras internas corresponden al estado interno asociado con los objetos y utilizado por varios algoritmos de especificación ECMAScript. Las ranuras internas no son propiedades del objeto ...
Por lo tanto, congelar el objeto no tiene ningún efecto sobre su capacidad de mutar su ranura interna
[[DateValue]]
.
Puede congelar una
Date
, o efectivamente de todos modos: Reemplace todos sus métodos de mutación con funciones no operativas (o funciones que arrojan un error) y luego
freeze
.
Pero según lo
observed
por
zzzzBov
(¡bonito!)
,
Date.prototype.setTime.call(d, 0)
no impide que alguien haga
Date.prototype.setTime.call(d, 0)
(en un intento deliberado de evitar el objeto congelado, o como un subproducto de algún complicado código que están usando).
Así que está
cerca
, pero no hay cigarro.
Aquí hay un ejemplo (estoy usando las funciones de ES2015 aquí, ya que vi que
let
su código, por lo que necesitará un navegador reciente para ejecutarlo; pero esto también se puede hacer con las funciones solo de ES5):
"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>
Creo
que
todos los métodos mutantes de
Date
comienzan con
set
, pero si no, es fácil modificar lo anterior.
¡Esta es una muy buena pregunta!
La respuesta de TJ Crowder
tiene una excelente solución, pero me hizo pensar: ¿qué más podemos hacer?
¿Cómo podemos ir alrededor de
Date.prototype.setTime.call(yourFrozenDate)
?
1er intento: "Contenedor"
Una forma directa es proporcionar una función
AndrewDate
que envuelva una fecha.
Tiene todo lo que tiene una fecha menos los 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
Lo que esto hace es crear un objeto que tiene todas las propiedades que tiene un objeto de fecha real y utiliza
Function.prototype.bind
para establecer su
this
.
Esta no es una forma infalible de reunir las llaves, pero espero que puedan ver mi intención.
Pero espera ... mirándolo un poco más aquí y allá, podemos ver que hay una mejor manera de hacerlo.
2do intento: 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());
¡Y lo resolvimos!
... más o menos.
Mira, Firefox es el único en este momento que implementa proxies, y por algunas extrañas razones, los objetos de fecha no pueden ser proxies.
Además, notará que aún puede hacer cosas como
''setDate'' in proxyDate
y verá las finalizaciones en la consola.
Para superar eso, se deben proporcionar más trampas;
específicamente,
has
,
enumerate
,
ownKeys
,
getOwnPropertyDescriptor
y ¡quién sabe qué casos extraños existen!
... Entonces, pensándolo bien, esta respuesta es casi inútil. Pero al menos nos divertimos, ¿verdad?
De
los documentos de MDN en
Object.freeze
(el énfasis es mío):
Los valores no se pueden cambiar para las propiedades de datos. Las propiedades de acceso (getters y setters) funcionan igual (y aún dan la ilusión de que está cambiando el valor). Tenga en cuenta que los valores que son objetos aún pueden modificarse, a menos que también estén congelados.
El método
setTime
del objeto Date no está cambiando una propiedad del objeto Date, por lo que continúa funcionando, a pesar de haber congelado la instancia.
La respuesta aceptada es realmente defectuosa, me temo.
En realidad, puede congelar una instancia de cualquier objeto, incluida una instancia de
Date
.
En apoyo de la
zzzzBov
de
zzzzBov
, congelar una instancia de objeto no implica que el estado del objeto se vuelva constante.
Una forma de demostrar que una instancia de
Date
está realmente congelada es siguiendo los pasos a continuación:
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
Pero puede lograr el comportamiento, supongo que desea lo siguiente:
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)
Puede envolverlo en una clase como estructura y definir captadores y definidores personalizados para evitar un cambio no deseado