Question

Y at-il un moyen d'obtenir un comportement / setter sur un tableau? J'imagine quelque chose comme ceci:

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;
        });
}
Était-ce utile?

La solution

accès Array n'est différent accès à la propriété normale. array[0] signifie array['0'], de sorte que vous pouvez définir une propriété avec le nom et l'accès '0' d'interception au premier élément du tableau par là.

Cependant, cela ne rend pas pratique pour tous, mais à court, tableaux plus ou moins de longueur fixe. Vous ne pouvez pas définir une propriété pour « tous les noms qui se trouvent être des entiers » en une seule fois.

Autres conseils

Utilisation proxies, vous pouvez obtenir le comportement souhaité:

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>

Le constructeur Proxy va créer un objet enveloppant nos fonctions Array et utiliser des pièges appelés à remplacer les comportements de base. La fonction get sera appelé à any propriété recherche et doSomething() avant de retourner la valeur.

Les proxies sont une caractéristique ES6 et ne sont pas pris en charge dans IE11 ou moins. Voir liste de compatibilité du navigateur.

Je regardais dans l'article JavaScript accesseurs , mais son exemple prototype ne fonctionnait pas pour moi. Après avoir essayé quelques solutions de rechange, j'ai trouvé un qui a semblé fonctionner. Vous pouvez utiliser Array.prototype.__defineGetter__ de la manière suivante:

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

a marché pour moi dans Chrome et Firefox.

J'espère que ça aide.

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

Il est possible de définir accesseurs et de mutateurs pour les tableaux JavaScript. Mais vous ne pouvez pas avoir des accesseurs et des valeurs en même temps. Consultez la documentation de Mozilla :

  

Il est impossible d'avoir simultanément un getter lié à une propriété et ont cette propriété détiennent effectivement une valeur

Donc, si vous définissez accesseurs pour un tableau que vous devez avoir un deuxième tableau pour la valeur réelle. Les l'illustre.

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

Le code utilise trois tableaux:

  1. une pour les valeurs réelles,
  2. un pour les valeurs codées JSON
  3. et un pour les accesseurs.

Le tableau avec les accesseurs est retourné à l'appelant. Quand un set est appelé par l'attribution d'une valeur à l'élément de matrice, les matrices contenant les valeurs codées et lisses sont mises à jour. Lorsque get est appelé, il retourne juste la valeur ordinaire. Et toString retourne toute requête contenant les valeurs codées.

Mais comme d'autres l'ont dit déjà: cela est logique que, lorsque la taille du tableau est constante. Vous pouvez modifier les éléments existants du tableau, mais vous ne pouvez pas ajouter des éléments supplémentaires.

Pourquoi ne pas créer une nouvelle classe pour les objets internes?

var a = new Car();

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

Et puis,

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

Je pense que vous avez l'idée.

Il est possible de créer des setters pour chaque élément d'un tableau, mais il y a une limite: vous ne seriez pas en mesure de définir directement les éléments du tableau pour les index qui sont en dehors de la région (par exemple initialisé myArray[2] = ... // wouldn't work if myArray.length < 2) Utilisation des fonctions Array.prototype marchera. (Par exemple, pousser, pop, épissures, quart de travail, unshift.) Je donne un exemple de la façon d'y arriver ici .

Vous pouvez ajouter toutes les méthodes que vous aimez un Array, en les ajoutant à Array.prototype. Voici un exemple qui ajoute un getter et setter

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

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

est la façon dont je fais les choses. Vous devrez modifier la création du prototype (j'ai enlevé un peu de ma version). Mais cela vous donnera le comportement par défaut getter / setter je suis habitué dans d'autres langues Class-Based. La définition d'un Getter et Setter ne signifie que l'écriture à l'élément sera ignoré ...

Hope this helps.

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

Cela me donne le comportement attendu de:

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

Reprenant la critizism de dmvaldman: L'écriture devrait maintenant être impossible. Je réécrit le code à 1) ne pas utiliser des éléments depracated (__ __ defineGetter) et 2) décline toute écriture (qui est: l'écriture non contrôlée) à l'élément de niveaux. Un setter exemple est inclus. (Je devais ajouter un espacement à __ defineGetter en raison de démarquage)

De la demande de 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]

Cette réponse est juste une extension de la solution basée sur Proxy.  Voir la solution avec proxy, en ce que seul get est mentionné, mais nous pouvons aussi utiliser  fixé comme je montre ici.

Avis: 3ème argument en jeu peut porter la valeur ...

Le code est explicite.

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
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top