In the CoffeeScript program below, I create a subclass of Array
which sets two positions in its constructor:
class SetPositionsArray extends Arr
Array.length is a special property that doesn't work when subclassed.
If you look at the compiled javascript for your first example the way CoffeeScript builds classes is by creating a function and calling __extends(SetPositionsArray, Array);
. This method can't 'copy' the concept of length into your new class.
The simplest explanation is: length
is magic.
length
obviously doesn't behave like an ordinary property, since it changes its value when you insert/delete other properties on its object (and, conversely, setting length = 0
will delete other properties); but there's nothing special about the identifier "length". That means you can easily write foo.length = 'bar'
, and the world will keep turning. Only on arrays does it have its special nature.
Now, you might expect when when you extend
the Array
constructor, that you get an array—but do you? Well, in one sense you do:
class PushValuesArray extends Array
(new PushValuesArray) instanceof Array # true
Unfortunately, for the purpose of length
, you don't. All the extends
keyword does here is create a prototype chain, and the Array prototype has a distinctly non-magical length
property:
Array::length # 0
That's all you get on your PushValuesArray
instance. Sadly, there's no way to duplicate that length
magic on your own objects. Your only option is to either write a function instead (say, size()
), or modify the Array
prototype with the methods you want and use true arrays instead.
To sum up: Subclassing Array
won't get you very far. That's why, for instance, jQuery has a very array-like API on its objects—
$('body').length # 1
$('body')[0] # [object HTMLBodyElement]
—but doesn't actually make those objects inherit from the Array prototype:
$('body') instanceof Array # false
The following Array subclass, using node.js and implemented in coffeescript, works by using util.inherits & Object.defineProperty
util = require 'util'
class NGArray extends []
constructor : ->
util.inherits @, Array
Object.defineProperty NGArray.prototype, "length",
get: ->
i = 0
for k,v of @
i++ unless isNaN parseInt(k)
i
array = new NGArray()
array[0] = 'value'
array[1] = true
array[2] = ->
array[3] = 568
array.push(false)
array.pop()
console.log(array.length) #4
I realize I'm rather late in the game here, but you shouldn't need to do what you're doing. Here's an example I've been tinkering with and I see no reason why it shouldn't work for you.
class ArrayWrapper
constructor: (name, args...)
@name = name
@push.apply(@, args)
Once this is in place I can do this:
>>> relation = new Relation('foo',1,2,3,4,5)
Object[1, 2, 3, 4, 5]
>>> relation.length
5