Skip to content

Instantly share code, notes, and snippets.

@pr1001
Created November 20, 2011 15:29
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save pr1001/1380360 to your computer and use it in GitHub Desktop.
Save pr1001/1380360 to your computer and use it in GitHub Desktop.
Immutable Javascript object properties

Wish you had some immutability in Javascript? Now you can!

var t = {a: 1, b: 2}.immutable();
console.log(t.a, t.b);
try {
  t.a = 3; // ->  Uncaught Error: a is immutable and cannot be modified.
} catch (e) {
  console.log(e);
}
var t2 = t.copy({a: 3});
console.log(t2.a, t2.b);
t2.mutable();
t2.a = 1;
console.log(t2.a, t2.b);

As you can see, the methods immutable, copy, and mutable are added to Object.prototype. This is done using custom getters and setters to mask the original properties, so it only works with objects that expose them and not things like strings. Of course, if you figure out how to get this to work for other Javascript datatypes, please let me know!

Object.prototype.immutable = function immutable() {
for (var k in this) {
// find all the properties of the object
if (k != "immutable" && this.hasOwnProperty(k)) {
// copy value to a 'hidden' property
this["_" + k] = this[k];
delete this[k];
// make them immutable by moving them and replacing them with getters and setters
// silly closures
(function (obj, key) {
obj.__defineGetter__(key, function() {
return obj["_" + key];
})
obj.__defineSetter__(key, function(input) {
throw new Error(key + " is immutable and cannot be modified.");
})
})(this, k);
}
}
return this;
}
Object.prototype.mutable = function mutable() {
for (var k in this) {
// find all the properties of the object
if (k != "immutable" && k.charAt(0) != '_' && this.hasOwnProperty(k) && this.hasOwnProperty('_' + k)) {
// if we have a getter and setter, remove them
(function (obj, key) {
delete obj[key];
obj[key] = obj["_" + key];
delete obj["_" + key];
})(this, k);
}
}
return this;
}
function clone(obj) {
// Handle the 3 simple types, and null or undefined
if (null == obj || "object" != typeof obj) return obj;
// Handle Date
if (obj instanceof Date) {
var copy = new Date();
copy.setTime(obj.getTime());
return copy;
}
// Handle Array
if (obj instanceof Array) {
var copy = [];
for (var i = 0; i < obj.length; ++i) {
copy[i] = clone(obj[i]);
}
return copy;
}
// Handle Object
if (obj instanceof Object) {
var copy = {};
for (var attr in obj) {
if (obj.hasOwnProperty(attr)) copy[attr] = clone(obj[attr]);
}
return copy;
}
throw new Error("Unable to copy obj! Its type isn't supported.");
}
Object.prototype.copy = function copy(params) {
// need to force a real copy, not a reference
var cp = clone(this);
cp.mutable();
for (var k in params) {
if (params.hasOwnProperty(k)) {
cp[k] = clone(params[k]);
}
}
return cp.immutable();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment