Según la documentación de MDNObject.freeze()
:
El
Object.freeze()
método 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 vuelve 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 parece que no funciona. 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 setTime
fallara o no hiciera nada. ¿Alguna idea de cómo congelar una cita?
javascript
node.js
immutability
Andrew Eisenberg
fuente
fuente
Object.freeze()
todo el objeto. Parece funcionar, excepto por este problema de fecha.Respuestas:
No lo creo. Usted puede conseguir cerca , sin embargo, ver debajo de la línea de abajo. Pero primero veamos por qué simplemente
Object.freeze
no funciona.Lo haría si se
Date
usara una propiedad de objeto para mantener su valor de tiempo interno, pero no es así. En su lugar, utiliza una[[DateValue]]
ranura interna . Las ranuras internas no son propiedades:Por lo tanto, congelar el objeto no tiene ningún efecto sobre su capacidad para mutar su
[[DateValue]]
ranura interna.Usted puede congelar una
Date
o eficacia de todos modos: Reemplazar todos sus métodos mutadores con funciones no-op (o funciones que arrojan un error) y luegofreeze
TI. Pero, como se observa por zzzzBov (agradable uno!) , Que no impide a alguien de hacerDate.prototype.setTime.call(d, 0)
(en un intento deliberado de moverse por el objeto congelado, o como un subproducto de un código complicado que está utilizando). Asi que esta cerca , pero no puro.Aquí hay un ejemplo (estoy usando las funciones de ES2015 aquí, ya que vi eso
let
en su código, por lo que necesitará un navegador reciente para ejecutarlo; pero esto también se puede hacer con las funciones exclusivas de ES5):Mostrar fragmento de código
"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 de mutación para
Date
empezarset
, pero si no, es fácil modificar los anteriores.fuente
You can freeze a Date, or effectively so anyway: Replace all its mutator methods with no-ops and then freeze it.
¡Gran idea!Date
" - ... ish, sólo recuerda que sobrescribir la función con un no-op puede evitarse llamando a la función de laDate.prototype
tales comoDate.prototype.setTime.call(frozenDate, 0)
, por supuesto, en la práctica esto sería ridículo, pero muestra de pruebas locales que trabajan al menos en Chrome.valueOf
marca de tiempo / y hacer que esa propiedad sea inmutable, y simplemente lanzarla a un nuevoDate
objeto cuando lo necesite?De los documentos de MDN en
Object.freeze
(énfasis mío):El
setTime
método del objeto Date no cambia una propiedad del objeto Date, por lo que continúa funcionando, a pesar de haber congelado la instancia.fuente
¡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 rodear el
Date.prototype.setTime.call(yourFrozenDate)
?1er intento: "Wrapper"
Una forma directa es proporcionar una
AndrewDate
función 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 hace es crear un objeto que tiene todas las propiedades que tiene un objeto de fecha real y utiliza
Function.prototype.bind
para establecer suthis
.Esta no es una forma infalible de reunir las llaves, pero espero que puedas ver mi intención.
Pero espera ... mirándolo un poco más aquí y allá, podemos ver que hay una mejor manera de hacer esto.
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 solucionamos!
...algo así como. Mira, Firefox es el único en este momento que implementa proxies, y por algunas razones extrañas, los objetos de fecha no se pueden usar. Además, notará que aún puede hacer cosas como
'setDate' in proxyDate
y verá finalizaciones en la consola. Para superar eso, es necesario proporcionar más trampas; específicamente,has
,enumerate
,ownKeys
,getOwnPropertyDescriptor
y quién sabe qué extraños casos extremos hay!... Entonces, pensándolo bien, esta respuesta es casi inútil. Pero al menos nos divertimos, ¿no?
fuente
Puede envolverlo en una estructura similar a una clase y definir captadores y definidores personalizados para evitar un cambio no deseado
fuente
La respuesta aceptada es en realidad defectuosa, me temo. De hecho, puede congelar una instancia de cualquier objeto, incluida una instancia de
Date
. En apoyo de @zzzzBov , congelar una instancia de objeto no implica que el estado del objeto se vuelva constante.Una forma de demostrar que una
Date
instancia 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 que supongo que desea de la siguiente manera:
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)
fuente