Вопрос

Есть ли способ получить поведение 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;
        });
}
Это было полезно?

Решение

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

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

Другие советы

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

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>

Конструктор прокси создаст объект, обертывающий наш массив, и будет использовать функции, называемые traps, для переопределения базового поведения.Тот Самый get функция будет вызвана для Любой поиск свойств и doSomething() прежде чем возвращать значение.

Прокси-серверы являются функцией ES6 и не поддерживаются в IE11 или более поздней версии.Видишь список совместимости браузера.

Я посмотрел статью Джона Резига. Геттеры и сеттеры JavaScript, но его пример-прототип у меня не сработал.Попробовав несколько альтернатив, я нашел тот, который, похоже, сработал.Вы можете использовать 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.

Я надеюсь, что это помогает.

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(', ') + ']';
        }
    }
);

Для массивов JavaScript можно определить геттеры и сеттеры.Но вы не можете иметь средства доступа и значения одновременно.Посмотрите Мозиллу документация:

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

Поэтому, если вы определяете методы доступа для массива, вам понадобится второй массив для фактического значения.Следующее пример иллюстрирует это.

//
// Poor man's 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;
  }
}

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

  1. один для фактических значений,
  2. один для значений в кодировке JSON
  3. и один для аксессуаров.

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

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

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

var a = new Car();

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

А потом,

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

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

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

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

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

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

я так делаю.Вам придется настроить создание прототипа (я немного удалил из своей версии).Но это даст вам поведение метода получения/установки по умолчанию, к которому я привык в других языках на основе классов.Определение геттера и отсутствия сеттера означает, что запись в элемент будет игнорироваться...

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

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]

Принимая критику со стороны Дмвальдмана:Писать теперь должно быть невозможно.Я переписал код так, чтобы 1) не использовать устаревшие элементы (__ defineGetter __) и 2) не принимать никаких написаний (то есть:неконтролируемая запись) в элемент level.Пример установки включен.(Мне пришлось добавить пробел в __ 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]

Этот ответ является всего лишь дополнением к решению, основанному на прокси.Смотрите решение с прокси, в котором упоминается только get, но мы также можем использовать set, как я показываю здесь.

УВЕДОМЛЕНИЕ:третий аргумент в наборе может содержать значение...

Код говорит сам за себя.

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

var accessCount = 0;

function doSomething() {
  accessCount++;
}

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

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

print(accessCount);      // 0
print(arr[0]);           // 'one'
print(accessCount);      // 1
arr[1] = 10;
print(accessCount);      // 2
print(arr[1]);           // 10
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top