Javascript Proxy set() local property on inherited

2019-07-09 04:03发布

According to MDN, handler.set() can trap Inherited property assignment:

Object.create(proxy)[foo] = bar;

In which case, how does one both monitor and allow local assignments on inherited objects?

var base = {
	foo: function(){
		return "foo";
	}
}

var proxy = new Proxy(base, {
	set: function(target, property, value, receiver){
		console.log("called: " + property + " = " + value, "on", receiver);
		//receiver[property] = value; //Infinite loop!?!?!?!?!
		//target[property] = value // This is incorrect -> it will set the property on base.
		
		/*
			Fill in code here.
		*/
		return true;
	}
})

var inherited = {}
Object.setPrototypeOf(inherited, Object.create(proxy));

inherited.bar = function(){
	return "bar";
}

//Test cases
console.log(base.foo); //function foo
console.log(base.bar); //undefined
console.log(inherited.hasOwnProperty("bar")) //true

2条回答
Root(大扎)
2楼-- · 2019-07-09 04:43

After some additional thought, i noticed that it intercepts 3 ops:

Property assignment: proxy[foo] = bar and proxy.foo = bar Inherited property assignment: Object.create(proxy)[foo] = bar
Reflect.set()

but not Object.defineProperty() which appears to be even lower level than the = operator.

Thus the following works:

var base = {
    foo: function(){
        return "foo";
    }
};

var proxy = new Proxy(base, {
    set: function(target, property, value, receiver){
        var p = Object.getPrototypeOf(receiver);
      
        Object.defineProperty(receiver, property, { value: value });   // ***
        return true;
    }
});

var inherited = {};
Object.setPrototypeOf(inherited, Object.create(proxy));

inherited.bar = function(){
    return "bar";
};

// Test cases
console.log(base.foo);                       // function foo
console.log(base.bar);                       // undefined
console.log(inherited.bar);                  // function bar
console.log(inherited.hasOwnProperty("bar")) // true

查看更多
迷人小祖宗
3楼-- · 2019-07-09 04:56

I see two options (maybe):

  1. Store the property in a Map, keeping the Maps for various receivers in a WeakMap keyed by the receiver. Satisfy get by checking the Map and returning the mapping there instead of from the object. (Also has.) Slight problem is that you also need to proxy the receiver (not just base) in order to handle ownKeys. So this could be unworkable.

  2. Temporarily get the proxy out of the inheritance chain while setting.

Here's that second one:

var base = {
    foo: function(){
        return "foo";
    }
};

var proxy = new Proxy(base, {
    set: function(target, property, value, receiver){
        const p = Object.getPrototypeOf(receiver); // ***
        Object.setPrototypeOf(receiver, null);     // ***
        receiver[property] = value;                // ***
        Object.setPrototypeOf(receiver, p);        // ***
        return true;
    }
});

var inherited = {};
Object.setPrototypeOf(inherited, Object.create(proxy));

inherited.bar = function(){
    return "bar";
};

// Test cases
console.log("base.foo:", base.foo); // function foo
console.log("base.bar:", base.bar); // undefined
console.log("inherited.bar:", inherited.bar); // function bar
console.log("inherited has own bar?", inherited.hasOwnProperty("bar")); // true

查看更多
登录 后发表回答