Подтвердить что ты не робот

Getter/setter в массиве javascript?

Есть ли способ получить поведение get/set в массиве? Я представляю себе что-то вроде этого:

var arr = ['one', 'two', 'three'];
var _arr = new Array();

for (var i = 0; i < arr.length; i++) {
    arr[i].__defineGetter__('value',
        function (index) {
            //Do something
            return _arr[index];
        });
    arr[i].__defineSetter__('value',
        function (index, val) {
            //Do something
            _arr[index] = val;
        });
}
4b9b3361

Ответ 1

Доступ к массиву ничем не отличается от обычного доступа к свойствам. array[0] означает array['0'], поэтому вы можете определить свойство с именем '0' и через него перехватить доступ к первому элементу массива.

Однако это делает его нецелесообразным для всех, кроме коротких, массивов с большей или меньшей фиксированной длиной. Вы не можете определить свойство для "всех имен, которые являются целыми" всего за один раз.

Ответ 2

Используя Proxies, вы можете получить желаемое поведение:

var _arr = ['one', 'two', 'three'];

var accessCount = 0;
function doSomething() {
  accessCount++;
}

var arr = new Proxy(_arr, {
  get: function(target, name) {
    doSomething();
    return target[name];
  }
});

function print(value) {
  document.querySelector('pre').textContent += value + '\n';
}

print(accessCount);      // 0
print(arr[0]);           // 'one'
print(arr[1]);           // 'two'
print(accessCount);      // 2
print(arr.length);       // 3
print(accessCount);      // 3
print(arr.constructor);  // 'function Array() { [native code] }'
<pre></pre>

Ответ 3

Я посмотрел в статье Джона Ресига JavaScript Getters And Setters, но его пример прототипа не работал у меня. Выбрав некоторые альтернативы, я нашел тот, который, казалось, работал. Вы можете использовать Array.prototype.__defineGetter__ следующим образом:

Array.prototype.__defineGetter__("sum", function sum(){
var r = 0, a = this, i = a.length - 1;
do {
    r += a[i];
    i -= 1;
} while (i >= 0);
return r;
});
var asdf = [1, 2, 3, 4];
asdf.sum; //returns 10

Работала для меня в Chrome и Firefox.

Ответ 4

Вы можете добавить любые методы, которые вам нравятся, к Array, добавив их в Array.prototype. Вот пример, который добавляет getter и setter

Array.prototype.get = function(index) {
  return this[index];
}

Array.prototype.set = function(index, value) {
  this[index] = value;
}

Ответ 5

Надеюсь, это поможет.

Object.extend(Array.prototype, {
    _each: function(iterator) {
                    for (var i = 0; i < this.length; i++)
                    iterator(this[i]);
                },
    clear: function() {
                    this.length = 0;
                    return this;
                },
    first: function() {
                    return this[0];
                },
    last: function() {
                return this[this.length - 1];
                },
    compact: function() {
        return this.select(function(value) {
                                                return value != undefined || value != null;
                                                }
                                            );
        },
    flatten: function() {
            return this.inject([], function(array, value) {
                    return array.concat(value.constructor == Array ?
                        value.flatten() : [value]);
                    }
            );
        },
    without: function() {
        var values = $A(arguments);
                return this.select(function(value) {
                        return !values.include(value);
                }
            );
    },
    indexOf: function(object) {
        for (var i = 0; i < this.length; i++)
        if (this[i] == object) return i;
        return -1;
    },
    reverse: function(inline) {
            return (inline !== false ? this : this.toArray())._reverse();
        },
    shift: function() {
        var result = this[0];
        for (var i = 0; i < this.length - 1; i++)
        this[i] = this[i + 1];
        this.length--;
        return result;
    },
    inspect: function() {
            return '[' + this.map(Object.inspect).join(', ') + ']';
        }
    }
);

Ответ 6

Можно определить Getters и Setters для массивов JavaScript. Но вы не можете иметь аксессоров и значений одновременно. См. Документацию Mozilla :

Невозможно одновременно получить геттер, связанный с свойством, и это свойство действительно имеет значение

