Getter/setter on javascript array?

前端 未结 10 2149
闹比i
闹比i 2020-12-01 02:12

Is there a way to get a get/set behaviour on an array? I imagine something like this:

var arr = [\'one\', \'two\', \'three\'];
var _arr = new Array();

for (         


        
相关标签:
10条回答
  • 2020-12-01 02:47

    Using Proxies, you can get the desired behavior:

    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>

    The Proxy constructor will create an object wrapping our Array and use functions called traps to override basic behaviors. The get function will be called for any property lookup, and doSomething() before returning the value.

    Proxies are an ES6 feature and are not supported in IE11 or lower. See browser compatibility list.

    0 讨论(0)
  • 2020-12-01 02:49

    this is the way I do things. You will have to tweak the Prototype Creation (I removed a bit from my Version). But this will give you the default getter / setter behavior I am used to in other Class-Based Languages. Defining a Getter and no Setter means that writing to the element will be ignored...

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

    This gives me the expected behavior of:

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

    Taking up the critizism from dmvaldman: Writing should now be impossible. I rewrote the code to 1)not use depracated elements (__ defineGetter __) and 2) not accept any writing (that is: uncontrolled writing) to the levels element. An example setter is included. (I had to add spacing to __ defineGetter because of markdown)

    From dmvaldmans request:

    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]
    
    0 讨论(0)
  • 2020-12-01 02:50

    This answer is just an extension to the solution based on Proxy. See the solution with proxy, in that only get is mentioned but we can also use set as I am showing here.

    Notice: 3rd argument in set can carry the value...

    The code is self explanatory.

    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
    
    0 讨论(0)
  • 2020-12-01 02:51

    It is possible to define Getters and Setters for JavaScript arrays. But you can not have accessors and values at the same time. See the Mozilla documentation:

    It is not possible to simultaneously have a getter bound to a property and have that property actually hold a value

    So if you define accessors for an array you need to have a second array for the actual value. The following example illustrates it.

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

    The code uses three arrays:

    1. one for the actual values,
    2. one for the JSON encoded values
    3. and one for the accessors.

    The array with the accessors is returned to the caller. When a set is called by assigning a value to the array element, the arrays containing the plain and encoded values are updated. When get gets called, it returns just the plain value. And toString returns the whole query containing the encoded values.

    But as others have stated already: this makes only sense, when the size of the array is constant. You can modify the existing elements of the array but you can not add additional elements.

    0 讨论(0)
提交回复
热议问题