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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/14791302/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-26 22:35:12  来源:igfitidea点击:

Why would I need to freeze an object in JavaScript?

javascriptimmutability

提问by Trident D'Gao

It is not clear to me when anyone would need to use Object.freezein JavaScript. MDN and MSDN don't give real life examples when it is useful. I get that part about not being able to change it at runtime which is enforced by a crash. The question is rather when would I appreciate a crash.

我不清楚什么时候有人需要Object.freeze在 JavaScript 中使用。当 MDN 和 MSDN 有用时,它不会给出现实生活中的例子。我得到关于无法在运行时更改它的那部分,这是由 crash 强制执行的。问题是我什么时候会感谢崩溃。

To me the immutability is a design time constraint which is supposed to be guaranteed by the type checker.

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

So is there any point in having a runtime crash in a dynamically typed language, besides detecting a violation better later than never?

那么在动态类型语言中运行时崩溃还有什么意义,除了晚点检测到违规总比没有好?

回答by Ian Atkin

The Object.freezefunction does the following:

Object.freeze函数执行以下操作:

  • Makes the object non-extensible, so that new properties cannot be added to it.
  • Sets the configurable attribute to false for all properties of the object. When - configurable is false, the property attributes cannot be changed and the property cannot be deleted.
  • Sets the writable attribute to false for all data properties of the object. When writable is false, the data property value cannot be changed.
  • 使对象不可扩展,因此无法向其添加新属性。
  • 将对象的所有属性的可配置属性设置为 false。当 -configure 为 false 时,不能更改属性属性,也不能删除该属性。
  • 将对象的所有数据属性的 writable 属性设置为 false。当 writable 为 false 时,无法更改数据属性值。

That's the whatpart, but whywould anyone do this?

那是什么部分,但为什么有人会这样做呢?

Well, in the object-oriented paradigm, the notion exists that an existing API contains certain elements that are not intended to be extended, modified, or re-used outside of their current context. The finalkeyword in various languages is the most suitable analogy of this. Even in languages that are not compiled and therefore easily modified, it still exists, i.e. PHP, and in this case, JavaScript.

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

回答by Plynx

You can use this when you have an object representing a logically immutable data structure, especially if:

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

  • Changing the properties of the object or altering its "duck type" could lead to bad behavior elsewhere in your application
  • The object is similar to a mutable type or otherwise looks mutable, and you want programmers to be warned on attempting to change it rather than obtain undefined behavior.
  • 更改对象的属性或更改其“鸭子类型”可能会导致应用程序中其他地方的不良行为
  • 该对象类似于可变类型或在其他方面看起来是可变的,并且您希望程序员在尝试更改它而不是获得未定义的行为时得到警告。

As an API author, this may be exactly the behavior you want. For example, you may have an internally cached structure that represents a canonical server response that you provide to the user of your API by reference but still use internally for a variety of purposes. Your users can reference this structure, but altering it may result in your API having undefined behavior. In this case, you want an exception to be thrown if your users attempt to modify it.

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

回答by PigBoT

In my nodejs server environment, I use freeze for the same reason I use 'use strict'. If I have an object that I do not want being extended or modified, I will freeze it. If something attempts to extend or modify my frozen object, I WANT my app to throw an error.

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

To me this relates to consistent, quality, more secure code.

对我来说,这与一致、质量、更安全的代码有关。

Also, Chrome is showing significant performance increases working with frozen objects.

此外, Chrome 在处理冻结对象时显示出显着的性能提升。

Edit: In my most recent project, I'm sending/receiving encrypted data between a government entity. There are a lot of configuration values. I'm using frozen object(s) for these values. Modification of these values could have serious, adverse side effects. Additionally, as I linked previously, Chrome is showing performance advantages with frozen objects, I assume nodejs does as well.

编辑:在我最近的项目中,我在政府实体之间发送/接收加密数据。有很多配置值。我正在为这些值使用冻结对象。修改这些值可能会产生严重的不良副作用。此外,正如我之前链接的那样,Chrome 显示了冻结对象的性能优势,我认为 nodejs 也是如此。

For simplicity, an example would be:

为简单起见,举个例子:

var US_COIN_VALUE = {
        QUARTER: 25,
        DIME: 10,
        NICKEL: 5,
        PENNY: 1
    };

return Object.freeze( US_COIN_VALUE );

There is no reason to modify the values in this example. And enjoy the benefits of speed optimizations.

没有理由修改此示例中的值。并享受速度优化带来的好处。

回答by Hien Huynh

Object.freezemainly using in Functional Programming (Immutability)

