npm
Version:
a package manager for JavaScript
181 lines (150 loc) • 3.7 kB
JavaScript
/**
* Module dependencies.
*/
var util = require('util')
var debug = require('debug')('array-index')
/**
* JavaScript Array "length" is bound to an unsigned 32-bit int.
* See: http://stackoverflow.com/a/6155063/376773
*/
var MAX_LENGTH = Math.pow(2, 32)
/**
* Module exports.
*/
module.exports = ArrayIndex
/**
* Subclass this.
*/
function ArrayIndex (length) {
Object.defineProperty(this, 'length', {
get: getLength,
set: setLength,
enumerable: false,
configurable: true
})
Object.defineProperty(this, '__length', {
value: 0,
writable: true,
enumerable: false,
configurable: true
})
if (arguments.length > 0) {
this.length = length
}
}
/**
* You overwrite the "__get__" function in your subclass.
*/
ArrayIndex.prototype.__get__ = function () {
throw new Error('you must implement the __get__ function')
}
/**
* You overwrite the "__set__" function in your subclass.
*/
ArrayIndex.prototype.__set__ = function () {
throw new Error('you must implement the __set__ function')
}
/**
* Converts this array class into a real JavaScript Array. Note that this
* is a "flattened" array and your defined getters and setters won't be invoked
* when you interact with the returned Array. This function will call the
* getter on every array index of the object.
*
* @return {Array} The flattened array
* @api public
*/
ArrayIndex.prototype.toArray = function toArray () {
var i = 0, l = this.length, array = new Array(l)
for (; i < l; i++) {
array[i] = this[i]
}
return array
}
/**
* Basic support for `JSON.stringify()`.
*/
ArrayIndex.prototype.toJSON = function toJSON () {
return this.toArray()
}
/**
* toString() override. Use Array.prototype.toString().
*/
ArrayIndex.prototype.toString = function toString () {
var a = this.toArray()
return a.toString.apply(a, arguments)
}
/**
* inspect() override. For the REPL.
*/
ArrayIndex.prototype.inspect = function inspect () {
var a = this.toArray()
Object.keys(this).forEach(function (k) {
a[k] = this[k]
}, this)
return util.inspect(a)
}
/**
* Getter for the "length" property.
* Returns the value of the "__length" property.
*/
function getLength () {
debug('getting "length": %o', this.__length)
return this.__length
}
/**
* Setter for the "length" property.
* Calls "ensureLength()", then sets the "__length" property.
*/
function setLength (v) {
debug('setting "length": %o', v)
return this.__length = ensureLength(v)
}
/**
* Ensures that getters/setters from 0 up to "_length" have been defined
* on `ArrayIndex.prototype`.
*
* @api private
*/
function ensureLength (_length) {
var length
if (_length > MAX_LENGTH) {
length = MAX_LENGTH
} else {
length = _length | 0
}
var cur = ArrayIndex.prototype.__length__ | 0
var num = length - cur
if (num > 0) {
var desc = {}
debug('creating a descriptor object with %o entries', num)
for (var i = cur; i < length; i++) {
desc[i] = setup(i)
}
debug('done creating descriptor object')
debug('calling `Object.defineProperties()` with %o entries', num)
Object.defineProperties(ArrayIndex.prototype, desc)
debug('finished `Object.defineProperties()`')
ArrayIndex.prototype.__length__ = length
}
return length
}
/**
* Returns a property descriptor for the given "index", with "get" and "set"
* functions created within the closure.
*
* @api private
*/
function setup (index) {
function get () {
return this.__get__(index)
}
function set (v) {
return this.__set__(index, v)
}
return {
enumerable: true
, configurable: true
, get: get
, set: set
}
}