Pregunta

¿Hay una manera de conseguir una / un comportamiento establecido get en una matriz? Me imagino algo como esto:

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;
        });
}
¿Fue útil?

Solución

acceso a conjunto no es diferente a la propiedad de acceso normal. array[0] significa array['0'], por lo que puede definir una propiedad con el nombre '0' y el acceso de interceptación con el primer elemento de la matriz a través de eso.

Sin embargo, eso hace que no sea práctico para todos, pero matrices, más o menos de longitud fija corto. No se puede definir una propiedad de “todos los nombres que pasan a ser números enteros” todos de una sola vez.

Otros consejos

proxies , se puede obtener el comportamiento deseado:

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>

El constructor proxy creará un objeto envolver nuestras funciones de matriz y el uso de los llamados trampas para anular comportamientos básicos. La función get será llamado para cualquier de búsqueda de la propiedad, y doSomething() antes de devolver el valor.

Proxies son una característica ES6 y no se admiten en EI11 o inferior. Ver lista de compatibilidad del navegador.

Miré hacia arriba en el artículo de John Resig JavaScript captadores y definidores , pero su ejemplo prototipo no funcionó para mí. Después de probar algunas alternativas, encontré uno que parecía funcionar. Puede utilizar Array.prototype.__defineGetter__ de la siguiente manera:

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

trabajó para mí en Chrome y Firefox.

espero que ayude.

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

Es posible definir los captadores y definidores para las matrices de JavaScript. Pero no se puede tener descriptores de acceso y valores al mismo tiempo. Consulte la documentación de la Mozilla :

  

No es posible tener al mismo tiempo un captador unido a una propiedad y tienen la propiedad de que en realidad posee un valor

Así que si se define descriptores de acceso para una matriz es necesario tener una segunda matriz para el valor real. Los siguientes ejemplo lo ilustra.

//
// 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;
  }
}

El código utiliza tres matrices:

  1. uno para los valores reales,
  2. uno para los valores JSON codificados
  3. y uno de los descriptores de acceso.

La matriz con los descriptores de acceso es devuelto a la persona que llama. Cuando un set se llama mediante la asignación de un valor al elemento de array, se actualizan las matrices que contienen los valores de fricción y codificados. Cuando se llama a get, devuelve sólo el valor normal. Y toString devuelve toda la consulta que contiene los valores codificados.

Sin embargo, como otros han dicho ya: este sólo tiene sentido, cuando el tamaño de la matriz es constante. Puede modificar los elementos existentes de la matriz, pero no se puede añadir elementos adicionales.

¿Por qué no crear una nueva clase de los objetos internos?

var a = new Car();

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

Y a continuación,

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

Creo que se entiende la idea.

Es posible crear setters para cada elemento de una matriz, pero no hay una limitación: no sería capaz de fijar directamente los elementos de matriz para índices que están fuera de la región inicializado (por ejemplo myArray[2] = ... // wouldn't work if myArray.length < 2) usando las funciones de Array.prototype trabajará. (Por ejemplo, empuje, pop, empalme, cambio, unshift.) Me dan un ejemplo de cómo lograr esto aquí .

Puede añadir cualquier método a su gusto de un Array, añadiéndolos a Array.prototype. He aquí un ejemplo que añade un getter y setter

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

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

esta es la forma en que hago las cosas. Usted tendrá que ajustar la creación de prototipos (He quitado un poco de mi versión). Pero esto le dará el comportamiento por defecto captador / definidor que estoy acostumbrado en otros idiomas basada en la clase. La definición de un Getter y Setter significa que hay escrito al elemento será ignorado ...

Espero que esto ayude.

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;
}

Esto me da el comportamiento esperado de:

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

Retomando el critizism de dmvaldman: La escritura debe ser ahora imposible. Reescribí el código para 1) no utilizar elementos depracated (__ __ defineGetter) y 2) no aceptar cualquier escritura (es decir: la escritura no controlada) al elemento de niveles. Se incluye un setter ejemplo. (I tenido que añadir espaciado a __ defineGetter debido a markdown)

A partir de la solicitud 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]

Esta respuesta es simplemente una extensión de la solución basada en proxy.  Ver la solución con proxy, en el que sólo se obtiene es mencionado, pero también podemos utilizar  establecer como estoy mostrando aquí.

Aviso: tercera argumento en conjunto puede llevar el valor ...

El código es auto explicativo.

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
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top