Using Object.defineProperty on a defined property

↘锁芯ラ 提交于 2019-12-13 02:35:32

问题


Consider a pattern like this, where we want to define a nice getter/setter interface for a property to hide some internal validation:

var validThings = {
    'pretty name a': 3293293,
    'pretty name b': 8275850,
    'pretty name c': 2983855
};


function Constructor() {
    var internalThing = {
        name: 'pretty name a',
        id: '3293293'
    };

    Object.defineProperty(this, 'thing', {
        get: function() { return internalThing.name; },
        set: function(val) {
            var id = validThings[val];
            if (id) internalThing = { name: val, id: id };
        }
    });
}

This lets us avoid worrying about the ids so we can just set 'thing' like this:

var constructoid = new Constructor();
constructoid.thing = 'pretty name b';
constructoid.thing; // 'pretty name b';

And of course it prevents us from setting it to an invalid value:

constructoid.thing = 'pretty name d';
constructoid.thing; // 'pretty name b';

But let's say we do want to be able to access the ID from outside the object as well. The most natural interface for that would be as a property of thing

constructoid.thing.id

But how would one go about defining such a property when 'thing' is itself a getter/setter? I am used to being able to throw properties on anything if needed, in JS -- functions, arrays, ice cream cones, whatever. But it seems to not be possible in this case, at least not in any way I've been able to think of.

Object.defineProperty(this.thing, 'id', {...}) // <-- error

Of course, I can simply define a property on the Constructor object itself, something like 'thingID', or I could return both the name and ID from the thing getter. I'm not looking for a solution like that, however obvious; this is a hypothetical question about whether it's actually possible to define a property on a defined property.


回答1:


I found a way:

    Object.defineProperty(this, 'thing', {
        get: function() {
            var name = internalThing.name;
            if (!name) return;
            name = new String(name);
            Object.defineProperty(name, 'id', {
                get: function() { return internalThing.id; }
            });
            return name;
        },
        set: function(val) {
            var id = validThings[val];
            if (id) internalThing = { name: val, id: id };
        }
    });

I've never had an occasion to use the String constructor before, but it turns out it allows you to add properties to a specific string. If anyone has a better approach, I'm still curious about other ways to achieve this effect.


Edit:

As Felix Kling pointed out, String as a constructor is too much trouble. The above pattern would work without caveats if the value returned by the first getter weren't primitive, so it could be useful in some situations, but for primitives I don't think there's any good solution. The object requirement is true for value properties as well -- although unlike accessors, defineProperty will accept a value-type previously defined property as its first argument. Presumably a value property is considered a real object, while an accessor property is a ... phantom abstraction? Whatever it is, you can't pin things to 'it' until 'it' exists (i.e., within a get function).



来源:https://stackoverflow.com/questions/22673941/using-object-defineproperty-on-a-defined-property

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!