Why does setting positions in a subclass of Array not change its length? Should I not subclass array?

前端 未结 4 1891
深忆病人
深忆病人 2021-01-19 04:56

In the CoffeeScript program below, I create a subclass of Array which sets two positions in its constructor:

class SetPositionsArray extends Arr         


        
相关标签:
4条回答
  • 2021-01-19 05:08

    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.

    0 讨论(0)
  • 2021-01-19 05:11

    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
    
    0 讨论(0)
  • 2021-01-19 05:20

    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
    
    0 讨论(0)
  • 2021-01-19 05:30

    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
    
    0 讨论(0)
提交回复
热议问题