Итак, если вы определяете accessors для массива, вам нужно иметь второй массив для фактического значения. Это иллюстрирует следующий пример.

//
// Poor man prepare for querySelector.
//
// Example:
//   var query = prepare ('#modeler table[data-id=?] tr[data-id=?]');
//   query[0] = entity;
//   query[1] = attribute;
//   var src = document.querySelector(query);
//
var prepare;
{
  let r = /^([^?]+)\?(.+)$/; // Regular expression to split the query

  prepare = function (query, base)
  {
    if (!base) base = document;
    var q  = []; // List of query fragments
    var qi = 0;  // Query fragment index
    var v  = []; // List of values
    var vi = 0;  // Value index
    var a  = []; // Array containing setters and getters
    var m;       // Regular expression match
    while (query) {
      m = r.exec (query);
      if (m && m[2]) {
        q[qi++] = m[1];
        query   = m[2];
        (function (qi, vi) {
          Object.defineProperty (a, vi, {
            get: function() { return v[vi]; },
            set: function(val) { v[vi] = val; q[qi] = JSON.stringify(val); }});
        })(qi++, vi++);
      } else {
        q[qi++] = query;
        query   = null;
      }
    }
    a.toString = function () { return q.join(''); }
    return a;
  }
}

В коде используются три массива:

  • для реальных значений
  • один для кодированных значений JSON
  • и один для аксессуаров.

Массив с аксессуарами возвращается вызывающему. Когда a set вызывается путем назначения значения элементу массива, массивы, содержащие простые и закодированные значения, обновляются. Когда вызов get вызывается, он возвращает просто равное значение. И toString возвращает весь запрос, содержащий закодированные значения.

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

Ответ 7

Почему бы не создать новый класс для внутренних объектов?

var a = new Car();

function Car()
{
   // here create the setters or getters necessary
}

И затем,

arr = new Array[a, new Car()]

Я думаю, вы поняли.

Ответ 8

Можно создать сеттеры для каждого элемента массива, но есть одно ограничение: вы не сможете напрямую устанавливать элементы массива для индексов, которые находятся за пределами инициализированной области (например, myArray[2] = ... // wouldn't work if myArray.length < 2). Использование массива. прототипы будут работать. (например, push, pop, splice, shift, unshift.) Я приведу пример того, как выполнить этот здесь.

Ответ 9

Я так делаю. Вам нужно будет настроить Prototype Creation (я удалил бит из моей версии). Но это даст вам поведение по умолчанию getter/setter, к которому я привык, в других языках на основе классов. Определение Getter и no Setter означает, что запись в элемент будет проигнорирована...

Надеюсь, что это поможет.

function Game () {
  var that = this;
  this._levels = [[1,2,3],[2,3,4],[4,5,6]];

  var self = {
    levels: [],
    get levels () {
        return that._levels;
    },
    setLevels: function(what) {
        that._levels = what;
        // do stuff here with
        // that._levels
    }
  };
  Object.freeze(self.levels);
  return self;
}

Это дает мне ожидаемое поведение:

var g = new Game()
g.levels
/// --> [[1,2,3],[2,3,4],[4,5,6]]
g.levels[0]
/// --> [1,2,3]

Взятие критики из dmvaldman: писать теперь невозможно. Я переписал код: 1) не использовать разрозненные элементы (__ defineGetter __) и 2) не принимать никакую запись (то есть: неконтролируемую запись) в элемент levels. Включен пример setterter. (Мне пришлось добавить интервал к __ defineGetter из-за уценки)

Из запроса dmvaldmans:

g.levels[0] = [2,3,4];
g.levels;
/// --> [[1,2,3],[2,3,4],[4,5,6]]

//using setter
g.setLevels([g.levels, g.levels, 1,2,3,[9]]);
g.levels;
/// --> [[[1,2,3],[2,3,4],[4,5,6]],[[1,2,3],[2,3,4],[4,5,6]], ....]

//using setLevels
g.setLevels([2,3,4]);
g.levels;
/// --> [2,3,4]