read entries defineproperties javascript node.js immutability

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