Javascript 为什么在数组的子类中设置位置不改变其长度?我不应该子类数组吗?

Javascript 为什么在数组的子类中设置位置不改变其长度?我不应该子类数组吗?,javascript,arrays,coffeescript,Javascript,Arrays,Coffeescript,在下面的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

在下面的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_数组
length
属性,因为它可以有效地设置数组上的位置。但是,我得到的结果是:

$ 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
属性(而我可以想象它是一些内部细节):

那么,为什么在数组中设置位置不会更改其长度呢


这个问题与我发布的相关。

最简单的解释是:
长度是魔法

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

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

class PushValuesArray extends Array
(new PushValuesArray) instanceof Array  # true
不幸的是,对于
长度
,您没有。这里所有的
extends
关键字都是创建一个原型链,数组原型有一个明显的非魔法
length
属性:

Array::length  # 0
这就是您在
PushValuesArray
实例中得到的全部内容。遗憾的是,无法在您自己的对象上复制
长度
魔法。您唯一的选择是编写一个函数(例如,
size()
),或者使用所需的方法修改
Array
原型,并使用真正的数组

总之:子类化
Array
不会让您走得很远。
这就是为什么,例如,jQuery在其对象上有一个非常类似于Array的API-

$('body').length  # 1
$('body')[0]      # [object HTMLBodyElement]
-但实际上并没有使这些对象从阵列原型继承:

$('body') instanceof Array  # false

Array.length是一个特殊的属性


如果您在第一个示例中查看已编译的javascript,那么CoffeeScript构建类的方式是创建一个函数并调用
\uuu extenses(SetPositionsArray,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

我意识到我在这里已经很晚了,但你不应该做你正在做的事情。这是一个我一直在修补的例子,我看不出为什么它不适合你

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
>>> relation = new Relation('foo',1,2,3,4,5)
Object[1, 2, 3, 4, 5]
>>> relation.length
5