Подтвердить что ты не робот

Зачем мне нужно заморозить объект в JavaScript?

Мне не ясно, когда кому-то понадобится использовать Object.freeze в JavaScript. MDN и MSDN не дают реальных примеров, когда это полезно. Я понял, что не могу изменить его во время выполнения, что вызвано сбоем. Вопрос скорее в том, когда я оценил бы аварию.

Для меня неизменность - это ограничение времени проектирования, которое должно быть гарантировано средством проверки типов.

Так есть ли смысл иметь сбой во время выполнения на динамически типизированном языке, кроме обнаружения нарушения лучше позже, чем никогда?

4b9b3361

Ответ 1

Функция Object.freeze выполняет следующие действия:

  • Делает объект не расширяемым, поэтому новые свойства не могут быть добавлены к нему.
  • Устанавливает настраиваемый атрибут в false для всех свойств объекта. Когда - настраиваемый - false, атрибуты свойств не могут быть изменены и свойство не может быть удалено.
  • Устанавливает атрибут writeable для false для всех свойств данных объекта. Когда writeable is false, значение свойства данных не может быть изменено.

Что бы какая часть, но почему кто-нибудь это сделал?

Ну, в объектно-ориентированной парадигме существует понятие, что существующий API содержит определенные элементы, которые не предназначены для расширения, изменения или повторного использования за пределами их текущего контекста. Наиболее подходящей аналогией является ключевое слово final на разных языках. Даже в языках, которые не компилируются и поэтому легко модифицируются, они все еще существуют, то есть PHP, и в этом случае JavaScript.

Ответ 2

Вы можете использовать это, когда у вас есть объект, представляющий логически неизменяемую структуру данных, особенно если:

  • Изменение свойств объекта или изменение его типа "утка" может привести к плохому поведению в другом месте вашего приложения.
  • Объект похож на изменяемый тип или иначе выглядит изменчивым, и вы хотите, чтобы программисты были предупреждены о попытке изменить его, а не получили поведение undefined.

Как автор API, это может быть именно то поведение, которое вы хотите. Например, у вас может быть внутренне кэшированная структура, представляющая каноническую реакцию сервера, которую вы предоставляете пользователю вашего API по ссылке, но по-прежнему используете внутренне для различных целей. Ваши пользователи могут ссылаться на эту структуру, но изменение может привести к тому, что ваш API будет иметь поведение undefined. В этом случае вы хотите, чтобы исключение было выбрано, если ваши пользователи попытаются его изменить.

Ответ 3

В моей серверной среде nodejs я использую freeze по той же причине, по которой я использую 'use strict'. Если у меня есть объект, который я не хочу расширять или изменять, я замораживаю его. Если что-то пытается расширить или изменить мой замороженный объект, я ХОЧУ, чтобы мое приложение выдало ошибку.

Для меня это относится к последовательному, качественному, более безопасному коду.

Кроме того, Chrome демонстрирует значительное повышение производительности при работе с замороженными объектами.

Редактировать: В моем последнем проекте я отправляю/получаю зашифрованные данные между государственными органами. Есть много значений конфигурации. Я использую замороженные объекты для этих значений. Изменение этих значений может иметь серьезные побочные эффекты. Кроме того, как я уже упоминал ранее, Chrome демонстрирует преимущества в производительности с замороженными объектами, я полагаю, что и nodejs тоже.

Для простоты пример будет:

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

return Object.freeze( US_COIN_VALUE );

Нет причин изменять значения в этом примере. И наслаждайтесь преимуществами оптимизации скорости.

Ответ 4

Object.freeze, в основном используя Функциональное программирование (неизменность)

Неизменность - это центральная концепция функционального программирования, поскольку без нее поток данных в вашей программе является потерянным. История штата заброшена, и странные ошибки могут проникнуть в ваше программное обеспечение.

