How to create an immutable object in JavaScript?

14

I know that it is possible to "freeze" an object in JavaScript using the Object.freeze method:

var x = { foo:"bar", complexo:[1,2,3] };
Object.freeze(x);
x.foo = "baz"; // Não tem efeito
console.log(x.foo); // bar

However, the complex objects within it remain mutable:

x.complexo[1] = 10;
console.log(x.complexo); // [1,10,3]

How do you make the entire object immutable?

    
asked by anonymous 18.02.2014 / 02:09

2 answers

9

According to the Object.freeze documentation , no there is a ready method for this, it is necessary to create a special function to iterate over the object's fields and apply freeze to each of them:

function deepFreeze (o) {
  var prop, propKey;
  Object.freeze(o); // Primeiro congela o objeto.
  for (propKey in o) {
    prop = o[propKey];
    if (!o.hasOwnProperty(propKey) || !(typeof prop === "object") || Object.isFrozen(prop)) {
      // Se o objeto está no prototype, não é um objeto, ou já está congelado, pule.
      // Note que isso pode deixar uma referência não congelada em algum lugar no objeto
      // se já existe um objeto congelado contendo outro objeto não congelado.
      continue;
    }

    deepFreeze(prop); // Chama deepFreeze recursivamente.
  }
}

Example of the special case mentioned in the code (to avoid it, comment on the part where it checks if the object is already frozen so as not to freeze it again):

var raso = { foo:{ bar:"baz" } };
Object.freeze(raso); // { bar:"baz" } continua não congelado
var fundo = { raso:raso }
deepFreeze(fundo); // raso não é afetado, pois já estava congelado
fundo.raso.foo.bar = 42; // Atribui corretamente

However, special care must be taken in cases where the object has circular references. This is not a problem in the above code as it stands (since "frozen" checking prevents the same object from being visited twice - thus avoiding an infinite loop), but becomes a problem if that test is removed .

Finally, it is good to mention that depending on the implementation freezing an object can negatively impact the performance (as cited #) - contrary to what would normally be expected, that such immutability would bring the possibility of optimizations that a mutable object does not allow.

    
18.02.2014 / 02:09
3
The Object class in ECMAScript 5 allows you to set a READ-ONLY property on an arbitrary object via the defineProperty method and thus create immutable objects in JavaScript, but this only works on the more modern Browsers that implement ES5 (IE9 +, Chrome, Safari, Firefox and Opera).

var o1 = {}; // Cria um novo objeto

Object.defineProperty(o1, "a", {
    value : 37,
    writable : false // define a propriedade "a" como imutável
});

console.log(o1.a); // loga o valor 37 de "a"

// Nehum Error será lançado na tentativa 
    // de atualizar o valor (somente será lançado se 
    // usarmos o strict mode e neste caso será lançado
    // mesmo se o novo valor seja igual ao original.
o1.a = 25;
console.log(o1.a); // loga 37. A atribuição não tem efeito.

In this proposal the solution is by Design and should be defined in Analysis time and more appropriate when using the Immutable pattern in JavaScript.

An attempt to later redefine this property with the value writable: true will throw a TypeError . See illustration below:

    
18.02.2014 / 14:53