You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
182 lines
3.7 KiB
182 lines
3.7 KiB
|
|
/** |
|
* Module dependencies. |
|
*/ |
|
|
|
var Symbol = require('es6-symbol'); |
|
var debug = require('debug')('array-index'); |
|
|
|
var get = Symbol('get'); |
|
var set = Symbol('set'); |
|
var length = Symbol('length'); |
|
|
|
/** |
|
* 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; |
|
ArrayIndex.get = get; |
|
ArrayIndex.set = set; |
|
|
|
/** |
|
* Subclass this. |
|
*/ |
|
|
|
function ArrayIndex (_length) { |
|
Object.defineProperty(this, 'length', { |
|
get: getLength, |
|
set: setLength, |
|
enumerable: false, |
|
configurable: true |
|
}); |
|
|
|
this[length] = 0; |
|
|
|
if (arguments.length > 0) { |
|
setLength.call(this, _length); |
|
} |
|
} |
|
|
|
/** |
|
* You overwrite the "get" Symbol in your subclass. |
|
*/ |
|
|
|
ArrayIndex.prototype[ArrayIndex.get] = function () { |
|
throw new Error('you must implement the `ArrayIndex.get` Symbol'); |
|
}; |
|
|
|
/** |
|
* You overwrite the "set" Symbol in your subclass. |
|
*/ |
|
|
|
ArrayIndex.prototype[ArrayIndex.set] = function () { |
|
throw new Error('you must implement the `ArrayIndex.set` Symbol'); |
|
}; |
|
|
|
/** |
|
* 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; |
|
var l = this.length; |
|
var 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 a; |
|
}; |
|
|
|
/** |
|
* Getter for the "length" property. |
|
* Returns the value of the "length" Symbol. |
|
*/ |
|
|
|
function getLength () { |
|
debug('getting "length": %o', this[length]); |
|
return this[length]; |
|
}; |
|
|
|
/** |
|
* Setter for the "length" property. |
|
* Calls "ensureLength()", then sets the "length" Symbol. |
|
*/ |
|
|
|
function setLength (v) { |
|
debug('setting "length": %o', v); |
|
return this[length] = ensureLength(this, v); |
|
}; |
|
|
|
/** |
|
* Ensures that getters/setters from 0 up to "_newLength" have been defined |
|
* on `Object.getPrototypeOf(this)`. |
|
* |
|
* @api private |
|
*/ |
|
|
|
function ensureLength (self, _newLength) { |
|
var newLength; |
|
if (_newLength > MAX_LENGTH) { |
|
newLength = MAX_LENGTH; |
|
} else { |
|
newLength = _newLength | 0; |
|
} |
|
var proto = Object.getPrototypeOf(self); |
|
var cur = proto[length] | 0; |
|
var num = newLength - cur; |
|
if (num > 0) { |
|
var desc = {}; |
|
debug('creating a descriptor object with %o entries', num); |
|
for (var i = cur; i < newLength; i++) { |
|
desc[i] = setup(i); |
|
} |
|
debug('calling `Object.defineProperties()` with %o entries', num); |
|
Object.defineProperties(proto, desc); |
|
proto[length] = newLength; |
|
} |
|
return newLength; |
|
} |
|
|
|
/** |
|
* 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[ArrayIndex.get](index); |
|
} |
|
function set (v) { |
|
return this[ArrayIndex.set](index, v); |
|
} |
|
return { |
|
enumerable: true, |
|
configurable: true, |
|
get: get, |
|
set: set |
|
}; |
|
}
|
|
|