É possível aplicar ou simular imutabilidade em um “objeto” tipo Map no JavaScript?

Antes de tudo, é preciso esclarecer algumas coisas.

Todos os mecanismos utilizados na pergunta para tornar um objeto “imutável” são apenas válidos para objetos, de modo que podemos definir propriedades imutáveis:

  • Object.defineProperty permite a definição de uma propriedade com atributos customizados. De modo, você pode definir uma propriedade com ((Writable)) e ((Configurable)) falses, tornando-a imutável.

Ou modificar o comportamento de um objeto já existente:

  • Object.freeze, o suprassumo dos três, essencialmente, torna um objeto imutável, de modo que:
    1. Previne a modificação de suas propriedades;
    2. Previne a remoção de suas propriedades; e:
    3. Previne a adição de novas propriedades.
  • Object.seal, similar ao Object.freeze, mas um pouco menos rigoroso, já que ainda permite a modificação de propriedades já existentes. Então:
    1. Previne a remoção de suas propriedades; e:
    2. Previne a adição de novas propriedades.
  • Object.preventExtensions, que apenas previne a adição de novas propriedades. É o mais fraquinho dos três.

Mas repare que esses quatro mecanismos trabalham exclusivamente sobre objetos, atuando sobre os descritores de propriedade. Somente a adição de novas propriedades que não tem a ver com os descritores, mas sim com a propriedade interna ((Extensible)), que faz parte de um objeto (e não de suas propriedades, tal como os atributos de propriedade).


Desse modo, não é esperado que esses quatro mecanismos funcionem para imutabilizar instâncias de Map.

Para entender melhor, vamos comparar as formas como objetos e mapas (instâncias de Map) armazenam seus valores.

Objetos Instâncias de Map
Armazenam seus valores, qualificados por uma chave, como uma propriedade do próprio objeto. Armazenam seus valores, qualificados por uma chave, como um “membro” do “armazém” interno de cada Map.

Desse modo, ao contrário de objetos, cujos valores armazenados podem ser acessados pelo programador (através de APIs do próprio JavaScript), os valores de mapas são mantidos em um “armazém” protegido do programador.

Isso significa que, a partir das APIs do JavaScript, é impossível tornar, de fato, algum membro de um mapa imutável.


O que poderia ser feito é criar uma “subclasse” de Map chamada ImmutableMap que, ao sobrescrever o método set, impede qualquer tipo de modificação em valores. Ademais, valores só podem ser adicionados na construção.

class ImmutableMap extends Map {
  constructor(initialEntries) {
    super();
    for (const (key, val) of initialEntries) {
      super.set(key, val);
    }
  }
  
  // Sobrescreve o método `set`:
  set() {
    console.log('This map is readonly.');
    // Não vou lançar erro para fins didáticos, mas seria de bom tom:
    // throw new TypeError('Attempted to mutate a readonly map.');
  }
}

const imap = new ImmutableMap((
  ('name', 'Luiz Felipe'),
  ('publicUserId', 'lffg')
));

console.log(imap.get('name')); //=> 'Luiz Felipe'
imap.set('name', 'Luiz Felipe 2'); //=> This map is readonly.
console.log(imap.get('name')); //=> 'Luiz Felipe'

Claro que realmente a utilidade disso me parece mínima, mas a ideia é essa.