为什么我需要在JavaScript中冻结对象?

为什么我需要在JavaScript中冻结对象?,javascript,immutability,Javascript,Immutability,我不清楚什么时候有人需要在JavaScript中使用Object.freeze。MDN和MSDN没有给出实际生活中有用的示例。 我得到了一部分关于不能在运行时更改它的信息,这是由崩溃强制执行的。问题是我什么时候会喜欢撞车 对我来说,不变性是一个设计时约束,应该由类型检查器来保证 因此,在动态类型语言中发生运行时崩溃有什么意义呢?除了在以后检测到冲突比从未检测到要好之外?对象的冻结功能执行以下操作: 使对象不可扩展,因此无法向其添加新属性 将对象的所有属性的可配置属性设置为false。如果-co

我不清楚什么时候有人需要在JavaScript中使用
Object.freeze
。MDN和MSDN没有给出实际生活中有用的示例。 我得到了一部分关于不能在运行时更改它的信息,这是由崩溃强制执行的。问题是我什么时候会喜欢撞车

对我来说,不变性是一个设计时约束,应该由类型检查器来保证


因此,在动态类型语言中发生运行时崩溃有什么意义呢?除了在以后检测到冲突比从未检测到要好之外?

对象的
冻结功能执行以下操作:

  • 使对象不可扩展,因此无法向其添加新属性
  • 将对象的所有属性的可配置属性设置为false。如果-configurable为false,则无法更改属性属性,也无法删除属性
  • 将对象的所有数据属性的可写属性设置为false。如果writable为false,则无法更改数据属性值
这就是什么部分,但是为什么会有人这样做


好的,在面向对象的范例中,存在这样一种概念,即现有API包含某些元素,这些元素不打算在当前上下文之外进行扩展、修改或重用。各种语言中的
final
关键字是最合适的类比。即使在未编译且因此易于修改的语言中,它仍然存在,例如PHP,在本例中为JavaScript。

当您有一个表示逻辑上不可变的数据结构的对象时,尤其是在以下情况下,您可以使用它:

  • 更改对象的属性或更改其“duck类型”可能会导致应用程序中其他地方出现不良行为
  • 该对象类似于可变类型,或者看起来是可变的,您希望程序员在尝试更改它时得到警告,而不是获得未定义的行为

作为API作者,这可能正是您想要的行为。例如,您可能有一个内部缓存结构,该结构表示通过引用提供给API用户的规范服务器响应,但仍在内部用于各种目的。用户可以引用此结构,但更改它可能会导致API具有未定义的行为。在这种情况下,如果您的用户试图修改异常,您希望抛出异常。

当您使用交互式工具时,我发现这很有用。而不是:

if ( ! obj.isFrozen() ) {
    obj.x = mouse[0];
    obj.y = mouse[1];
}
你可以简单地做:

obj.x = mouse[0];
obj.y = mouse[1];

只有当对象未冻结时,属性才会更新。

我可以想到几个地方可以使用该对象。冻结会非常方便

第一个可以使用
freeze
的实际实现是在开发需要服务器上的“状态”与浏览器中的状态匹配的应用程序时。例如,假设您需要为函数调用添加一定级别的权限。如果您在应用程序中工作,开发人员可能会在某些地方很容易更改或覆盖权限设置,而根本没有意识到这一点(特别是当对象通过引用传递时!)。但总体而言,权限永远不会更改,更改权限时出错是首选。因此,在这种情况下,权限对象可能会被冻结,从而限制开发人员错误地“设置”权限。对于类似用户的数据,如登录名或电子邮件地址,也可以这样说。这些东西可能被错误地或恶意地用坏代码破坏

另一个典型的解决方案是在游戏循环代码中。有许多游戏状态设置需要冻结,以保持游戏状态与服务器同步

将Object.freeze视为使对象成为常数的一种方法。无论何时,只要您想要变量常量,出于类似的原因,您都可以使用冻结对象常量

有时还需要通过函数和数据传递传递不可变的对象,并且只允许使用setter更新原始对象。这可以通过克隆和冻结“getter”的对象,并且只使用“setters”更新原始对象来实现


这些都是无效的吗?也可以说,由于缺少动态变量,冻结对象的性能可能更高,但我还没有看到任何证据

对象冻结的唯一实际用途是在开发过程中。对于生产代码,冻结/密封对象绝对没有好处

愚蠢的打字错误

它可以帮助您在开发过程中解决这个非常常见的问题:

if (myObject.someProp = 5) {
    doSomething();
}
在严格模式下,如果
myObject
被冻结,则会引发错误

实施编码协议/限制

    get children() { return Object.freeze(Object.create(this._children)); }

    // OR, if you deeply care about performance:
    get children() {
        return this._PUBLIC_children === undefined
            ? (this._PUBLIC_children = Object.freeze(Object.create(this._children)))
            : this._PUBLIC_children;
    }
它还将有助于在团队中实施特定的协议,特别是对于可能与其他人的编码风格不同的新成员


很多Java玩家喜欢向对象添加很多方法,让JS感觉更熟悉。冻结对象会阻止它们这样做。

在我的nodejs服务器环境中,我使用冻结的原因与使用“使用严格”相同。如果我有一个对象,我不想被扩展或修改,我会冻结它。如果有东西试图扩展或修改我冻结的对象,我希望我的应用程序抛出一个错误

对我来说,这涉及到一致、高质量、更安全的代码

而且,

编辑: 在我最近的项目中,我在政府实体之间发送/接收加密数据。有很多配置值。我对这些值使用冻结对象。修改这些值可能会产生严重的副作用。此外,正如我之前链接的,Chrome在冻结obj上显示出性能优势
class Node {
    constructor() {
        this._children = [];
        this._parent = undefined;
    }

    get children() { return this._children; }
    get parent() { return this._parent; }

    set parent(newParent) {
        // 1. if _parent is not undefined, remove this node from _parent's children
        // 2. set _parent to newParent
        // 3. if newParent is not undefined, add this node to newParent's children
    }

    addChild(node) { node.parent = this; }
    removeChild(node) { node.parent === this && (node.parent = undefined); }
    ...
}
let newNode = new Node();
myNode.children.push(newNode);
    get children() { return Object.freeze(Object.create(this._children)); }

    // OR, if you deeply care about performance:
    get children() {
        return this._PUBLIC_children === undefined
            ? (this._PUBLIC_children = Object.freeze(Object.create(this._children)))
            : this._PUBLIC_children;
    }
someNode.children.__proto__.push(new Node());
    get myObject() { return Object.freeze(new Proxy(this._myObject, {})); }
const a = Object.freeze({
  foo: 'Hello',
  bar: 'world',
  baz: '!'
});