Object.freeze主要用于函数式编程(Immutability)

Immutabilityis a central concept of functional programming because without it, the data flow in your program is lossy. State history is abandoned, and strange bugs can creep into your software.

不变性是函数式编程的一个核心概念,因为没有它,程序中的数据流是有损的。状态历史被放弃,奇怪的错误可能会蔓延到您的软件中。

In JavaScript, it's important not to confuse const, with immutability. const creates a variable name binding which can't be reassigned after creation. const does not create immutable objects. You can't change the object that the binding refers to, but you can still change the properties of the object, which means that bindings created with const are mutable, not immutable. Immutable objects can't be changed at all. You can make a value truly immutable by deep freezing the object. JavaScript has a method that freezes an object one-level deep.

在 JavaScript 中,重要的是不要将 const 与不变性混淆。const 创建一个变量名绑定,创建后不能重新分配。const 不会创建不可变对象。您无法更改绑定所引用的对象,但您仍然可以更改对象的属性,这意味着使用 const 创建的绑定是可变的,而不是不可变的。不可变对象根本无法更改。您可以通过深度冻结对象使值真正不可变。JavaScript 有一种方法可以将对象冻结一层

const a = Object.freeze({
  foo: 'Hello',
  bar: 'world',
  baz: '!'
});

回答by Philippe

With the V8 release v7.6the performance of frozen/sealed arrays is greatly improved. Therefore, one reason you would like to freeze an object is when your code is performance-critical.

V8 版本 v7.6中,冻结/密封阵列的性能大大提高。因此,您希望冻结对象的原因之一是您的代码对性能至关重要。

回答by light

The only practical use for Object.freezeis during development. For production code, there is absolutely no benefit for freezing/sealing objects.

唯一的实际用途Object.freeze是在开发期间。对于生产代码,冻结/密封对象绝对没有好处。

Silly Typos

愚蠢的错别字

It could help you catch this very common problem during development:

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

if (myObject.someProp = 5) {
    doSomething();
}

In strict mode, this would throw an error if myObjectwas frozen.

在严格模式下,如果myObject被冻结,这将引发错误。

Enforce Coding Protocol / Restriction

执行编码协议/限制

It would also help in enforcing a certain protocol in a team, especially with new members who may not have the same coding style as everyone else.

它还有助于在团队中执行特定协议,尤其是对于可能与其他人具有不同编码风格的新成员。

A lot of Java guys like to add a lot of methods to objects to make JS feel more familiar. Freezing objects would prevent them from doing that.

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

回答by Stuart Hallows

What is a practical situation when you might want to freeze an object? One example, on application startup you create an object containing app settings. You may pass that configuration object around to various modules of the application. But once that settings object is created you want to know that it won't be changed.

当您可能想要冻结对象时,实际情况是什么?例如,在应用程序启动时,您创建一个包含应用程序设置的对象。您可以将该配置对象传递给应用程序的各个模块。但是一旦创建了该设置对象,您就想知道它不会被更改。

回答by Rafael Perrella

This is an old question, but I think I have a good case where freeze might help. I had this problem today.

这是一个老问题,但我认为我有一个很好的案例,冻结可能会有所帮助。我今天遇到了这个问题。



The problem

问题

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); }
    ...
}

As you can see, when you change the parent, it automatically handles the connection between these nodes, keeping children and parent in sync. However, there is one problem here:

如您所见,当您更改父节点时,它会自动处理这些节点之间的连接,使子节点和父节点保持同步。但是,这里有一个问题:

let newNode = new Node();
myNode.children.push(newNode);

Now, myNodehas newNodein its children, but newNodedoes not have myNodeas its parent. So you've just broken it.

现在,myNodenewNode在它的children,但newNode没有myNode作为它的parent。所以你刚刚打破了它。

(OFF-TOPIC) Why are you exposing the children anyway?

(题外话)你为什么要暴露孩子们?

Yes, I could just create lots of methods: countChildren(), getChild(index), getChildrenIterator() (which returns a generator), findChildIndex(node), and so on... but is it really a better approach than just returning an array, which provides an interface all javascript programmers already know?

是的,我可以创建很多方法:countChildren()、getChild(index)、getChildrenIterator()(返回一个生成器)、findChildIndex(node) 等等……但这真的是比仅仅返回更好的方法吗?一个数组,它提供了一个所有 javascript 程序员都知道的接口?

  1. You can access its lengthto see how many children it has;
  2. You can access the children by their index (i.e. children[i]);
  3. You can iterate over it using for .. of;
  4. And you can use some other nice methods provided by an Array.
  1. 您可以访问它length以查看它有多少个孩子;
  2. 您可以通过他们的索引(即children[i])访问孩子;
  3. 您可以使用for .. of;迭代它
  4. 您可以使用数组提供的其他一些不错的方法。

