有没有办法将Object.freeze()作为JavaScript日期?

有没有办法将Object.freeze()作为JavaScript日期?,javascript,node.js,immutability,Javascript,Node.js,Immutability,根据: Object.freeze()方法冻结一个对象:即防止向其添加新属性;防止现有财产被移除;并防止更改现有属性或其可枚举性、可配置性或可写性。本质上,对象是有效地不可变的。该方法返回正在冻结的对象 我原以为对某个日期调用冻结将阻止对该日期的更改,但它似乎不起作用。下面是我正在做的事情(运行Node.js v5.3.0): 我本以为对setTime的调用要么失败,要么什么也不做。有没有关于如何冻结日期的想法?来自(我的重点): 无法更改数据属性的值访问器属性(getter和setter)的工

根据:

Object.freeze()
方法冻结一个对象:即防止向其添加新属性;防止现有财产被移除;并防止更改现有属性或其可枚举性、可配置性或可写性。本质上,对象是有效地不可变的。该方法返回正在冻结的对象

我原以为对某个日期调用冻结将阻止对该日期的更改,但它似乎不起作用。下面是我正在做的事情(运行Node.js v5.3.0):

我本以为对
setTime
的调用要么失败,要么什么也不做。有没有关于如何冻结日期的想法?

来自(我的重点):

无法更改数据属性的值访问器属性(getter和setter)的工作原理是相同的(并且仍然会产生您正在更改值的错觉)。请注意,作为对象的值仍然可以修改,除非它们也被冻结

Date对象的
setTime
方法没有更改Date对象的属性,因此它继续工作,尽管已冻结实例

有没有办法将Object.freeze()作为JavaScript日期

我不这么认为。不过,你可以靠近一点,看下面的一行。但首先让我们看看为什么仅仅
Object.freeze
不起作用

我希望在某个日期调用冻结将阻止对该日期的更改

如果
日期
使用对象属性来保存其内部时间值,则它将,但它没有。它使用的是一个。这些不是财产:

内部插槽对应于与对象关联并由各种ECMAScript规范算法使用的内部状态。内部插槽不是对象属性

因此,冻结对象不会对其变异其
[[DateValue]]
内部插槽的能力产生任何影响


您可以冻结
日期
,或者有效地冻结日期:将其所有mutator方法替换为无操作函数(或引发错误的函数),然后冻结它。但正如(很好的一个!),这并不能阻止某人执行
Date.prototype.setTime.call(d,0)
(故意绕过冻结的对象,或者作为他们正在使用的一些复杂代码的副产品)。很接近,但没有雪茄

下面是一个示例(我在这里使用ES2015功能,因为我在代码中看到了
let
,所以您需要一个最新的浏览器来运行它;但这也可以通过仅ES5功能来实现):

“严格使用”;
设d=新日期();
冻结日期(d);
d、 设定时间(0);
snippet.log(d);
函数nop(){
}
功能冻结日期(d){
所有名称(d).forEach(名称=>{
if(name.startsWith(“set”)&&typeof d[name]=“function”){
d[名称]=无;
}
});
冻结(d);
返回d;
}
函数全名(obj){
var names=Object.create(null);//或在此处使用映射
var thisObj;
for(thisObj=obj;thisObj;thisObj=Object.getPrototypeOf(thisObj)){
Object.getOwnPropertyNames(thisObj.forEach)(名称=>{
名称[名称]=1;
});
}
返回对象。键(名称);
}


您可以将其封装在类结构中,并定义自定义getter和setter,以防止不希望的更改

这是一个非常好的问题

有一个极好的解决方案,但它让我思考:我们还能做什么?我们怎样才能绕过
Date.prototype.setTime.call(yourFrozenDate)

第一次尝试:“包装器” 一种直接的方法是提供一个包装日期的
和wdate
函数。它包含日期的所有内容,不包括设定者:

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
这样做的目的是创建一个对象,该对象具有实际日期对象所具有的所有属性,并用于设置其
this

这不是一个傻瓜般的收集钥匙的方法,但希望你能看到我的意图

但是等一下,再看远一点,我们可以看到有更好的方法

第二次尝试: 我们解决了

…有点。看,Firefox是目前唯一一个实现代理的浏览器,由于一些奇怪的原因,日期对象不能被代理。此外,您会注意到,您仍然可以在proxyDate中执行类似于“setDate”的操作,并且您将在console中看到完成。为了克服这一问题,需要提供更多的陷阱;特别是,谁知道有什么奇怪的边缘案例


…因此,仔细想想,这个答案几乎毫无意义。但至少我们玩得很开心,对吧?

我担心,公认的答案实际上是有缺陷的您实际上可以冻结任何对象的实例,包括
Date
的实例。为了支持的答案,冻结对象实例并不意味着对象的状态变为常量

证明
日期
实例确实已冻结的一种方法是,遵循以下步骤:

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
但你可以实现我想你想要的行为如下:

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)

让日期成为一个整数对我来说很有效:

let date = new Date();
const integerDate = Date.parse(date);
let unchangedDate = new Date(integerDate);

使用代理对象可能是当今最好的解决方案。基于,我修改并改进了代理处理程序,以实现最大的兼容性:

constnoop=()=>{}
常量dateProxyHandler={
获取(目标、道具、接受者){
如果(prop==Symbol.toStringTag)返回“日期”
if(typeof prop==“string”&&prop.startsWith(“set”))返回noop
常量值=Reflect.get(目标、道具、接收器)
返回值的类型==“函数”&&prop!==“构造函数”
?值绑定(目标)
:价值
},
}
函数冻结(值){
返回值instanceof Date
?新代理(Object.freeze(新日期(数字(值))),dateProxyHandler)
:对象。冻结(值)
}
const frozenDate=冻结(新日期())
frozenDate.setHours(0)//noop
frozenDate.getHours()//工作时间:)
JSON.stringi
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)
let date = new Date();
const integerDate = Date.parse(date);
let unchangedDate = new Date(integerDate);