Frage

Gibt es eine Möglichkeit, ein get / set-Verhalten auf einem Array zu bekommen? Ich stelle mir so etwas wie folgt aus:

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;
        });
}
War es hilfreich?

Lösung

Array Zugriff ist nicht anders in den normalen Zugriff auf Eigenschaften. array[0] Mittel array['0'], so dass Sie eine Eigenschaft mit dem Namen '0' und Intercept-Zugriff auf das erste Array-Element durch das definieren können.

Doch das macht es für alle unpraktisch, aber kurz, mehr oder weniger feste Länge Arrays. Sie können eine Eigenschaft für „alle Namen, die ganzen Zahlen sein passieren“ definieren alle in einem Rutsch.

Andere Tipps

Mit Proxies , können Sie sich das gewünschte Verhalten:

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>

Der Proxy-Konstruktor wird ein Objekt Einwickeln unserer Array und Verwendung Funktionen genannt Fallen erstellen Grund Verhalten außer Kraft zu setzen. Die get Funktion wird aufgerufen werden jeder Immobilien-Lookup und doSomething(), bevor Sie den Wert zurück.

Proxies sind eine ES6-Funktion und sind in IE11 oder senken nicht unterstützt. Siehe Browser-Kompatibilitätsliste.

Ich sah in John Resig Artikel JavaScript Getter und Setter , aber sein Vorbild Beispiel für mich nicht funktioniert hat. Nachdem ich einige Alternativen ausprobiert, fand ich eine, die Arbeit zu sein schien. Sie können Array.prototype.__defineGetter__ auf folgende Weise verwendet werden:

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

Für mich arbeitet in Chrome und Firefox.

Ich hoffe, es hilft.

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 ist möglich, Getter und Setter für JavaScript-Arrays zu definieren. Aber man kann nicht Zugriffs- und Werte in der gleichen Zeit hat. Siehe die Mozilla Dokumentation :

  

Es ist nicht möglich, gleichzeitig einen Getter zu haben, um eine Eigenschaft gebunden und hat die Eigenschaft tatsächlich hält einen Wert

Wenn Sie also Accessoren für ein Array definieren müssen Sie für den Istwert ein zweites Feld haben. Die folgende Beispiel zeigt es.

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

Der Code verwendet drei Arrays:

  1. ein für den Ist-Wert,
  2. ein für die JSON codierte Werte
  3. und eine für die Accessoren.

Das Array mit der Zugriffs- wird an den Aufrufer zurückgegeben. Wenn ein set durch Zuordnen eines Wertes zu dem Array-Element bezeichnet wird, sind die Anordnungen, die einfach und kodierten Werten aktualisiert. Wenn get aufgerufen wird, gibt es nur die Ebene Wert. Und toString gibt die gesamte Abfrage der codierten Werte enthält.

Aber wie andere bereits gesagt: Das macht nur Sinn, wenn die Größe des Arrays konstant ist. Sie können die vorhandenen Elemente des Arrays ändern, aber Sie können nicht zusätzliche Elemente hinzuzufügen.

Warum eine neue Klasse für die inneren Objekte nicht erstellen?

var a = new Car();

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

Und dann,

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

Ich glaube, Sie bekommen die Idee.

Es ist möglich, Setter für jedes Element eines Arrays zu erstellen, aber es gibt eine Einschränkung: Sie würden nicht in der Lage sein, direkt Satz-Array-Elemente für Indizes, die außerhalb des initialisierten Bereich (zB myArray[2] = ... // wouldn't work if myArray.length < 2) Mit den Funktionen Array.prototype wird funktionieren. (ZB Push, Pop, Spleiß, verschiebung, unshift.) Ich gebe ein Beispiel dafür, wie dies zu tun? hier .

Sie können addieren, was Methoden, die Sie zu einem Array mögen, indem sie Array.prototype hinzufügen. Hier ist ein Beispiel, das einen Getter und Setter

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

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

Das ist die Art, wie ich die Dinge tun. Sie haben die Prototype Schöpfung zwicken (ich ein bisschen von meiner Version entfernt). Aber dies wird Ihnen die Standard-Getter / Setter Verhalten Ich bin es gewohnt, in anderen klassenbasierten Sprachen. Definieren eines Getter und Setter keine Mittel, die ignoriert werden, um das Element zu schreiben ...

Hope, das hilft.

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

Das gibt mir das erwartete Verhalten von:

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

Unter dem critizism von dmvaldman up: Schreiben sollte jetzt unmöglich sein. Ich schrieb den Code nicht auf 1) unerwün- schte Elemente verwenden (__ __ defineGetter) und 2) keine Schreib annehmen (das heißt: unkontrolliertes Schreiben) auf das Niveau Element. Ein Beispiel Setter ist im Preis enthalten. (Ich hatte wegen Abschlag Abstand zu __ defineGetter hinzufügen)

Von dmvaldmans anfordern:

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]

Diese Antwort ist nur eine Erweiterung der Lösung auf Basis von Proxy.  Sehen Sie sich die Lösung mit Proxy, dass nur get erwähnt, aber wir können auch Gebrauch  Set wie zeige ich hier.

Hinweis: 3. Argument setzen Sie den Wert tragen kann ...

Der Code ist selbsterklärend.

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
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top