Skip to content

Instantly share code, notes, and snippets.

@Fordi
Last active August 29, 2015 14:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Fordi/d7d741b655474e057fd5 to your computer and use it in GitHub Desktop.
Save Fordi/d7d741b655474e057fd5 to your computer and use it in GitHub Desktop.
/**
* ImmutableArray - exactly what it sounds like: an Array you can't change.
*
* Usage:
* var myArray = [1, 2, 3],
* myImmutableArray = myArray.immutable;
*
* ImmutableArrays can be used in place of normal Arrays in most cases, with the exception of the following mutator functions:
* #pop(), #push(item), #reverse(), #shift(), #unshift(item), #splice(start, length, items...), and #sort([sortfn])
* If used, these will throw a warning, copy the array, mutate it, and return the mutated array.
*/
(function () {
"use strict";
/**
* @private
* Create a PropertyDescriptor for Object.defineProperty / Object.defineProperties that describes a property that cannot be changed.
*/
function fixedProp(value) {
return {
enumerable: false,
configurable: false,
writable: false,
value: value
};
}
/**
* @private
* Create a PropertyDescriptor for Object.defineProperty / Object.defineProperties that describes a permanent getter
*/
function getter(get) {
return {
enumerable: false,
configurable: false,
get: get
}
}
/**
* @private
* The list of methods on Array.prototype that are known to mutate the array
*/
var mutators = ["pop", "push", "reverse", "shift", "unshift", "splice", "sort"],
/**
* @private
* Building the extension to ImmutableArray that will override mutator functions with copier functions, and emit a warning
*/
immutableProto = mutators.reduce(function (proto, methodName) {
proto[methodName] = fixedProp(function () {
console.warn("[Code Quality]: Calling ImmutableArray#" + methodName + "() doesn't change it; please use #mutate(callback, context), or #slice() it first. Returning the mutated copy of the array instead of the normal return value.");
var copy = this.slice();
copy[methodName].apply(copy, arguments);
return copy;
});
return proto;
}, {});
Object.defineProperties(Array.prototype, {
/**
* Returns a new immutable Array based on this Array.
*/
immutable: getter(function () {
return new ImmutableArray(this);
}),
/**
* Provided to create the same idempotent interface on Arrays and ImmutableArrays. Since Arrays are mutable, this returns identity
*/
mutable: getter(function () {
return this;
}),
/**
* Get a copy of the Array; an alias for this.slice().
*/
copy: getter(function () {
return this.slice();
}),
/**
* Create a new ImmutableArray from a mutation of an existing Array or ImmutableArray
* @param Function callback(mutableArray) The developer-supplied method used to mutate the array
* @param Object context The context in which to mutate the array
*/
mutate: fixedProp(function (callback, context) {
var mutableArray = this.slice();
callback.call(context || null, mutableArray);
return mutableArray.immutable;
})
});
/**
* @private
* @constructor
* Creates an ImmutableArray
* @param Array content Defines the content for the ImmutableArray
*/
function ImmutableArray(content) {
this.length = content.length;
for (let i = 0; i < content.length; i += 1) {
this[i] = content[i];
}
Object.defineProperty(this, 'constructor', fixedProp(ImmutableArray));
Object.preventExtensions(this);
Object.freeze(this);
}
ImmutableArray.prototype = [];
Object.defineProperties(ImmutableArray.prototype, immutableProto);
Object.defineProperties(ImmutableArray.prototype, {
/**
* Provided to create the same idempotent interface on Arrays and ImmutableArrays.
* Since ImmutableArrays are immutable, this returns identity
*/
immutable: getter(function () {
return this;
}),
/**
* Create a mutable copy of this ImmutableArray
*/
mutable: getter(function () {
return this.slice();
}),
/**
* Get a "copy" of this ImmutableArray. Since ImmutableArrays are immutable, this can be identity.
*/
copy: getter(function () {
return this;
})
});
}());
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment