Il me semble que les types immuables sont impossibles en Javascript, ou quelqu'un connaît-il des astuces pour les créer? Est-ce une bonne ou une mauvaise pratique?

Par exemple, quelque chose comme,

var Point2D = function Point2D(x, y) {

    var _x = x;
    var _y = y;

    function constructor() {

        var self = {};

        // Pseudo-Immutable concept
        self.x = function() {
            return _x;
        }();

        self.y = function() {
            return _y;
        }();

        return self;
    }

    return constructor();

}

Ce qui bien sûr n'est pas vraiment immuable, mais s'il était soit 1) bien documenté que les propriétés 'x' et 'y' sont des fonctions getter ou 2) a lancé une sorte d'alerte lors de la validation de l'immuabilité, alors il pourrait agir comme un objet immuable de facto.

Pensées?

9
Sean Thoman 13 oct. 2011 à 23:52

6 réponses

Meilleure réponse

Si vous n'avez pas à vous soucier des anciens navigateurs, vous pouvez consulter {{ X0}}.

En dehors de cela, je ne pense pas qu'il y ait beaucoup d'options car toute fonction / propriété sur un objet peut être redéfinie à tout moment en JavaScript.

6
Felix Kling 13 oct. 2011 à 19:57
Point2D = function(x, y)
{
    var privateX = x;
    var privateY = y;

    return {
        getX: function()
        {
            return privateX;
        },
        getY: function()
        {
            return privateY;
        }
    };
};

var foo = new Point2D(20, 30);
console.log(foo.getX(), foo.getY());

Étant donné que privateX et privateY n'existent que dans la portée du constructeur, ils ne sont accessibles que par les fonctions définies dans le constructeur (getX, getY).

2
Scott A 13 oct. 2011 à 20:19

Il est possible en javascript de définir des getters et setters de propriétés:

function Point2D(){
    this.__defineGetter__("x", function(){

    });

    this.__defineSetter__("x", function(val){

    });
}

Les variables sous-jacentes qu'elles modifient seront cependant mutables.

2
Zack Bloom 13 oct. 2011 à 19:56

Il y a maintenant un nouveau mot réservé javascript const. const rend les constantes d'exécution immuables.

Remarque: const ne rend PAS les objets ou les tableaux immuables.

Vous pouvez être un peu immuable en utilisant Object.freeze pour empêcher la mutation des objets.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/const http://caniuse.com/#search=const

DEMO

0
Fresheyeball 20 nov. 2016 à 22:30

Vous pouvez utiliser Object.freeze(o); pour rendre un objet immuable dans les nouveaux navigateurs.

Le Point2D pourrait donc être implémenté comme ceci:

var Point2D = function(x, y) { 
    this.x = x; 
    this.y = y;
    Object.freeze(this);
}  

Désormais, aucune nouvelle propriété ne peut être ajoutée à un objet Point2D et ses propriétés existantes ne peuvent pas être modifiées:

> var p = new Point2D(99, 123)
undefined
> p.x
99
> p.x = 7
7
> p.x
99
> p.foo = "This won't be added"
undefined
> JSON.stringify(p);
"{"x":99,"y":123}"

Si vous souhaitez uniquement verrouiller l'objet pour qu'aucune nouvelle propriété ne soit ajoutée, vous pouvez utiliser Object.seal(o); à la place. Cela vous permettra de muter les propriétés existantes, mais pas d'en ajouter de nouvelles.

> var o = {x:1, y:2}
undefined
> Object.seal(o);
[object Object]
> JSON.stringify(o);
"{"x":1,"y":2}"
> o.foo = "This won't be added";
99
> o.x = 37 // Seal allows to mutate object 
37
JSON.stringify(o);
"{"x":37,"y":2}"

freeze et seal font partie d'ECMAScript 5.1 décrit de manière plus formelle ici

MDN indique que freeze est pris en charge dans:

  • Firefox (Gecko) 4 (2.0)
  • Chrome 6
  • Internet Explorer 9
  • Opera 12
  • Safari 5.1

Alternativement, vous pouvez utiliser un style de codage plus fonctionnel:

var Point2D = function(x, y) { 
   return function(prop) { 
      switch (prop) {
         case "x": return x;
         case "y": return y;
         default: throw new Error("Property '" + prop + "' not supported");
      }
   };
}

L'utilisation serait alors comme:

> var p = Point2D(1,2)
undefined
> p("x")
1
> p("y")
2
> p("foo")
Error: Property 'foo' not supported

Je ne connais aucun moyen de modifier les "propriétés" x et y en utilisant cette approche car elles sont liées par la portée de la fonction Point2D. Cette approche n'est pas communément vue en javascript (pour autant que je sache), mais est similaire à la façon dont le passage de message / OO peut être réalisé par exemple dans Scheme.

19
Emil H 30 avril 2014 à 09:52

Le lancement d'ES2015 dans le mélange peut aider:

function Point2D(x, y) {
  const instanceX = x,
    instanceY = y;

  return {
    get x() { return instanceX; },
    get y() { return instanceY; }
  }
}
-1
kon.simeonov 11 mars 2017 à 11:05
7759672