Javascript javascript数组上的getter/setter?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2449182/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Getter/setter on javascript array?
提问by Martin Hansen
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 (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;
});
}
采纳答案by bobince
Array access is no different to normal property access. array[0]means array['0'], so you can define a property with name '0'and intercept access to the first array item through that.
数组访问与普通属性访问没有区别。array[0]意味着array['0'],因此您可以定义具有名称的属性'0'并通过它拦截对第一个数组项的访问。
However, that does make it impractical for all but short, more-or-less-fixed-length Arrays. You can't define a property for “all names that happen to be integers” all in one go.
然而,这确实使得它对于除短的、或多或少固定长度的数组之外的所有数组来说都是不切实际的。您无法一次性为“所有碰巧是整数的名称”定义一个属性。
回答by aebabis
Using Proxies, you can get the desired behavior:
使用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>
The Proxy constructor will create an object wrapping our Array and use functions called traps to override basic behaviors. The getfunction will be called for anyproperty lookup, and doSomething()before returning the value.
Proxy 构造函数将创建一个对象来包装我们的 Array 并使用称为陷阱的函数来覆盖基本行为。该get函数将被调用以进行任何属性查找,并且doSomething()在返回值之前。
Proxies are an ES6 feature and are not supported in IE11 or lower. See browser compatibility list.
代理是 ES6 功能,在 IE11 或更低版本中不受支持。请参阅浏览器兼容性列表。
回答by rolandog
I looked up in John Resig's article JavaScript Getters And Setters, but his prototype example didn't work for me. After trying out some alternatives, I found one that seemed to work. You can use Array.prototype.__defineGetter__in the following way:
我查阅了 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
Worked for me in Chrome and Firefox.
在 Chrome 和 Firefox 中为我工作。
回答by ceving
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:
可以为 JavaScript 数组定义 Getter 和 Setter。但是您不能同时拥有访问器和值。请参阅 Mozilla文档:
It is not possible to simultaneously have a getter bound to a property and have that property actually hold a value
不可能同时将 getter 绑定到属性并让该属性实际持有一个值
So if you define accessors for an array you need to have a second array for the actual value. The following exampleillustrates 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:
该代码使用三个数组:
- one for the actual values,
- one for the JSON encoded values
- and one for the accessors.
- 一个用于实际值,
- 一个用于 JSON 编码的值
- 一个用于访问器。
The array with the accessors is returned to the caller. When a setis called by assigning a value to the array element, the arrays containing the plain and encoded values are updated. When getgets called, it returns just the plain value. And toStringreturns the whole query containing the encoded values.
带有访问器的数组返回给调用者。当set通过为数组元素分配一个值来调用 a 时,包含普通值和编码值的数组将被更新。当get被调用时,它只返回普通值。并toString返回包含编码值的整个查询。
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.
但正如其他人已经指出的那样:只有当数组的大小恒定时,这才有意义。您可以修改数组的现有元素,但不能添加其他元素。
回答by Martinez
I hope it helps.
我希望它有帮助。
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(', ') + ']';
}
}
);
回答by Dónal
You can add whatever methods you like to an Array, by adding them to Array.prototype. Here's an example that adds a getter and setter
您可以将任何您喜欢的方法添加到Array,方法是将它们添加到Array.prototype. 这是一个添加 getter 和 setter 的示例
Array.prototype.get = function(index) {
return this[index];
}
Array.prototype.set = function(index, value) {
this[index] = value;
}
回答by jpabluz
Why not create a new class for the inner objects?
为什么不为内部对象创建一个新类?
var a = new Car();
function Car()
{
// here create the setters or getters necessary
}
And then,
进而,
arr = new Array[a, new Car()]
I think you get the idea.
我想你应该已经明白了。
回答by Tibin Thomas
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.
这个答案只是基于代理的解决方案的扩展。请参阅使用代理的解决方案,其中仅提到了 get,但我们也可以使用我在此处展示的 set。
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
回答by Constablebrew
It is possible to create setters for each element of an array, but there is one limitation: you would not be able to directly set array elements for indexes that are outside the initialized region (e.g. myArray[2] = ... // wouldn't work if myArray.length < 2) Using the Array.prototype functions will work. (e.g. push, pop, splice, shift, unshift.) I give an example of how to accomplish this here.
可以为数组的每个元素创建 setter,但有一个限制:您不能直接为初始化区域之外的索引设置数组元素(例如myArray[2] = ... // wouldn't work if myArray.length < 2) 使用 Array.prototype 函数将起作用。(例如,push、pop、splice、shift、unshift。)我在这里举了一个例子来说明如何做到这一点。
回答by LeTigre
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...
这就是我做事的方式。您将不得不调整原型创建(我从我的版本中删除了一些)。但这将为您提供我在其他基于类的语言中习惯的默认 getter/setter 行为。定义一个 Getter 而没有 Setter 意味着写入元素将被忽略......
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)
接受 dmvaldman 的批评:现在写作应该是不可能的。我将代码重写为 1) 不使用已弃用的元素 (__defineGetter __) 和 2) 不接受对 levels 元素的任何写入(即:不受控制的写入)。包括一个示例设置器。(由于降价,我不得不为 __defineGetter 添加间距)
From dmvaldmans request:
来自 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]

