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

bra*_*zzi 4 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_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()
Run Code Online (Sandbox Code Playgroud)

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

$ coffee sp.coffee
sp_array: 
[ 1, 2 ]
sp_array[0]: 
1
sp_array[1]: 
2
sp_array.length: 
0
sp_array.myLength(): 
0
Run Code Online (Sandbox Code Playgroud)

也就是说,长度为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()
Run Code Online (Sandbox Code Playgroud)

在这种情况下,我得到了预期的结果,除了数组中有一个实际的 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
Run Code Online (Sandbox Code Playgroud)

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

这个问题关系到这一个为我贴这个答案.

Tre*_*ham 8

最简单的解释是:length魔术.

length显然不像普通的属性,因为当它在其对象上插入/删除其他属性时它会改变它的值(相反,设置length = 0将删除其他属性); 但是标识符 "length" 并没有什么特别之处.这意味着你可以轻松写作foo.length = 'bar',世界将继续转变.仅在阵列上才具有其特殊性质.

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

class PushValuesArray extends Array
(new PushValuesArray) instanceof Array  # true
Run Code Online (Sandbox Code Playgroud)

不幸的是,出于目的length,你没有.extends这里的所有关键字都是创建一个原型链,而Array 原型具有明显的非魔法length属性:

Array::length  # 0
Run Code Online (Sandbox Code Playgroud)

这就是你在PushValuesArray实例上得到的全部内容.可悲的是,没有办法length在你自己的物体上复制魔法.你唯一的选择是编写一个函数(比如说size()),或者Array用你想要的方法修改原型,而是使用真正的数组.

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

$('body').length  # 1
$('body')[0]      # [object HTMLBodyElement]
Run Code Online (Sandbox Code Playgroud)

- 但实际上并没有使这些对象继承自Array原型:

$('body') instanceof Array  # false
Run Code Online (Sandbox Code Playgroud)