В JavaScript важно не смешивать const с неизменностью. const создает привязку имени переменной, которая не может быть переназначена после создания. const не создает неизменяемые объекты. Вы не можете изменить объект, на который ссылается привязка, но вы все равно можете изменить свойства объекта, а это означает, что привязки, созданные с помощью константы, являются изменяемыми, а не неизменными. Неизменяемые объекты не могут быть изменены вообще. Вы можете сделать значение поистине неизменным благодаря глубокому замораживанию объекта. JavaScript имеет метод, который заморозит объект на уровне одного уровня.

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

Ответ 5

Это старый вопрос, но я думаю, что у меня есть хороший случай, когда замораживание может помочь. Сегодня у меня была эта проблема.


Проблема

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 children
        // 2. set _parent to newParent
        // 3. if newParent is not undefined, add this node to newParent children
    }

    addChild(node) { node.parent = this; }
    removeChild(node) { node.parent === this && (node.parent = undefined); }
    ...
}

Как вы можете видеть, при изменении родителя он автоматически обрабатывает соединение между этими узлами, сохраняя синхронизацию дочерних элементов и родительских элементов. Однако здесь есть одна проблема:

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

Теперь myNode имеет newNode в своем children, но newNode не имеет myNode как своего parent. Итак, вы только что сломали его.

(OFF-TOPIC) Почему вы все равно подвергаете детей?

Да, я мог бы просто создать множество методов: countChildren(), getChild (index), getChildrenIterator() (который возвращает генератор), findChildIndex (node) и т.д.... но действительно ли это лучший подход, чем просто возврат массива, который предоставляет интерфейс, который уже знают программисты javascript?

  • Вы можете получить доступ к своему length, чтобы узнать, сколько у него детей;
  • Вы можете получить доступ к детям по их индексу (т.е. children[i]);
  • Вы можете перебирать его с помощью for .. of;
  • И вы можете использовать другие полезные методы, предоставляемые массивом.

Примечание: возврат копии массива не может быть и речи! Это стоит линейного времени, и любые обновления исходного массива не распространяются на копию!


Решение

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

Готово!

  • Object.create: мы создаем объект, который наследует от this._children (т.е. имеет this._children как его __proto__). Это само по себе решает почти всю проблему:
    • Это простое и быстрое (постоянное время)
    • Вы можете использовать все, что предоставляется интерфейсом Array
    • Если вы измените возвращаемый объект, он не изменит оригинал!
  • Object.freeze: однако тот факт, что вы можете изменить возвращаемый объект, НО изменения не влияют на исходный массив, крайне запутанный для пользователя класса! Итак, мы просто замораживаем это. Если он пытается его модифицировать, исключается исключение (при условии строгого режима), и он знает, что не может (и почему). Похоже, что для myFrozenObject[x] = y не исключение, если вы не в строгом режиме, но myFrozenObject в любом случае не изменяется, поэтому он все еще не очень-то странный.

Конечно, программист может обойти его, обратившись к __proto__, например:

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

Но мне нравится думать, что в этом случае они действительно знают, что делают, и имеют веские основания для этого.

ВАЖНО: обратите внимание, что это не так хорошо работает для объектов: использование hasOwnProperty в for.. in всегда возвращает false.


ОБНОВЛЕНИЕ: с помощью Proxy решить ту же проблему для объектов

Просто для завершения: если у вас есть объект вместо массива, вы все равно можете решить эту проблему с помощью прокси. На самом деле, это общее решение, которое должно работать с любым элементом, но я рекомендую (если вы его избежите) из-за проблем с производительностью:

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

Это все еще возвращает объект, который нельзя изменить, но сохраняет все функциональные возможности только для чтения. Если вам действительно нужно, вы можете отказаться от Object.freeze и реализовать требуемые ловушки (set, deleteProperty,...) в прокси, но это требует дополнительных усилий, и поэтому Object.freeze пригодится прокси.

Ответ 6

