Is it possible to implement dynamic getters/setters in JavaScript?

前端 未结 4 1036
甜味超标
甜味超标 2020-11-22 17:39

I am aware of how to create getters and setters for properties whose names one already knows, by doing something like this:

// A trivial example:
function My         


        
4条回答
  •  没有蜡笔的小新
    2020-11-22 17:48

    Preface:

    T.J. Crowder's answer mentions a Proxy, which will be needed for a catch-all getter/setter for properties which don't exist, as the OP was asking for. Depending on what behavior is actually wanted with dynamic getters/setters, a Proxy may not actually be necessary though; or, potentially, you may want to use a combination of a Proxy with what I'll show you below.

    (P.S. I have experimented with Proxy thoroughly in Firefox on Linux recently and have found it to be very capable, but also somewhat confusing/difficult to work with and get right. More importantly, I have also found it to be quite slow (at least in relation to how optimized JavaScript tends to be nowadays) - I'm talking in the realm of deca-multiples slower.)


    To implement dynamically created getters and setters specifically, you can use Object.defineProperty() or Object.defineProperties(). This is also quite fast.

    The gist is that you can define a getter and/or setter on an object like so:

    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.
    

    Several things to note here:

    • You cannot use the value property in the property descriptor (not shown above) simultaneously with get and/or set; from the docs:

      Property descriptors present in objects come in two main flavors: data descriptors and accessor descriptors. A data descriptor is a property that has a value, which may or may not be writable. An accessor descriptor is a property described by a getter-setter pair of functions. A descriptor must be one of these two flavors; it cannot be both.

    • Thus, you'll note that I created a val property outside of the Object.defineProperty() call/property descriptor. This is standard behavior.
    • As per the error here, don't set writable to true in the property descriptor if you use get or set.
    • You might want to consider setting configurable and enumerable, however, depending on what you're after; from the docs:

      configurable

      • true if and only if the type of this property descriptor may be changed and if the property may be deleted from the corresponding object.

      • Defaults to false.


      enumerable

      • true if and only if this property shows up during enumeration of the properties on the corresponding object.

      • Defaults to false.


    On this note, these may also be of interest:

    • Object.getOwnPropertyNames(obj): gets all properties of an object, even non-enumerable ones (AFAIK this is the only way to do so!).
    • Object.getOwnPropertyDescriptor(obj, prop): gets the property descriptor of an object, the object that was passed to Object.defineProperty() above.
    • obj.propertyIsEnumerable(prop);: for an individual property on a specific object instance, call this function on the object instance to determine whether the specific property is enumerable or not.

提交回复
热议问题