2013-02-09 4 views
21

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

Для меня неизменяемость - это ограничение времени проектирования, которое гарантировано статично с помощью проверки типа.

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

+6

Зачем вам нужно сортировать массив? Потому что вам нужно его сортировать для того, чтобы X Y Z. – Jon

+2

Невосприимчивость ортогональна статической типизации. Если ваша программа ожидает/требует, чтобы объект не был изменен, тогда «замораживание» объекта будет защищать от кода, который [неправильно] пытается его изменить. – 2013-02-09 20:44:34

+0

@bonomo Это ортогонально, на самом деле. Возьмем такой язык, как Java. И, скажем, интерфейс 'List '. Предположим, мы создаем новый изменяемый объект, например 'l = new ArrayList '. Теперь предположим, что мы делаем 'Collections.unmodifiableList (l)', который возвращает новый список, а также 'List '. Однако этот новый список является неизменным, а исходный список изменен. Оба соответствуют «Перечислению ». – 2013-02-09 22:29:58

ответ

9

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

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

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

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

+7

Это не отвечает на вопрос. – delnan

+1

Я думаю, что ОП знает, что он делает, но хочет знать, когда это было бы полезно. – Wex

6

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

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

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

+1

1. Вы говорите, что немедленное исключение - лучший способ пойти, чем позволить объекту модифицироваться и передаваться? Что касается меня, то в любом случае это плохо. –

+1

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

+0

2. Программисты не будут предупреждать, скорее они получат исключение, если объект замерзнет, ​​и они забыли сделать проверку isFrozen. Другой источник проблем во время выполнения. Позвольте мне сказать это по-разному, вы торгуете проблемами одного вида для разных видов проб. –

0

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

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

Вы могли бы просто сделать:

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

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

+3

Это будет «TypeError» – Plynx

+0

Только в строгом соответствии. – Wex

+4

Этот подход не имеет никакого отношения к читабельности кода. Я думаю, что это плохая идея использовать его таким образом. Более того, somd реализаций trhow исключение при попытке модифицировать замороженный объект. –

0

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

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

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

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

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

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

0

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

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

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

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

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

Enforce кодирования Протокол/ограничения

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

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

+0

google typescript –

+0

Почему? Как машинописный текст помогает ответить на вопрос, для чего используется «Object.freeze»? – light

+1

Я полностью не согласен. Как насчет функционального программирования? Я хочу быть уверенным, что мои чистые функции ничего не меняют, даже случайно. – op1ekun

3

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

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

Кроме того, Chrome is showing significant performance increases working with frozen objects.

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

Для простоты, пример может быть:

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

return Object.freeze(US_COIN_VALUE); 

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

+2

Я исхожу из языков статического типа, где в момент компиляции применяется неизменяемость. Я не могу увидеть большую ценность в так называемой «неизменности», выполняемой во время выполнения, потому что нарушения неизбежны, и каждое нарушение означает крах. Это лучше, чем ничего, но намного меньше того, что вы получили бы со статической проверкой. Следовательно, мой вопрос, есть ли что-то еще для него, кроме использования раннего сбоя при попытке изменить так называемый неизменяемый объект? –

+0

Я отредактировал свой ответ, чтобы уточнить использование «реальной жизни» объекта Object.freeze(). Надеюсь, это полезно. Хотя вы не согласны с его использованием, это очень хорошо подходит для моей цели, и я считаю, что использую его по одной из причин, по которым это было предназначено. Подумайте о принятии его в качестве ответа. – PigBoT

0

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

1

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


Проблема

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

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

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

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

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

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


Раствор

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

Готово!

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

Конечно, программист может обойти это путем доступа __proto__, например:

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

Но мне нравится думать, что в этом случае они на самом деле знают, что они делают, и есть хороший повод, чтобы сделать это ,

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


UPDATE: использование Proxy для решения тех же задач для объектов

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

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

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

0

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

0

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

2

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

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

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

const a = Object.freeze({ 
    foo: 'Hello', 
    bar: 'world', 
    baz: '!' 
}); 
Смежные вопросы