Я мог видеть, что это полезно, когда вы работаете с интерактивным инструментом. Вместо:

if ( ! obj.isFrozen() ) {
    obj.x = mouse[0];
    obj.y = mouse[1];
}

Вы можете просто сделать:

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

Свойства будут обновляться только в том случае, если объект не заблокирован.

Ответ 7

Я могу думать о нескольких местах, которые Object.freeze пригодится.

Первой реализацией реального мира, которая могла бы использовать freeze, является разработка приложения, которое требует "состояния" на сервере, чтобы соответствовать тому, что в браузере. Например, представьте, что вам нужно добавить уровень разрешений на вызовы функций. Если вы работаете в приложении, могут быть места, где разработчик может легко изменить или перезаписать настройки разрешений, даже не осознавая этого (особенно, если объект передавался через ссылку!). Но разрешения по большому счету никогда не могут меняться, а ошибки при их изменении предпочтительны. Таким образом, в этом случае объект разрешений может быть заморожен, что позволяет разработчику ошибочно ошибочно устанавливать "разрешения". То же самое можно сказать и для пользовательских данных, таких как имя пользователя или адрес электронной почты. Эти вещи могут быть ошибочно или злонамеренно нарушены с плохим кодом.

Другое типичное решение было бы в коде цикла игры. Существует множество настроек состояния игры, которые вы хотите заморозить, чтобы сохранить, что состояние игры поддерживается синхронно с сервером.

Подумайте о Object.freeze как способе создания объекта как константы. В любое время, когда вы хотели бы иметь переменную константу, вы могли бы иметь константу объекта с замораживанием по тем же причинам.

Также есть моменты, когда вы хотите передать неизменяемые объекты через функции и передачу данных, и только разрешить обновление исходного объекта с помощью сеттеров. Это можно сделать путем клонирования и замораживания объекта для "геттеров" и только обновления оригинала с помощью "сеттеров".

Являются ли какие-либо из этих недопустимых вещей? Можно также сказать, что замороженные объекты могут быть более эффективными из-за отсутствия динамических переменных, но я пока не видел никаких доказательств этого.

Ответ 8

Единственное практическое применение для Object.freeze - во время разработки. Для производственного кода нет абсолютно никакой выгоды для замораживания/герметизации объектов.

Глупые опечатки

Это может помочь вам поймать эту очень распространенную проблему во время разработки:

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

В строгом режиме это вызовет ошибку, если myObject был заморожен.

Использовать протокол кодирования/ограничение

Это также поможет в установлении определенного протокола в команде, особенно с новыми членами, которые могут не иметь одинакового стиля кодирования, как и все остальные.

Многие ребята из Java любят добавлять много методов к объектам, чтобы JS чувствовал себя более знакомым. Замораживание объектов помешало бы им сделать это.

Ответ 9

Когда вы пишете библиотеку/фреймворк в JS, и вы не хотите, чтобы какой-либо разработчик нарушил ваше динамическое создание языка, переназначив "внутренние" или общедоступные свойства. Это наиболее очевидный прецедент для неизменности.

Ответ 10

Не знаю, помогает ли это, но я использую его для создания простых перечислений. Это позволяет мне, надеюсь, не получать данные Duff в базе данных, зная, что источник данных пытался быть неизменным без целенаправленной попытки разбить код. Со статически типизированной точки зрения это позволяет обосновать конструкцию кода.

Ответ 11

Какова практическая ситуация, когда вы захотите заморозить объект? Например, при запуске приложения вы создаете объект, содержащий настройки приложения. Вы можете передать этот объект конфигурации в различные модули приложения. Но как только этот объект настроек будет создан, вы хотите знать, что он не будет изменен.

Ответ 12

С выпуском V8 v7.6 производительность замороженных/запечатанных массивов значительно улучшена. Поэтому одна из причин, по которой вы хотели бы заморозить объект, - это когда ваш код критичен к производительности.