It seems to me that immu开发者_如何学Pythontable types are impossible in Javascript, or does anyone know of any tricks to create them? Is it a good or bad practice?
For instance something like,
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();
}
Which of course isn't really immutable, but if it were either 1) well-documented that the properties 'x' and 'y' are getter-functions or 2) threw some kind of alert when validating for immutability then it could act as a de-facto immutable object.
Thoughts?
You can use Object.freeze(o);
to make an object immutable in newish browsers.
The Point2D
could thus be implemented like this:
var Point2D = function(x, y) {
this.x = x;
this.y = y;
Object.freeze(this);
}
Now no new properties can be added to a Point2D
object and it's existing properties can not be altered:
> 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}"
If you only want to lock down the object to not have any new properties added then you can use Object.seal(o);
instead. This will allow you to mutate existing properties, but not add new ones.
> 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
and seal
is part of ECMAScript 5.1 described more formally here
MDN states that freeze
is supported in:
- Firefox (Gecko) 4 (2.0)
- Chrome 6
- Internet Explorer 9
- Opera 12
- Safari 5.1
Alternatively, you could use a more functional coding style:
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");
}
};
}
Usage would then be like:
> var p = Point2D(1,2)
undefined
> p("x")
1
> p("y")
2
> p("foo")
Error: Property 'foo' not supported
I know of no way to alter the "properties" x
and y
using this aproach since they are bound by the scope of the Point2D
function. This approach is not commonly seen in javascript (as far as I know), but is similar to how message passing/OO can be achieved in for instance Scheme.
If you don't have to worry about older browsers you can look into Object.defineProperty
.
Other than that, I don't think there is much of an option since any function/property on an object can be redefined at any point in JavaScript.
It is possible in javascript to define property getters and setters:
function Point2D(){
this.__defineGetter__("x", function(){
});
this.__defineSetter__("x", function(val){
});
}
The underlying variables they modify will, however, be mutable.
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());
Since privateX and privateY only exist in the scope of the constructor, they can only be accessed by the functions defined in the constructor (getX, getY).
There is now a new javascript reserved word const
. const
makes runtime constants that are immutable.
Note: const does NOT render objects or arrays to be immutable.
You can be kinda sorta immutable-ish by using Object.freeze
to prevent mutation of objects.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/const http://caniuse.com/#search=const
DEMO
Throwing some ES2015 in the mix can help:
function Point2D(x, y) {
const instanceX = x,
instanceY = y;
return {
get x() { return instanceX; },
get y() { return instanceY; }
}
}
精彩评论