3

I'm trying to implement a collection in Javascript - is there anyway to implement an array-like indexer for elements in my collection?

So far, I have the following code:

var Collection = function() {

    var collection = [];

    var addAccessor = function(api, name) {
      if (toString.call(collection[name]) == '[object Function]') {
        api[name] = (function(){
          return function () {
            return collection[name](arguments);
          };
        }());
      }
      else {
        Object.defineProperty(api, name, {
          get: function() { return collection.length; },
          enumerable: true,
          configurable: true
        });
      }
    };

    var publicApi = {};

    var methods = Object.getOwnPropertyNames(Array.prototype);
    for(var i = 0, len = methods.length; i < len; ++i) {
      var method = methods[i];
      addAccessor(publicApi, method);
    }

    return publicApi;
  };
};

All of the Array.prototype methods and properties work as expected.

var c = Collection();
c.push(4);
console.log(c.length);  // 1

But the one thing I can't figure out is how to get the following to work:

console.log(c[0]);     // should print 4, currently undefined

Is there anyway to do this?

2
  • 1
    You can't. Any specific reason you're not just using an Array? Commented Sep 25, 2012 at 15:36
  • I think something is missing in your code. collection is never populated, so no Array prototypes are copied. Can you post a working fiddle? Commented Sep 25, 2012 at 15:42

2 Answers 2

4

If you want to "extend" Array, the classic way would be something like:

function Collection(){};
Collection.prototype = new Array();
Collection.constructor = Collection;

Now add your own methods:

Collection.prototype.color = function() {
    this.push('color');
};

And use it with new:

var myArray = new Collection();
myArray.push(1);
myArray.color();

If you want to add a new push method that access Array push, try:

Collection.prototype.push = function() {
    console.log('pushed!');
    Array.prototype.push.apply(this, [].slice.call(arguments));
};
Sign up to request clarification or add additional context in comments.

5 Comments

This is where I started, but then I needed to raise events on some Array methods. How would I override Collection.prototype.push in this case so that I can execute my own code while still pushing the element onto the array?
Array.prototype.push.apply(this, [].slice.call(arguments)); results in a stack overflow.
Missed the change to the prototype declaration and that was what I missed when I first tried to implement it this way which sent me on my wild goose chase... This works perfectly. Thanks!
Wouldn't it be better to use Collection.prototype = Array.prototype instead of ... = new Array()? That should also include any future additions to the Array prototype itself.
I found the difference: My suggestion connects both prototypes and altering Collection.prototype also affects the base Array.prototype. Using a new Array() avoids that and should be preferred. Future additions to Array.prototype (in this case) are available in new Collection instances anyway.
0

using classes:

class Collection extends Array {
  color() {
    this.push('color')
  }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.