Note: returning a copy of the array is out of question! It costs linear time, and any updates to the original array do not propagate to the copy!

注意:返回数组的副本是不可能的!它花费线性时间,并且对原始数组的任何更新都不会传播到副本!



The solution

解决方案

    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;
    }

Done!

完毕!

  1. Object.create: we create an object that inherits from this._children(i.e. has this._childrenas its __proto__). This alone solves almost the entire problem:
    • It's simple and fast (constant time)
    • You can use anything provided by the Array interface
    • If you modify the returned object, it does not change the original!
  2. Object.freeze: however, the fact that you can modify the returned object BUT the changes do not affect the original array is extremely confusing for the user of the class! So, we just freeze it. If he tries to modify it, an exception is thrown (assuming strict mode) and he knows he can't (and why). It's sad no exception is thrown for myFrozenObject[x] = yif you are not in strict mode, but myFrozenObjectis not modified anyway, so it's still not-so-weird.
  1. Object.create:我们创建了一个继承自的对象this._children(即this._children作为它的__proto__)。仅此一项就几乎解决了整个问题:
    • 它简单快捷(恒定时间)
    • 你可以使用 Array 接口提供的任何东西
    • 如果修改返回的对象,它不会改变原来的!
  2. Object.freeze: 但是,您可以修改返回的对象但更改不会影响原始数组的事实对于类的用户来说非常混乱!所以,我们只是冻结它。如果他试图修改它,就会抛出一个异常(假设是严格模式)并且他知道他不能(以及为什么)。很遗憾,myFrozenObject[x] = y如果您不是在严格模式下,myFrozenObject则不会抛出异常,但无论如何都不会被修改,所以它仍然不是那么奇怪。

Of course the programmer could bypass it by accessing __proto__, e.g:

当然,程序员可以通过访问绕过它__proto__,例如:

someNode.children.__proto__.push(new Node());

But I like to think that in this case they actually know what they are doing and have a good reason to do so.

但我喜欢认为在这种情况下,他们实际上知道自己在做什么,并且有充分的理由这样做。

IMPORTANT: notice that this doesn't work so well for objects: using hasOwnProperty in the for .. in will always return false.

重要提示:请注意,这对于对象来说效果不佳:在 for .. in 中使用 hasOwnProperty 将始终返回 false。



UPDATE: using Proxyto solve the same problem for objects

更新:使用代理解决对象的相同问题

Just for completion: if you have an object instead of an Array you can still solve this problem by using Proxy. Actually, this is a generic solution that should work with any kind of element, but I recommend against (if you can avoid it) due to performance issues:

只是为了完成:如果你有一个对象而不是一个数组,你仍然可以通过使用代理来解决这个问题。实际上,这是一个适用于任何类型元素的通用解决方案,但由于性能问题,我建议不要(如果可以避免的话):

    get myObject() { return Object.freeze(new Proxy(this._myObject, {})); }

This still returns an object that can't be changed, but keeps all the read-only functionality of it. If you really need, you can drop the Object.freezeand implement the required traps (set, deleteProperty, ...) in the Proxy, but that takes extra effort, and that's why the Object.freezecomes in handy with proxies.

这仍然返回一个无法更改的对象,但保留它的所有只读功能。如果您真的需要,您可以Object.freeze在代理中删除并实现所需的陷阱(set、deleteProperty、...),但这需要额外的努力,这就是为什么Object.freeze代理会派上用场的原因。

回答by WeNeedAnswers

Don't know if this helps, but I use it to create simple enumerations. It allows me to hopefully not get duff data in a database, by knowing the source of the data has been attempted to be unchangeable without purposefully trying to break the code. From a statically typed perspective, it allows for reasoning over code construction.

不知道这是否有帮助,但我用它来创建简单的枚举。它让我希望不会在数据库中获取 duff 数据,因为我知道数据的来源已被尝试不可更改,而不会故意尝试破坏代码。从静态类型的角度来看,它允许对代码构造进行推理。

回答by Alexander Mills

When you're writing a library/framework in JS and you don't want some developer to break your dynamic language creation by re-assigning "internal" or public properties. This is the most obvious use case for immutability.

当您在 JS 中编写库/框架并且您不希望某些开发人员通过重新分配“内部”或公共属性来破坏您的动态语言创建时。这是不变性最明显的用例。