【问题标题】:Why does setting positions in a subclass of Array not change its length? Should I not subclass array?为什么在 Array 的子类中设置位置不会改变它的长度?我不应该继承数组吗?
【发布时间】:2011-11-19 00:06:53
【问题描述】:

在下面的 CoffeeScript 程序中,我创建了一个 Array 的子类,它在其构造函数中设置了两个位置:

class SetPositionsArray extends Array
  constructor: (x,y) ->
    @[0] = x
    @[1] = y

  myLength: ->
    @length

sp_array = new SetPositionsArray 1, 2

console.log "sp_array: "
console.log sp_array
console.log "sp_array[0]: "
console.log sp_array[0]
console.log "sp_array[1]: "
console.log sp_array[1]
console.log "sp_array.length: "
console.log sp_array.length
console.log "sp_array.myLength(): "
console.log sp_array.myLength()

我希望这段代码能改变sp_arraylength 属性,因为它有效地设置了它的位置。但是,我得到的输出是:

$ coffee sp.coffee
sp_array: 
[ 1, 2 ]
sp_array[0]: 
1
sp_array[1]: 
2
sp_array.length: 
0
sp_array.myLength(): 
0

即长度为0。

然后,我创建了另一个类,它在实例中推送值而不是设置它们:

class PushValuesArray extends Array
  constructor: (x,y) ->
    @push x
    @push y

  myLength: ->
    @length


pv_array = new PushValuesArray 1, 2


console.log "pv_array: "
console.log pv_array
console.log "pv_array[0]: "
console.log pv_array[0]
console.log "pv_array[1]: "
console.log pv_array[1]
console.log "pv_array.length: "
console.log pv_array.length
console.log "pv_array.myLength(): "
console.log pv_array.myLength()

在这种情况下,我得到了预期的结果,除了数组中有一个 实际 length 属性(虽然我想这将是一些内部细节):

$ coffee pv.coffee
pv_array: 
[ 1, 2, length: 2 ]
pv_array[0]: 
1
pv_array[1]: 
2
pv_array.length: 
2
pv_array.myLength(): 
2

那么,为什么设置数组中的位置不会改变它的长度呢?

这个问题与this one 相关,我为此发布了this answer

【问题讨论】:

    标签: javascript arrays coffeescript


    【解决方案1】:

    最简单的解释是:length 是魔法。

    length 显然不像普通属性,因为当您在其对象上插入/删除其他属性时,它会更改其值(相反,设置length = 0 将删除其他属性);但是 identifier “长度”并没有什么特别之处。这意味着您可以轻松写foo.length = 'bar',世界将继续转动。只有在数组上才有它的特殊性。

    现在,您可能会期望当您 extend Array 构造函数时,您会得到一个数组——但是您呢?好吧,从某种意义上说,你这样做了:

    class PushValuesArray extends Array
    (new PushValuesArray) instanceof Array  # true
    

    不幸的是,对于length,你没有。 extends 关键字在这里所做的只是创建一个原型链,而数组 prototype 具有一个明显不神奇的 length 属性:

    Array::length  # 0
    

    这就是您在 PushValuesArray 实例上获得的全部内容。可悲的是,没有办法在您自己的对象上复制 length 魔法。您唯一的选择是改写一个函数(例如,size()),或者使用您想要的方法修改 Array 原型并改用真正的数组。

    总结一下:子类化Array 不会让你走得太远。这就是为什么,例如,jQuery 在其对象上有一个非常类似于数组的 API——

    $('body').length  # 1
    $('body')[0]      # [object HTMLBodyElement]
    

    ——但实际上并不使这些对象继承自 Array 原型:

    $('body') instanceof Array  # false
    

    【讨论】:

      【解决方案2】:

      Array.length 是 doesn't work when subclassed 的特殊属性。

      如果您查看第一个示例的已编译 javascript,CoffeeScript 构建类的方式是创建一个函数并调用 __extends(SetPositionsArray, Array);。此方法无法将长度的概念“复制”到您的新类中。

      【讨论】:

        【解决方案3】:

        以下 Array 子类,使用 node.js 并在 coffeescript 中实现,使用 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
        

        【讨论】:

          【解决方案4】:

          我意识到我在这里玩的比较晚,但你不应该做你正在做的事情。这是我一直在修改的一个示例,我认为它不适合您。

          class ArrayWrapper
            constructor: (name, args...)
              @name = name
              @push.apply(@, args)
          

          一旦完成,我就可以这样做:

          >>> relation = new Relation('foo',1,2,3,4,5)
          Object[1, 2, 3, 4, 5]
          >>> relation.length
          5
          

          【讨论】:

            猜你喜欢
            • 2010-10-04
            • 1970-01-01
            • 2012-10-23
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2017-06-24
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多