Domanda

C'è un modo per ottenere un get / set comportamento su un array? Immagino qualcosa di simile:

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;
        });
}
È stato utile?

Soluzione

Array accesso non è diverso alla normalità l'accesso alle proprietà. array[0] significa array['0'], in modo da poter definire una proprietà con il nome '0' e accesso intercettare al primo elemento dell'array attraverso quella.

Tuttavia, ciò lo rende poco pratico per tutti, ma le matrici breve, più o meno a lunghezza fissa. Non è possibile definire una proprietà per “tutti i nomi che si trovano ad essere numeri interi” tutti in una volta.

Altri suggerimenti

Proxy , è possibile ottenere il comportamento desiderato:

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>

Il costruttore Proxy creerà un oggetto avvolgendo le nostre funzioni di array e utilizzare chiamati trappole per ignorare i comportamenti di base. La funzione get sarà chiamata per qualsiasi di ricerca immobili, e doSomething() prima di restituire il valore.

proxy sono una caratteristica ES6 e non sono supportati in IE11 o inferiore. Vedere lista di compatibilità del browser.

Ho guardato in di John Resig articolo JavaScript getter e setter , ma il suo esempio prototipo non ha funzionato per me. Dopo aver provato alcune alternative, ho trovato uno che sembrava funzionare. È possibile utilizzare Array.prototype.__defineGetter__ nel seguente modo:

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

ha lavorato per me in Chrome e Firefox.

Spero che aiuta.

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

E 'possibile definire getter e setter per gli array JavaScript. Ma non si può avere funzioni di accesso e di valori allo stesso tempo. Vedere la Mozilla documentazione :

  

Non è possibile avere contemporaneamente un getter legata a una proprietà e che hanno proprietà in realtà contenere un valore

Quindi, se si definisce di accesso per una matrice è necessario disporre di un secondo array per il valore effettivo. I seguenti esempio lo illustra.

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

Il codice utilizza tre matrici:

  1. una per i valori reali,
  2. una per i valori codificati JSON
  3. e uno per le funzioni di accesso.

L'array con le funzioni di accesso viene restituito al chiamante. Quando un set viene chiamato assegnando un valore per l'elemento di matrice, le matrici contenenti i valori normali e codificati vengono aggiornati. Quando get viene chiamato, restituisce solo il valore normale. E toString restituisce l'intera query contenente i valori codificati.

Ma come altri hanno detto già: questo senso solo, quando la dimensione della matrice è costante. È possibile modificare gli elementi esistenti della matrice, ma non è possibile aggiungere ulteriori elementi.

Perché non creare una nuova classe per gli oggetti interni?

var a = new Car();

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

E poi,

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

Penso che si ottiene l'idea.

È possibile creare incastonatori per ciascun elemento di un array, ma c'è una limitazione: non sarebbe in grado di impostare direttamente elementi dell'array di indici che sono al di fuori della regione inizializzato (es myArray[2] = ... // wouldn't work if myArray.length < 2) Utilizzando le funzioni Array.prototype funzionerà. (Es spinta, pop, splice, shift, unshift.) Mi danno un esempio di come realizzare questo qui .

È possibile aggiungere qualsiasi metodo che ti piace ad un Array, con l'aggiunta di loro di Array.prototype. Ecco un esempio che aggiunge un getter e setter

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

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

Questo è il mio modo di fare le cose. Si dovrà modificare la Creazione Prototype (ho tolto un po 'dalla mia versione). Ma questo vi darà il comportamento predefinito getter / setter sono abituato a in altra classe-Based Lingue. Definizione di un Getter e nessun Setter significa che la scrittura per l'elemento viene ignorato ...

Spero che questo aiuti.

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

Questo mi dà il comportamento previsto:

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

Riprendendo il critizism da dmvaldman: La scrittura dovrebbe essere impossibile. Ho riscritto il codice per 1) Non utilizzare elementi depracated (__ defineGetter __) e 2), non accettare qualsiasi iscritto (cioè: la scrittura non controllata) all'elemento livelli. Un esempio setter è incluso. (Ho dovuto aggiungere spaziatura a __ defineGetter a causa della riduzione dei prezzi)

Da Richiesta 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]

Questa risposta è solo un'estensione della soluzione basata su proxy.  Vedere la soluzione con delega, in quanto solo ottiene è menzionato, ma possiamo anche usare  impostato come sto mostrando qui.

Avviso: 3 ° argomento nel set può portare il valore ...

Il codice si spiega da sé.

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
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top