javascript中的不可变类型

时间:2011-10-13 19:52:32

标签: javascript oop types immutability

在我看来,Javascript中不可变类型是不可能的,或者有没有人知道创建它们的任何技巧?这是好事还是坏事?

例如,

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

}

当然,这不是真正不可改变的,但如果它是1)有充分证据表明属性'x'和'y'是getter-functions或者2)在验证不变性时会抛出某种警报然后它可以充当事实上的不可变对象。

思想?

6 个答案:

答案 0 :(得分:19)

您可以使用Object.freeze(o);在新浏览器中使对象不可变。

Point2D因此可以这样实现:

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

现在没有新属性可以添加到Point2D对象,并且它的现有属性不能更改:

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

如果您只想锁定对象以不添加任何新属性,则可以使用Object.seal(o);代替。这将允许您改变现有属性,但不能添加新属性。

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

freezeseal是ECMAScript 5.1的一部分described more formally here

MDN表示支持freeze

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

或者,您可以使用更具功能性的编码风格:

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

用法就像:

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

我知道无法改变"属性" xy使用此方法,因为它们受Point2D函数范围的约束。这种方法在javascript中并不常见(据我所知),但类似于如何在例如Scheme中实现消息传递/ OO。

答案 1 :(得分:5)

如果您不必担心旧浏览器,可以查看Object.defineProperty

除此之外,我认为没有太多选项,因为对象上的任何函数/属性都可以在JavaScript中的任何位置重新定义。

答案 2 :(得分:2)

可以在javascript中定义属性getter和setter:

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

    });

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

    });
}

然而,他们修改的基础变量将是可变的。

答案 3 :(得分:2)

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

由于privateX和privateY仅存在于构造函数的范围内,因此只能通过构造函数中定义的函数访问它们(getX,getY)。

答案 4 :(得分:0)

现在有一个新的javascript保留字constconst使运行时常量不可变。

注意:const不会将对象或数组渲染为不可变的。

通过使用Object.freeze来防止对象突变,你可以有点不可变。

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

DEMO

答案 5 :(得分:-1)

投入一些ES2015可以提供帮助:

i