How to create an immutable object in Javascript?

Asked

Viewed 817 times

14

I know it is possible to "freeze" a Javascript object through the method Object.freeze:

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 changeable:

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

How to make the whole object immutable?

  • 2

    Perfect to have created another question and answered it instead of leaving it lost in the constant variable answer at http://answall.com/q/6190/2884. With this answer here will make it easier for those who use Google later

  • 1

    +1 well cool, the answer is the same as I would, but I hope that few people go using this resource, after all I already have my reservations regarding use of constants, imagine now explain to someone who does not understand the scope that he gave a Freeze on an object that is being used and needs to be manipulated in another context but belonging to the same scope.

2 answers

9


According to the documentation of Object.freeze, there is no ready method for this, it is necessary to create a special function to iterate on the fields of the object and apply freeze in 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 the part where it checks if the object is already frozen to not 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 should be taken with cases where the object has circular references. This is not a problem in the above code as it is (since checking for "already frozen" prevents the same object from being visited twice - and therefore avoids an infinite loop), but becomes a problem if this test is removed.

Finally, it is worth mentioning that depending on the implementation freezing an object can negatively impact performance (as cited in a comment to a related question) - contrary to what would normally be expected, that this immutability would bring the possibility of optimizations that a mutable object does not allow.

3

The class Object in Ecmascript 5 allows defining a READ-ONLY property in an arbitrary object via the method defineProperty and with this we can create immutable objects in Javascript, but this only works in the most 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 Immutable Javascript.

An attempt to reset later in your code this property with the value writable: true will launch a Typeerror. See illustration below:

inserir a descrição da imagem aqui

Browser other questions tagged

You are not signed in. Login or sign up in order to post.