我知道如何通过执行以下操作为已经知道名称的属性创建 getter 和 setter:
// A trivial example:
function MyObject(val){
this.count = 0;
this.value = val;
}
MyObject.prototype = {
get value(){
return this.count < 2 ? "Go away" : this._value;
},
set value(val){
this._value = val + (++this.count);
}
};
var a = new MyObject('foo');
alert(a.value); // --> "Go away"
a.value = 'bar';
alert(a.value); // --> "bar2"
现在,我的问题是,是否有可能定义像这样的包罗万象的获取者和二传手? 即,为尚未定义的任何属性名称创建 getter 和 setter。
这个概念在PHP中使用__get()
和__set()
魔术方法是可能的(有关这些方法的信息,请参阅PHP文档(,所以我真的在问是否有与这些等效的JavaScript?
不用说,理想情况下,我想要一个跨浏览器兼容的解决方案。
> 这在 ES2015(又名"ES6"(规范中发生了变化:JavaScript 现在有代理。代理允许您创建作为其他对象(外观(的真正代理的对象。下面是一个简单的示例,它在检索时将字符串的任何属性值转换为全部大写,并为不存在的属性返回"missing"
而不是undefined
:
"use strict";
if (typeof Proxy == "undefined") {
throw new Error("This browser doesn't support Proxy");
}
let original = {
example: "value",
};
let proxy = new Proxy(original, {
get(target, name, receiver) {
if (Reflect.has(target, name)) {
let rv = Reflect.get(target, name, receiver);
if (typeof rv === "string") {
rv = rv.toUpperCase();
}
return rv;
}
return "missing";
}
});
console.log(`original.example = ${original.example}`); // "original.example = value"
console.log(`proxy.example = ${proxy.example}`); // "proxy.example = VALUE"
console.log(`proxy.unknown = ${proxy.unknown}`); // "proxy.unknown = missing"
original.example = "updated";
console.log(`original.example = ${original.example}`); // "original.example = updated"
console.log(`proxy.example = ${proxy.example}`); // "proxy.example = UPDATED"
不重写的操作具有其默认行为。在上面,我们覆盖的只是 get
,但有一个完整的操作列表,你可以挂钩。
在get
处理程序函数的参数列表中:
-
target
是被代理的对象(在我们的例子中是original
(。 -
name
(当然(是要检索的属性的名称,它通常是一个字符串,但也可以是符号。 -
receiver
是应在 getter 函数中用作this
的对象,如果属性是访问器而不是数据属性。在正常情况下,这是代理或从它继承的东西,但它可以是任何东西,因为陷阱可能由Reflect.get
触发。
这使您可以创建一个具有所需捕获所有getter和setter功能的对象:
"use strict";
if (typeof Proxy == "undefined") {
throw new Error("This browser doesn't support Proxy");
}
let obj = new Proxy({}, {
get(target, name, receiver) {
if (!Reflect.has(target, name)) {
console.log("Getting non-existent property '" + name + "'");
return undefined;
}
return Reflect.get(target, name, receiver);
},
set(target, name, value, receiver) {
if (!Reflect.has(target, name)) {
console.log(`Setting non-existent property '${name}', initial value: ${value}`);
}
return Reflect.set(target, name, value, receiver);
}
});
console.log(`[before] obj.example = ${obj.example}`);
obj.example = "value";
console.log(`[after] obj.example = ${obj.example}`);
上面的输出是:
获取不存在的属性"示例"[before] obj.example = undefined设置不存在的属性"示例",初始值:值[之后] obj.example = 值
请注意,当我们尝试检索example
尚不存在时,我们如何获得"不存在"消息,并在创建它时再次收到"不存在"消息,但在此之后不会。
2011年的答案(被上述内容淘汰,仍然与仅限于ES5功能(如Internet Explorer(的环境相关(:
不,JavaScript 没有包罗万象的属性功能。您正在使用的访问器语法包含在规范的第 11.1.5 节中,并且不提供任何通配符或类似的东西。
当然,你可以实现一个函数来做到这一点,但我猜你可能不想使用f = obj.prop("example");
而不是f = obj.example;
和obj.prop("example", value);
而不是obj.example = value;
(这对于函数处理未知属性是必需的(。
FWIW,getter 函数(我没有打扰二传手逻辑(看起来像这样:
MyObject.prototype.prop = function(propName) {
if (propName in this) {
// This object or its prototype already has this property,
// return the existing value.
return this[propName];
}
// ...Catch-all, deal with undefined property here...
};
但同样,我无法想象你真的想这样做,因为它改变了你使用对象的方式。
前言:
T.J. Crowder的回答提到了一个Proxy
,对于不存在的属性,一个包罗万象的获取者/设置者将需要它,正如OP所要求的那样。根据动态 getter/setter 实际需要的行为,Proxy
实际上可能不是必需的;或者,您可能希望将Proxy
与我将在下面向您展示的内容结合使用。
(附言我最近在 Linux 上的 Firefox 中彻底尝试了Proxy
,发现它非常强大,但也有些令人困惑/难以使用和正确处理。更重要的是,我还发现它非常慢(至少相对于现在的 JavaScript 优化程度而言(——我说的是十进制倍数的慢速度。
要专门实现动态创建的 getter 和 setter,您可以使用 Object.defineProperty()
或 Object.defineProperties()
。这也相当快。
要点是您可以在对象上定义 getter 和/或 setter,如下所示:
let obj = {};
let val = 0;
Object.defineProperty(obj, 'prop', { //<- This object is called a "property descriptor".
//Alternatively, use: `get() {}`
get: function() {
return val;
},
//Alternatively, use: `set(newValue) {}`
set: function(newValue) {
val = newValue;
}
});
//Calls the getter function.
console.log(obj.prop);
let copy = obj.prop;
//Etc.
//Calls the setter function.
obj.prop = 10;
++obj.prop;
//Etc.
这里需要注意的几点:
- 您不能将属性描述符(上面未显示(中的
value
属性与get
和/或set
一起使用; 从文档中:对象中存在的属性描述符有两种主要形式:数据描述符和访问器描述符。数据描述符是具有值的属性,该值可能是可写的,也可能是不可写的。访问器描述符是由一对 getter-setter 函数描述的属性。描述符必须是这两种风格之一;它不能两者兼而有之。
- 因此,您会注意到我在
Object.defineProperty()
调用/属性描述符之外创建了一个val
属性。这是标准行为。 - 根据此处的错误,如果使用
get
或set
,请不要在属性描述符中将writable
设置为true
。 - 但是,您可能需要考虑设置
configurable
和enumerable
,具体取决于您的追求; 从文档中:配置
true
当且仅当可以更改此属性描述符的类型,并且是否可以从相应的对象中删除该属性时。默认为 false。
可枚举
true
当且仅当此属性在枚举相应对象的属性期间显示时。默认为 false。
在这一点上,这些也可能令人感兴趣:
-
Object.getOwnPropertyNames(obj)
:获取对象的所有属性,甚至是不可枚举的属性(AFAIK 这是这样做的唯一方法! -
Object.getOwnPropertyDescriptor(obj, prop)
:获取对象的属性描述符,即传递给上述Object.defineProperty()
的对象。 -
obj.propertyIsEnumerable(prop);
:对于特定对象实例上的单个属性,在对象实例上调用此函数以确定特定属性是否可枚举。
以下是解决此问题的原始方法:
var obj = {
emptyValue: null,
get: function(prop){
if(typeof this[prop] == "undefined")
return this.emptyValue;
else
return this[prop];
},
set: function(prop,value){
this[prop] = value;
}
}
为了使用它,属性应作为字符串传递。所以这里有一个它是如何工作的一个例子:
//To set a property
obj.set('myProperty','myValue');
//To get a property
var myVar = obj.get('myProperty');
编辑:基于我建议的改进的、更面向对象的方法如下:
function MyObject() {
var emptyValue = null;
var obj = {};
this.get = function(prop){
return (typeof obj[prop] == "undefined") ? emptyValue : obj[prop];
};
this.set = function(prop,value){
obj[prop] = value;
};
}
var newObj = new MyObject();
newObj.set('myProperty','MyValue');
alert(newObj.get('myProperty'));
您可以在此处看到它的工作。
我正在寻找一些东西,我自己想通了。
/*
This function takes an object and converts to a proxy object.
It also takes care of proxying nested objectsa and array.
*/
let getProxy = (original) => {
return new Proxy(original, {
get(target, name, receiver) {
let rv = Reflect.get(target, name, receiver);
return rv;
},
set(target, name, value, receiver) {
// Proxies new objects
if(typeof value === "object"){
value = getProxy(value);
}
return Reflect.set(target, name, value, receiver);
}
})
}
let first = {};
let proxy = getProxy(first);
/*
Here are the tests
*/
proxy.name={} // object
proxy.name.first={} // nested object
proxy.name.first.names=[] // nested array
proxy.name.first.names[0]={first:"vetri"} // nested array with an object
/*
Here are the serialised values
*/
console.log(JSON.stringify(first)) // {"name":{"first":{"names":[{"first":"vetri"}]}}}
console.log(JSON.stringify(proxy)) // {"name":{"first":{"names":[{"first":"vetri"}]}}}
var x={}
var propName = 'value'
var get = Function("return this['" + propName + "']")
var set = Function("newValue", "this['" + propName + "'] = newValue")
var handler = { 'get': get, 'set': set, enumerable: true, configurable: true }
Object.defineProperty(x, propName, handler)
这对我有用