0

Originally my objective is when any property of the instances of class OnlyOneProp is set,
i.e. obj.what_ever_property = "value", at last it will only modify obj.the_only_prop.

Behavior is like this:

var obj = new OnlyOneProp();
obj.what_ever_property = "value";
console.log(obj.only_property, obj.what_ever_property);
// expected output:
// >value undefined

Original Question: Is there a way to implement above behaviour?



edit:

With respect to the solution in the answer,
here are some follow up questions:

1) Is there any major flaw to the above code? (Like I had once mixed up receiver and target, which may cause infinite looping on the setter)

2) Would Proxy hinder the performance a lot?

3) Is there any way to bypass the above proxy setter? (Like defineProperty()or so)

4) It can also be an implementation of ReadOnlyObject (after removing the setting line in setter), but would there be a better implementation? (It's a bit out of topic but I also want to know, because I really want to remove the top Proxy which is just overriding the constructor)

Valen
  • 1,693
  • 1
  • 20
  • 17

2 Answers2

1

If you return an object from the constructor, the new operator returns that object, not the new object it passed to the constructor as this. So a more straight forward version of OnePropertyClass might be

class OnePropertyClass{
    constructor( value) {
        var self = this;
        return new Proxy( this, {
           set: function(target, property, value) {
                    self["only_property"] = value;
                    return true;
                }
           }
        );
    }
}

This can be simplified by using an arrow function instead of the closure:

class OnePropertyClass{
    constructor() {
        return new Proxy( this, {
           set: (target, property, value) => {
                    this.only_property = value;
                    return true;
                }
           }
        );
    }
}

var obj = new OnePropertyClass();
obj.what_ever_property = "value";
console.log(obj.only_property, obj.what_ever_property);

It doesn't set up any setter loops because the setter stores the value on the actual this object of the constructor, not on the proxy object returned.

Instances of this version of OnePropertyClass inherit per usual - the constructor property returns the OnePropertyClass constructor function, and Object.prototype properties and methods are still inherited.

You may wish to freeze OnePropertyClass.prototype to prevent additions of any other inherited properties. You may also wish to provide trap functions for defineProperty and possibly setPrototype to prevent run time property additions - see MDN handler object methods for details.

Proxy implementation is probably written in C++ and I would expect most of the additional overheads will lie in calling the setter function.

I have not tested this version for extensibility and did not use the target parameter of the set handler Please experiment before use :-)

traktor
  • 17,588
  • 4
  • 32
  • 53
  • oh thank you I once thought the return value from the constructor is useless as I tried it with returning string, now I know that it has to be a ["complex" object](https://www.bennadel.com/blog/2522-providing-a-return-value-in-a-javascript-constructor.htm). Returning from constructor certainly make the code more clean and beautiful. – Valen Jan 25 '18 at 08:26
0

After digging up from MDN Proxy and inspiration from dynamic setter/getter,

I've come up with the code below:

var OnlyOneProp = new Proxy(
    // target
    class{// normal class definition
        constructor(){
            // console.log("anonymous constructor");
        }
    }, {
    construct(target, args, caller){
        // if(!new.target){}
        // console.log("proxy construct");
        return new Proxy(new target(), {
            set(target, name, value, receiver){
                target.only_property = value;
                return true; // must have according to some specification
            }
        });
    },
});

var obj = new OnlyOneProp();
obj.what_ever_property = "value";
console.log(obj.only_property, obj.what_ever_property);
// output: value undefined

It's fully functioning but as you may see there are two new Proxy() instantiation (although the first one only execute once), which I want to remove if possible.

Valen
  • 1,693
  • 1
  • 20
  • 17