javascript数组上的getter/setter?

IT技术 javascript arrays setter getter
2021-03-20 17:54:39

有没有办法在数组上获得获取/设置行为?我想象这样的事情:

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;
        });
}
6个回答

使用Proxies,您可以获得所需的行为:

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>

Proxy 构造函数将创建一个对象包装我们的 Array 并使用称为陷阱的函数来覆盖基本行为。get函数将被调用以进行任何属性查找,并且doSomething()在返回值之前。

代理是 ES6 功能,在 IE11 或更低版本中不受支持。请参阅浏览器兼容性列表。

@xoxox 谢谢。我删除了那部分
2021-04-28 17:54:39
“但是请注意,代理返回的是对象而不是数组。” 试试这个:print(arr.constructor);你会发现它仍然是一个数组。顺便说一句,也typeof (new Array())回来'object'了。
2021-05-13 17:54:39
我需要一个集合函数。它会如何相似
2021-05-21 17:54:39

数组访问与普通属性访问没有区别。array[0]意味着array['0'],因此您可以定义具有名称的属性'0'并通过它拦截对第一个数组项的访问。

然而,这确实使得它对于除短的、或多或少固定长度的数组之外的所有数组来说都是不切实际的。您不能一次性为“所有碰巧是整数的名称”定义一个属性。

有趣的策略。我还在等待 JavaScript 引入一个包罗万象的 getter/setter 机制,我会成为一个快乐的鸭子!
2021-05-14 17:54:39
值得一提的是,作为 Array 键在规范中被定义为能够将它们的键转换为无符号 32 位整数。很好的答案。
2021-05-16 17:54:39
已经4年了..还有可能吗?我正在为此绞尽脑汁,因为我的网络应用程序确实需要此功能..
2021-05-16 17:54:39
在这里实施了@bobince 的建议它适用于所有情况,除非在既定的数组限制之外访问数组。
2021-05-17 17:54:39
2021-05-17 17:54:39

我查阅了 John Resig 的文章JavaScript Getters And Setters,但他的原型示例对我不起作用。在尝试了一些替代方案后,我发现了一个似乎有效的方法。您可以通过Array.prototype.__defineGetter__以下方式使用:

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

在 Chrome 和 Firefox 中为我工作。

我希望它有帮助。

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(', ') + ']';
        }
    }
);
嗯,一个我希望能奏效的好主意。目前没有办法拦截Object[property],这确实是需要覆盖的。这只是提供了一个类似 Object 的数组,这真的不是海报所要求的。
2021-04-24 17:54:39
这实际上是一个好主意,应该正确解释。该代码正在创建 Array 的子类。关键是扩展Array.prototype更相关的答案是var SubArrayClass = {}; SubArrayClass.prototype = Object.extend(Array.prototype, { get: ..., set: ... });
2021-05-17 17:54:39

可以为 JavaScript 数组定义 Getter 和 Setter。但是您不能同时拥有访问器和值。请参阅 Mozilla文档

不可能同时将 getter 绑定到一个属性并让该属性实际持有一个值

因此,如果您为数组定义访问器,则需要为实际值设置第二个数组。下面的例子说明了它。

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

该代码使用三个数组:

  1. 一个用于实际值,
  2. 一个用于 JSON 编码的值
  3. 一个用于访问器。

带有访问器的数组返回给调用者。set通过为数组元素分配一个值来调用 a 时,包含普通值和编码值的数组将被更新。get被调用时,它只返回普通值。toString返回包含编码值的整个查询。

但正如其他人已经指出的那样:只有当数组的大小恒定时,这才有意义。您可以修改数组的现有元素,但不能添加其他元素。