有没有办法将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);