当前位置: 首页 > 面试题库 >

是否可以在JavaScript中实现动态获取器/设置器?

袁雅逸
2023-03-14
问题内容

我知道如何通过执行以下操作来为名称已经知道的属性创建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?

不用说,理想情况下,我想要一个跨浏览器兼容的解决方案。


问题答案:

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"

您未覆盖的操作具有其默认行为。在上面,我们要覆盖的全部是get,但是有一个可以挂接的操作的完整列表。

get处理程序函数的参数列表中:

  • target是要代理的对象(original在本例中为)。
  • name (当然)是要检索的属性的名称,通常是一个字符串,但也可以是一个符号。
  • receiver``this如果属性是访问器而不是数据属性,则是应该在getter函数中使用的对象。在正常情况下,这是代理或继承自代理的东西,但是 可以 是任何东西,因为陷阱可能由触发Reflect.get

这使您可以使用所需的全部获取和设置器功能创建对象:

"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}`);

上面的输出是:

获取不存在的属性“ foo”
[之前] obj.foo =未定义
设置不存在的属性'foo',初始值:bar
[之后] 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...
};

但是再次,我无法想象您真的想这样做,因为它如何改变您使用对象的方式。



 类似资料:
  • 问题内容: 如果我能得到类似下面的信息,那将是很棒的。 伪代码: 在打印U时,将返回以下内容: 能够获得小部件设置将非常有用。这样我就可以相应地操纵其他小部件。 问题答案: 如果知道所需的设置,则可以使用该方法获取值,例如 它将打印 如果您想知道所有可用的选项,widget.config包含配置,并且如果您希望的话,可以创建您可能需要的全部或部分设置,例如 输出:

  • 问题内容: JavaScript具有词法作用域,这意味着从函数内部访问的非局部变量在定义时将解析为该函数的父级作用域中存在的变量。这与动态作用域相反,在动态作用域中,从函数内部访问的非局部变量在调用时将解析为该函数的调用范围中存在的变量。 上面的程序以词法范围的语言先打印1,然后再打印2,然后以动态范围的语言先打印3,然后再打印1。由于JavaScript具有词法范围,因此将显示1,然后显示2,如

  • 我的自定义spring boot starter和作为依赖项使用的spring boot应用程序消费者面临一个问题。我在两份申请中都有。yml但似乎我正在寻找的配置只有在消费者中定义时才有效。 我在starter中的配置如下: 我在自动配置类中定义了这个bean: 拥有此应用程序的使用者可以完美地检索到它。yml和其他变量: 但如果我将其从消费者中删除并放入应用程序中。启动程序bean在创建它们时

  • 问题内容: 让我们考虑以下情况。有一个,有一个。子窗格将添加到父窗格。考虑到可以动态地添加和删除子窗格而没有任何限制和顺序的情况下,如何才能仅在可见子窗格的情况下使parentPane可见。当然childPane的可见状态也可以随时更改。是否可以创建动态Bindings.OR,以便我可以动态向其添加/删除子可见属性?如果是,那怎么办?如果没有,那么在这种情况下使用什么解决方案? 问题答案: 您可以

  • 问题内容: 无论如何,是否有一个微调器提示与编辑文本字段所提供的提示类似。我知道您可以使用提示给您一个标题栏,但在单击微调器之前,仍将初始微调器字段留为空白。我目前有一种粗略的方法来将虚拟字段设置为旋转器数组的第一部分,即问题,然后在末尾进行检查以确保旋转器不等于问题字符串。有没有更清洁/更好的方法呢? 谢谢! 问题答案: 这是一个比Ravi Vyas代码简单一些的解决方案(感谢您的启发!):

  • 现在我正在用Apache Kafka做一些测试。在Kafka生产者的配置中,参数batch.size和linger.ms控制批处理策略。是否可以在生产的同时动态地制作这些参数?例如。如果数据摄取率上升很快,我们可能希望增加batch.size以每批积累更多的消息。我没有找到任何动态批处理与Kafka生产者的例子。有没有可能实施?