29

我想在代码中实现类似 down 的行为:

function Foo(name) {
    this.name = name;
};

var myFoo = new Foo('myName');

myFoo.name('newMyName'); // sets myFoo.name = 'newMyName'
myFoo.name(); // returns 'myName'

但很明显,在这种情况下,我将使用 name 函数覆盖 name 属性。无论如何有可能实现该功能?

4

2 回答 2

43

在谈论 javascript 中的 getter 和 setter 时,您可能会谈论以下两个概念之一:

1. getter 和 setter 作为一个概念,就像在任何其他 OO 语言中一样。

您的问题中的代码就是这种情况。在这种情况下,对象的属性很简单,可能是对象或函数,也可能是对象或函数。javascript 在同一个命名空间中跟踪两者。实际上,函数只是 javascript 中的对象,因此没有像 C 等语言那样为函数提供单独命名空间的概念。

在这种情况下,“getters”和“setters”只是常规函数,因此需要单独存储值。围绕这一点有几种策略。

一种是使用Java 中常见的隐式getSomething()和样式函数。setSomething()这允许您从属性名称中消除 getter 和 setter 的歧义,因为 getter 和 setter 的名称中添加了单词“get”和“set”。

第二种策略是您在问题中编写的策略。在这种情况下,您需要将属性存储在另一个名称中,以免与 getter/setter 共享相同的名称。

第三种策略是将值存储在闭包中:

    function Foo (name) {
        var name = name;
        this.name = function (str) {
            if (str !== undefined) name = str;
            return name;
        }
    }

请注意,在上面的代码中,值存储在其中,name但 getter/setterthis.name是一个完全不同的变量。这允许您的示例代码按预期工作:

    var me = new Foo('Mark');
    me.name(); // returns Mark
    me.name('Andy'); // sets name to Andy

2. getter 和 setter 作为 javascript 中的一种机制。

这是遵循 ECMAscript 5 规范的较新版本的 javascript 的一个功能。此功能允许属性在读取或写入时执行代码,类似于.innerHTMLDOM 对象的属性在您为其分配内容时调用 HTML 解析器的方式。

getter 和 setter 的语法类似于函数,但引入了getandset关键字来代替function.

带有 getter 和 setter 的属性的简单示例:

    var me = {
        first_name : "",
        last_name : "",
        get name() {
            return this.first_name + " " + this.last_name;
        },
        set name(str) {
            var n = str.split(/\s+/);
            this.first_name = n.shift();
            this.last_name = n.join(' ');
        }
    }

上面的代码允许您将获取和设置的函数first_name视为last_name变量而不是函数。要使用namegetter 和 setter,您只需执行以下操作:

    me.name = "James Bond";
    alert(me.first_name); // should alert James
    alert(me.last_name); // should alert Bond
    me.last_name = "Dean";
    alert(me.name); // should alert James Dean

使用 javascript get/set 机制,您不能使用相同的名称将值存储在对象中。例如:

    var foo = {
        set bar(x) {this.bar=x}
    }

上面的代码将编译但尝试设置 bar:foo.bar = 1会因为无限循环而导致堆栈溢出 - this.bar=setter 内部将再次调用 setter。

于 2013-10-23T05:35:10.967 回答
0

如果您想使用与属性同名的 JavaScript getter/setter,例如拦截某些 setter 以实现副作用,您可以Proxy为您的对象创建一个。

function editableProxy (myObj) {
    return new Proxy(myObj, {
        toJSON: () => myObj,
        get: function (target, prop) {
            return Reflect.get(myObj, prop);
        },
        set: function (target, prop, receiver) {
            if (prop === 'billTo') {
                myObj.billToId = receiver?.id;
            }
            return Reflect.set(myObj, prop, receiver);
        },
    });
};

所有吸气剂都正常工作。设置器正常工作,但如果你设置了一个billTo它也会设置一个 billToId。

let da = {id:123}
let wrapped = editableProxy(da)
let id = wrapped.id // 123
wrapped.id=234
wrapped.id===da.id // true
wrapped.billTo={id:567,name:'Big Bad Bill'} // triggers setter side effect
wrapped.billToId // 567
于 2021-09-15T17:05:18.000 回答