meg*_*lop 119 javascript metaprogramming getter-setter
我知道如何通过执行以下操作为名称已知的属性创建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"
Run Code Online (Sandbox Code Playgroud)
现在,我的问题是,是否有可能定义这些全能的吸气剂和定型器?即,创建getter和setter的任何属性名称是不是已经定义.
这个概念可以在PHP中使用__get()和__set()魔术方法(请参阅PHP文档以获取有关这些的信息),所以我真的要问是否有与这些相当的JavaScript?
不用说,我理想地喜欢跨浏览器兼容的解决方案.
T.J*_*der 191
2013年和2015年更新 (见以下2011年的原始答案):
这改变了ES2015(又名"ES6")规范:JavaScript现在有代理.代理允许您创建对象(外观)其他对象的真实代理.这是一个简单的示例,它将任何字符串属性值转换为检索的所有上限:
"use strict";
if (typeof Proxy == "undefined") {
throw new Error("This browser doesn't support Proxy");
}
let original = {
"foo": "bar"
};
let proxy = new Proxy(original, {
get(target, name, receiver) {
let rv = Reflect.get(target, name, receiver);
if (typeof rv === "string") {
rv = rv.toUpperCase();
}
return rv;
}
});
console.log(`original.foo = ${original.foo}`); // "original.foo = bar"
console.log(`proxy.foo = ${proxy.foo}`); // "proxy.foo = BAR"Run Code Online (Sandbox Code Playgroud)
您未覆盖的操作具有默认行为.在上面,我们覆盖的是get,但是有一个可以挂钩的操作列表.
在get处理函数的参数列表中:
target是被代理的对象(original在我们的例子中).name 是(当然)被检索的属性的名称,通常是一个字符串,但也可以是一个符号.receiverthis如果属性是访问者而不是数据属性,则应该在getter函数中使用该对象.在正常情况下,这是代理或从其继承的东西,但它可以是任何东西,因为陷阱可能被触发Reflect.get.这使您可以使用所需的catch-all 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.foo = ${obj.foo}`);
obj.foo = "bar";
console.log(`[after] obj.foo = ${obj.foo}`);Run Code Online (Sandbox Code Playgroud)
以上的输出是:
Getting non-existent property 'foo' [before] obj.foo = undefined Setting non-existent property 'foo', initial value: bar [after] obj.foo = bar
请注意当我们尝试在foo尚不存在时检索"不存在"消息时,以及当我们创建它时,我们会再次获取"不存在"消息,但在此之后不会.
从2011年回答 (见上文2013年和2015年更新):
不,JavaScript没有全面的属性功能.您正在使用的访问器语法在规范的第11.1.5节中介绍,并且不提供任何通配符或类似的东西.
当然,您可以实现一个函数来执行此操作,但我猜您可能不想使用f = obj.prop("foo");而不是f = obj.foo;(obj.prop("foo", value);而不是obj.foo = value;函数处理未知属性所必需的).
FWIW,getter函数(我没有使用setter逻辑)看起来像这样:
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...
};
Run Code Online (Sandbox Code Playgroud)
但同样,我无法想象你真的想要这样做,因为它改变了你如何使用这个对象.
And*_*rew 18
前言:
TJ Crowder 的答案提到了 a Proxy,对于不存在的属性,它需要一个包罗万象的 getter/setter ,正如 OP 所要求的那样。根据动态 getter/setter 实际需要的行为, aProxy实际上可能不是必需的;Proxy或者,您可能希望将 a与我将在下面向您展示的内容结合使用。
(PS 我最近在 Linux 上的 Firefox 中进行了Proxy彻底的实验,发现它非常有能力,但也有些令人困惑/难以使用和正确使用。更重要的是,我还发现它相当慢(至少在与当今 JavaScript 的优化程度有关) - 我说的是慢十倍的领域。)
要专门实现动态创建的 getter 和 setter,您可以使用Object.defineProperty()or 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.
Run Code Online (Sandbox Code Playgroud)
这里需要注意几点:
value属性描述符(上面未get显示)中的属性和/或set;来自文档:
对象中存在的属性描述符有两种主要类型:数据描述符和访问器描述符。数据描述符是具有值的属性,该值可能是可写的,也可能是不可写的。访问器描述符是由一对 getter-setter 函数描述的属性。描述符必须是这两种风格之一;不可能两者兼而有之。
val属性。这是标准行为。Object.defineProperty()writable为。truegetsetconfigurable和enumerable,具体取决于您的需求;来自文档:
可配置
true当且仅当该属性描述符的类型可以更改并且该属性可以从相应的对象中删除时。默认为 false。
可枚举的
true当且仅当该属性在相应对象的属性枚举期间出现。默认为 false。
就此而言,这些也可能令人感兴趣:
Object.getOwnPropertyNames(obj):获取对象的所有属性,甚至是不可枚举的属性(据我所知这是唯一的方法!)。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;
}
}
Run Code Online (Sandbox Code Playgroud)
为了使用它,属性应该作为字符串传递.所以这是一个如何工作的例子:
//To set a property
obj.set('myProperty','myValue');
//To get a property
var myVar = obj.get('myProperty');
Run Code Online (Sandbox Code Playgroud)
编辑: 基于我提出的改进,更面向对象的方法如下:
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'));
Run Code Online (Sandbox Code Playgroud)
你可以看到它在这里工作.
| 归档时间: |
|
| 查看次数: |
45044 次 |
| 最近记录: |