简体   繁体   中英

How to concat two subclassed arrays?

I have two array subclasses: Nodes and Beacons. When I run .concat on two of their instances, it creates a new array with 2 elements: the entire nodes array instance, and the entire beacons array instance. What should I do to make it work as expected?

Nodes.js

mgm.Nodes = function() {
  Array.apply(this, arguments);
  this.fetch();
};

mgm.Nodes.prototype = Object.create(Array.prototype);
mgm.Nodes.prototype.constructor = mgm.Nodes;

mgm.Nodes.prototype.create = function(data) {
  //mgm.Node is an object and should be fine to replace with Object for testing purposes
  var node = new mgm.Node(data);
  this.push(node);
  return node;
};

mgm.Nodes.prototype.get = function(id) {
};

mgm.Nodes.prototype.fetch = function() {
  var n = 20;
  for (var i = 0; i < n; i++) {
    var data = {
      id: i,
      attributes: {name: undefined,}
    };
    this.create(data);
  }
};

mgm.Nodes.prototype.remove = function(node) {
  return this.splice(this.indexOf(node), 1)[0];
};

mgm.Nodes.prototype.update = function(node) {
  // TODO: bind this to node.on('save')
};

Beacons.js

mgm.Beacons = function() {
  this.fetch();
};

mgm.Beacons.prototype = Object.create(Array.prototype);
mgm.Beacons.constructor = mgm.Beacons;

mgm.Beacons.prototype.create = function(data) {
  //mgm.Beacon is an object and should be fine to replace with Object for testing purposes
  var beacon = new mgm.Beacon(data);
  this.push(beacon);
  return beacon;
};

mgm.Beacons.prototype.fetch = function() {
  this.create({x: 200, y: 150, fixed: true, tag: ''});
};

Running this results in a length 2 array (expected 21), where the 0th element has a length of 20:

var nodes = new Nodes();
var beacons = new Beacons();
console.log(nodes.concat(beacons));

It creates a new array with 2 elements: the entire nodes array instance, and the entire beacons array instance.

That's because they are no array instances. See what the concat method does to determine whether the to-be-concatenated objects are concatenated item-by-item:

If the value of the [[Class]] internal property of E is "Array", then

[iterate E and push each single element]

else [put E as a whole on the result array]

Your Beacons and Nodes might inherit from Array.prototype , but they are simple objects - no special [[Class]], no auto-adjusting length property. Btw, Array.apply(this, arguments); does not work as expected, it simply creates a new Array. Use this.push.apply(this, arguments); .

What should I do to make it work as expected?

You can overwrite concat with your own implementation:

mgm.Nodes.prototype.concat = mgm.Beacons.prototype.concat = function concat(other) {
    var O = Object(this),
        A = new Array(), // new this.constructor() ?
        n = 0,
        items = [O];
    items.push.apply(items, arguments);
    while (items.length > 0) {
        var E = items.shift();
        if (typeof E.length == "number") { // not: Array.isArray(E)
            var k = 0,
                len = E.length;
            while (k < len) {
                var P = ""+k;
                if (Object.prototype.hasOwnProperty.call(E, P)) {
                    var subElement = E[P];
                    A[n] = subElement;
                    n++;
                }
                k++;
            }
        } else { // E is not Array-like
            A[n] = E;
            n++;
        }
    }
    return A;
};

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM