Почему установка позиций в подклассе Array не меняет его длину? Должен ли я не подкласс массив? - PullRequest
4 голосов
/ 17 сентября 2011

В программе 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()

Я бы надеялся, что этот код изменит свойство 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

То есть длина равна 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

Итак, почему установка позиции в массиве не меняет его длину?

Этот вопрос относится к этому , для которого я отправил этот ответ .

Ответы [ 4 ]

8 голосов
/ 17 сентября 2011

Самое простое объяснение: length это магия.

length явно не ведет себя как обычное свойство, так как оно меняет свое значение, когда вы вставляете / удаляете другие свойства его объекта (и, наоборот, установка length = 0 удаляет другие свойства); но в идентификаторе "length" нет ничего особенного. Это означает, что вы можете легко написать foo.length = 'bar', и мир будет продолжать вращаться. Только на массивах он имеет особый характер.

Теперь вы можете ожидать, когда, когда вы extend конструктор Array, вы получите массив, но не так ли? Ну, в некотором смысле вы делаете:

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

К сожалению, с целью length вы этого не сделаете. Все ключевое слово extends здесь создает цепочку прототипов, а прототип Array имеет явно немагическое свойство length:

Array::length  # 0

Это все, что вы получаете в своем PushValuesArray экземпляре. К сожалению, нет способа скопировать магию length на ваши собственные объекты. Единственный вариант - вместо этого написать функцию (скажем, size()) или изменить прототип Array нужными вам методами и использовать вместо этого истинные массивы.

Подводя итог: Подклассы Array не дадут вам очень далеко. Вот почему, например, jQuery имеет очень похожий на массив API на своих объектах -

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

- но на самом деле эти объекты не наследуются от прототипа Array:

$('body') instanceof Array  # false
3 голосов
/ 17 сентября 2011

Array.length - это специальное свойство, которое не работает при использовании подкласса .

Если вы посмотрите на скомпилированный javascript для своего первого примера, то способ, которым CoffeeScript создает классы, заключается в создании функции и вызове __extends(SetPositionsArray, Array);. Этот метод не может «скопировать» понятие длины в ваш новый класс.

0 голосов
/ 28 февраля 2014

Я понимаю, что опаздываю в игре, но вам не нужно делать то, что вы делаете.Вот пример, с которым я возился, и я не вижу причин, почему он не должен работать для вас.

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
0 голосов
/ 21 мая 2012

Следующий подкласс 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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...