Observe changes to a Map using a Proxy [duplicate] - javascript

This question already has answers here:
Why is Proxy to a Map object in ES2015 not working
(2 answers)
Closed 5 years ago.
I use a Map javascript object. I'd like to observe changes to a Map instance using a Proxy object. However, whatever handler object I try to feed in to the proxy, I consistently get the error Method Map.prototype.set called on incompatible receiver. What am I doing wrong? The idiomatic example I expect to work looks like
var map = new Map();
var proxy = new Proxy(map, {
set(obj, prop, value) {
console.log(value);
}
});
proxy.set(1, 1); --> error
I also tried setting a handler for apply, but to no avail.

First off, understand that your error also reproduces with just
var map = new Map();
var proxy = new Proxy(map, {});
proxy.set(1, 1);
It is not related to your usage of set(obj, prop, value).
Why it fails
To break that a bit more, understand that this is basically the same as doing
var map = new Map();
var proxy = new Proxy(map, {});
Map.prototype.set.call(proxy, 1, 1);
which also errors. You are calling the set function for Map instances, but passing it a Proxy instead of a Map instance.
And that is the core of the issue here. Maps store their data using an private internal slot that is specifically associated with the map object itself. Proxies do not behave 100% transparently. They allow you to intercept a certain set of operations on an object, and perform logic when they happen, which usually means proxying that logic through to some other object, in your case from proxy to map.
Unfortunately for your case, proxying access to the Map instance's private internal slot is not one of the behaviors that can be intercepted. You could kind of imagine it like
var PRIVATE = new WeakMap();
var obj = {};
PRIVATE.set(obj, "private stuff");
var proxy = new Proxy(obj, {});
PRIVATE.get(proxy) === undefined // true
PRIVATE.get(obj) === "private stuff" // true
so because the object pass as this to Map.prototype.set is not a real Map, it can't find the data it needs and will throw an exception.
Solution
The solution here means you actually need to make the correct this get passed to Map.prototype.set.
In the case of a proxy, the easiest approach would be to actually intercept the access to .set, e.g
var map = new Map();
var proxy = new Proxy(map, {
get(target, prop, receiver) {
// Perform the normal non-proxied behavior.
var value = Reflect.get(target, prop, receiver);
// If something is accessing the property `proxy.set`, override it
// to automatically do `proxy.set.bind(map)` so that when the
// function is called `this` will be `map` instead of `proxy`.
if (prop === "set" && typeof value === "function") value = value.bind(target);
return value;
}
});
proxy.set(1, 1);
Of course that doesn't address your question about intercepting the actual calls to .set, so for that you can expand on this to do
var map = new Map();
var proxy = new Proxy(map, {
get(target, prop, receiver) {
var value = Reflect.get(target, prop, receiver);
if (prop === "set" && typeof value === "function") {
// When `proxy.set` is accessed, return your own
// fake implementation that logs the arguments, then
// calls the original .set() behavior.
const origSet = value;
value = function(key, value) {
console.log(key, value);
return origSet.apply(map, arguments);
};
}
return value;
}
});
proxy.set(1, 1);

Related

Why is Set not executed when using Proxy on Map Object? [duplicate]

This question already has answers here:
Why is Proxy to a Map object in ES2015 not working
(2 answers)
Closed 4 years ago.
I have created a simple Proxy example on a Map Object. I can't figure out why the Set handler is not being executed and Get is executed instead, for Sets.
const handler = {
get: (targetObj, propName, receiverProxy) => {
console.log('PROXY: From get.')
let ret = Reflect.get(targetObj, propName, receiverProxy)
if (typeof ret === 'function') {
ret = ret.bind(targetObj)
}
return ret
},
set: (targetObj, propName, propValue, receiverProxy) => {
console.log('PROXY: From set.')
return Reflect.set(targetObj, propName, propValue, receiverProxy)
},
}
const targetMap = new Map([
['iron', 'man'],
])
const targetObj = {}
const proxy = new Proxy(targetMap, handler)
console.log(proxy.set('super', 'man'))
console.log(proxy.get('super'))
If you want a proxy setup that lets you intercept attempted calls to the Map .set() method, you'd have to do something like this:
let handler = {
get: (target, property, proxy) {
if (property === "set") {
console.log("intercepted a '.set' access on the proxied map");
return (key, value) => {
console.log("intercepted a 'set()' call on the proxied map");
return target.set(key, value);
};
}
return target[property];
}
};
The "get" handler method is called for any property access is attempted. In the method call
proxy.set("some key", "some value");
the "set" property of the object has to be looked up before the method can actually be called. It's that lookup operation that results in the handler "get" method invocation.
Now if you make the proxy as in your code and then do
proxy.set("some key", "some value");
you'll see that the log message fires when that "interceptor" function you returned is invoked.
Set and get doesn't work like that.
You can use the proxy object like any normal object. For example:
proxy.super = 'man'; // fires the setter
const x = proxy.super; // fires the getter
Then, in your get/set handlers you can call the original get/set methods on the targetObj.

Determining if get handler in Proxy object is handling a function call

I currently have a Proxy object that I want to capture property calls to if the property is not defined.
A basic version of my code would be something like this.
var a = new Proxy({}, {
get: function(target, name, receiver) {
if (target in name) {
return target[name];
} else {
function a() {
return arguments;
}
var args = a();
return [target, name, receiver, args];
}
}
});
Property calls to a here (i.e: a.b; a.c() etc) should return the target, name, receiver and arguments of the property call.
The problem I wish to solve, however, requires me to know whether the property call is for a property or a function, such that I can apply different treatments to each. Checking the length of the arguments object does not work, as calling a.c() would yield a length of 0 just like a.b, so it would be treated as a plain property and not a method.
Is there a way, therefore, to identify whether the property attempting to be accessed is being called as a function or not.
UPDATE: I should clarify, this method needs to work if the accessed property/method is undefined, as well as existing.
It's possible in a very hacky way. We return a function if the property is undefined. If this function is called, then we know the user was trying to call the property as a function. If it never is, it was called as a property. To check if the function was called, we take advantage of the fact that a Promise's callback is called in the next iteration of the event loop. This means that we won't know if it's a property or not until later, as the user needs a chance to call the function first (as our code is a getter).
One drawback of this method is that the value returned from the object will be the new function, not undefined, if the user was expecting a property. Also this won't work for you if you need the result right away and can't wait until the next event loop iteration.
const obj = {
func: undefined,
realFunc: () => "Real Func Called",
prop: undefined,
realProp: true
};
const handlers = {
get: (target, name) => {
const prop = target[name];
if (prop != null) { return prop; }
let isProp = true;
Promise.resolve().then(() => {
if (isProp) {
console.log(`Undefined ${name} is Prop`)
} else {
console.log(`Undefined ${name} is Func`);
}
});
return new Proxy(()=>{}, {
get: handlers.get,
apply: () => {
isProp = false;
return new Proxy(()=>{}, handlers);
}
});
}
};
const proxied = new Proxy(obj, handlers);
let res = proxied.func();
res = proxied.func;
res = proxied.prop;
res = proxied.realFunc();
console.log(`realFunc: ${res}`);
res = proxied.realProp;
console.log(`realProp: ${res}`);
proxied.propC1.funcC2().propC3.funcC4().funcC5();
Would the typeof operator work for you?
For example:
if(typeof(a) === "function")
{
...
}
else
{
...
}
You can't know ahead of time whether it's a call expression or just a member expression, but you can deal with both situations simultaneously.
By returning a proxy targeting a deep clone of the original property that reflects all but two trap handlers to the original property, you can either chain or invoke each member expression.
The catch is that the proxy target also needs to be callable so that the handler.apply trap does not throw a TypeError:
function watch(value, name) {
// create handler for proxy
const handler = new Proxy({
apply (target, thisArg, argsList) {
// something was invoked, so return custom array
return [value, name, receiver, argsList];
},
get (target, property) {
// a property was accessed, so wrap it in a proxy if possible
const {
writable,
configurable
} = Object.getOwnPropertyDescriptor(target, property) || { configurable: true };
return writable || configurable
? watch(value === object ? value[property] : undefined, property)
: target[property];
}
}, {
get (handler, trap) {
if (trap in handler) {
return handler[trap];
}
// reflect intercepted traps as if operating on original value
return (target, ...args) => Reflect[trap].call(handler, value, ...args);
}
});
// coerce to object if value is primitive
const object = Object(value);
// create callable target without any own properties
const target = () => {};
delete target.length;
delete target.name;
// set target to deep clone of object
Object.setPrototypeOf(
Object.defineProperties(target, Object.getOwnPropertyDescriptors(object)),
Object.getPrototypeOf(object)
);
// create proxy of target
const receiver = new Proxy(target, handler);
return receiver;
}
var a = watch({ b: { c: 'string' }, d: 5 }, 'a');
console.log(a('foo', 'bar'));
console.log(a.b());
console.log(a.b.c());
console.log(a.d('hello', 'world'));
console.log(a.f());
console.log(a.f.test());
Open Developer Tools to view Console.
The Stack Snippets Console attempts to stringify the receiver in a weird way that throws a TypeError, but in the native console and Node.js it works fine.
Try it online!
Some ideas I've come up with, which achieve a similar result at a small cost:
A
typeof(a.b) === "function" //`false`, don't call it.
typeof(a.c) === "function" //`true`, call it.
//Maybe you're not intending to try to call non-functions anyways?
a.c();
B
get: function(target, property) {
//For this, it would have to already be set to a function.
if (typeof(target[property] === "function") {
}
}
C
a.b;
//Simply change the structuring a little bit for functions, e.g.:
a.func.c();
//Then, `func` would be set and handled as a special property.

Why is Proxy to a Map object in ES2015 not working

I'm running the following script through Google Chrome Version 57.0.2987.133:
var loggingProxyHandler = {
"get" : function(targetObj, propName, receiverProxy) {
let ret = Reflect.get(targetObj, propName, receiverProxy);
console.log("get("+propName.toString()+"="+ret+")");
return ret;
},
"set" : function(targetObj, propName, propValue, receiverProxy) {
console.log("set("+propName.toString()+"="+propValue+")");
return Reflect.set(targetObj, propName, propValue, receiverProxy);
}
};
function onRunTest()
{
let m1 = new Map();
let p1 = new Proxy(m1, loggingProxyHandler);
p1.set("a", "aval"); // Exception thrown from here
}
onRunTest();
NOTE: Requires a browser supporting ES2015's Proxy
When run, I see the handler's get trap is called to return the Map's set function
and then I receive the following error:
"Uncaught TypeError: Method Map.prototype.set called on incompatible receiver [object Object]"
at Proxy.set (native)
...
I tried removing the trap functions from the loggingProxyHandler (making it an empty object) but still receive the same error.
My understanding was that a Proxy object was supposed to be able to generated for all native ES5 and ES2015 javascript objects. Array seems to work well under the same proxy handler.
Did I misunderstand the specs?
Is my code missing something?
Is there a known bug in Chrome? (I did a search and found no defects for Chrome on this subject.)
The reason you're getting the error is that the proxy isn't getting involved in the p1.set() method call (other than that the get trap is used to retrieve the function reference). So once the function reference has been retrieved, it's called with this set to the proxy p1, not the map m1 — which the set method of a Map doesn't like.
If you're really trying to intercept all property access calls on the Map, you can fix it by binding any function references you're returning from get (see the *** lines):
const loggingProxyHandler = {
get(target, name/*, receiver*/) {
let ret = Reflect.get(target, name);
console.log(`get(${name}=${ret})`);
if (typeof ret === "function") { // ***
ret = ret.bind(target); // ***
} // ***
return ret;
},
set(target, name, value/*, receiver*/) {
console.log(`set(${name}=${value})`);
return Reflect.set(target, name, value);
}
};
function onRunTest() {
const m1 = new Map();
const p1 = new Proxy(m1, loggingProxyHandler);
p1.set("a", "aval");
console.log(p1.get("a")); // "aval"
console.log(p1.size); // 1
}
onRunTest();
NOTE: Requires a browser supporting ES2015's Proxy
Notice that when calling Reflect.get and Reflect.set, we don't pass along the receiver (in fact, we're not using the receiver argument at all in those, so I've commented the parameter out). That means they'll use the target itself as the receiver, which you need if the properties are accessors (like Map's size property) and they need their this to be the actual instance (as Map's size does).
If your goal is just to intercept Map#get and Map#set, though, you don't need a proxy at all. Either:
Create a Map subclass and instantiate that. Assumes you control the creation of the Map instance, though.
Create a new object that inherits from the Map instance, and override get and set; you don't have to be in control of the original Map's creation.
Replace the set and get methods on the Map instance with your own versions.
Here's #1:
class MyMap extends Map {
set(...args) {
console.log("set called");
return super.set(...args);
}
get(...args) {
console.log("get called");
return super.get(...args);
}
}
const m1 = new MyMap();
m1.set("a", "aval");
console.log(m1.get("a"));
#2:
const m1 = new Map();
const p1 = Object.create(m1, {
set: {
value: function(...args) {
console.log("set called");
return m1.set(...args);
}
},
get: {
value: function(...args) {
console.log("get called");
return m1.get(...args);
}
}
});
p1.set("a", "aval");
console.log(p1.get("a"));
#3:
const m1 = new Map();
const m1set = m1.set; // Yes, we know these are `Map.prototype.set` and
const m1get = m1.get; // `get`, but in the generic case, we don't necessarily
m1.set = function(...args) {
console.log("set called");
return m1set.apply(m1, args);
};
m1.get = function(...args) {
console.log("get called");
return m1get.apply(m1, args);
}
m1.set("a", "aval");
console.log(m1.get("a"));
Let me add more to this.
Many built-in objects, for example Map, Set, Date, Promise and others make use of so-called internal slots.
These are like properties but reserved for internal, specification-only purposes. For instance, Map stores items in the internal slot [[MapData]]. Built-in methods access them directly, not via [[Get]]/[[Set]] internal methods. So Proxy can’t intercept that.
For example:
let map = new Map();
let proxy = new Proxy(map, {});
proxy.set('name', 'Pravin'); // Error
Internally, a Map stores all data in its [[MapData]] internal slot. The proxy doesn't have such slot. The built-in method Map.prototype.set method tries to access the internal property this.[[MapData]], but because this=proxy, can't find it in proxy and just fails.
There’s a way to fix it:
let map = new Map();
let proxy = new Proxy(map,{
get(target,prop,receiver){
let value = Reflect.get(...arguments);
return typeof value === 'function'?value.bind(target):value;
}
});
proxy.set('name','Pravin');
console.log(proxy.get('name')); //Pravin (works!)
Now it works fine, because get trap binds function properties, such as map.set, to the target object (map) itself. So the value of this inside proxy.set(...) will be not proxy, but the original map. So when the internal implementation of set tries to access this.[[MapData]] internal slot, it succeeds.

JavaScript proxy objects don't work

JavaScript proxy objects in Firefox don't seem to work on web audio objects.
For example:
audio = new AudioContext();
s = audio.createOscillator();
s0 = new Proxy (s, {});
s0.connect(audio.destination);
s0.start();
The above code should forward all operations on s0 to s. However, I get errors like:
"TypeError: 'start' called on an object that does not implement interface OscillatorNode."
I've searched for any info on this, but have not found anything relevant. Is this a known bug / limitation? Are these objects not proxiable for some definate reason?
Thanks for any info on this.
-- Rich
The problem is that, when you call a method on a proxy, the method will receive the proxy as the this value, not the underlying object.
function Constructor() {}
Constructor.prototype.method = function() {
return this;
};
var obj = new Constructor(),
proxy = new Proxy(obj, {});
obj.method(); // obj
proxy.method(); // proxy
In this case, your AudioContext instance is a non-standard object, so the implementation can have implementation defined internal data stored on it, which can be used to know whether it's an AudioContext or not. Since proxy objects only redirect essential internal methods, it can be detected that it's not an AudioContext instance.
If you really need to use a proxy wrapper, you can try adding a get trap:
var audio = new AudioContext(),
s = audio.createOscillator();
s0 = new Proxy (s, {
get: function(target, property, receiver) {
var val = target[property];
if(typeof val !== 'function') return val;
return function(...args) {
var thisVal = this === receiver ? target : this; /* Unwrap the proxy */
return Reflect.apply(val, thisVal, args);
}
}
});
s0.connect(audio.destination);
s0.start();

Javascript equivalent function to PHP __call [duplicate]

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 MyObject(val){
this.count = 0;
this.value = val;
}
MyObject.prototype = {
get value(){
return this.count < 2 ? "Go away" : this._value;
},
set value(val){
this._value = val + (++this.count);
}
};
var a = new MyObject('foo');
alert(a.value); // --> "Go away"
a.value = 'bar';
alert(a.value); // --> "bar2"
Now, my question is, is it possible to define sort of catch-all getters and setters like these? I.e., create getters and setters for any property name which isn't already defined.
The concept is possible in PHP using the __get() and __set() magic methods (see the PHP documentation for information on these), so I'm really asking is there a JavaScript equivalent to these?
Needless to say, I'd ideally like a solution that is cross-browser compatible.
This changed as of the ES2015 (aka "ES6") specification: JavaScript now has proxies. Proxies let you create objects that are true proxies for (facades on) other objects. Here's a simple example that turns any property values that are strings to all caps on retrieval, and returns "missing" instead of undefined for a property that doesn't exist:
"use strict";
if (typeof Proxy == "undefined") {
throw new Error("This browser doesn't support Proxy");
}
let original = {
example: "value",
};
let proxy = new Proxy(original, {
get(target, name, receiver) {
if (Reflect.has(target, name)) {
let rv = Reflect.get(target, name, receiver);
if (typeof rv === "string") {
rv = rv.toUpperCase();
}
return rv;
}
return "missing";
}
});
console.log(`original.example = ${original.example}`); // "original.example = value"
console.log(`proxy.example = ${proxy.example}`); // "proxy.example = VALUE"
console.log(`proxy.unknown = ${proxy.unknown}`); // "proxy.unknown = missing"
original.example = "updated";
console.log(`original.example = ${original.example}`); // "original.example = updated"
console.log(`proxy.example = ${proxy.example}`); // "proxy.example = UPDATED"
Operations you don't override have their default behavior. In the above, all we override is get, but there's a whole list of operations you can hook into.
In the get handler function's arguments list:
target is the object being proxied (original, in our case).
name is (of course) the name of the property being retrieved, which is usually a string but could also be a Symbol.
receiver is the object that should be used as this in the getter function if the property is an accessor rather than a data property. In the normal case this is the proxy or something that inherits from it, but it can be anything since the trap may be triggered by Reflect.get.
This lets you create an object with the catch-all getter and setter feature you want:
"use strict";
if (typeof Proxy == "undefined") {
throw new Error("This browser doesn't support Proxy");
}
let obj = new Proxy({}, {
get(target, name, receiver) {
if (!Reflect.has(target, name)) {
console.log("Getting non-existent property '" + name + "'");
return undefined;
}
return Reflect.get(target, name, receiver);
},
set(target, name, value, receiver) {
if (!Reflect.has(target, name)) {
console.log(`Setting non-existent property '${name}', initial value: ${value}`);
}
return Reflect.set(target, name, value, receiver);
}
});
console.log(`[before] obj.example = ${obj.example}`);
obj.example = "value";
console.log(`[after] obj.example = ${obj.example}`);
The output of the above is:
Getting non-existent property 'example'
[before] obj.example = undefined
Setting non-existent property 'example', initial value: value
[after] obj.example = value
Note how we get the "non-existent" message when we try to retrieve example when it doesn't yet exist, and again when we create it, but not after that.
Answer from 2011 (obsoleted by the above, still relevant to environments limited to ES5 features like Internet Explorer):
No, JavaScript doesn't have a catch-all property feature. The accessor syntax you're using is covered in Section 11.1.5 of the spec, and doesn't offer any wildcard or something like that.
You could, of course, implement a function to do it, but I'm guessing you probably don't want to use f = obj.prop("example"); rather than f = obj.example; and obj.prop("example", value); rather than obj.example = value; (which would be necessary for the function to handle unknown properties).
FWIW, the getter function (I didn't bother with setter logic) would look something like this:
MyObject.prototype.prop = function(propName) {
if (propName in this) {
// This object or its prototype already has this property,
// return the existing value.
return this[propName];
}
// ...Catch-all, deal with undefined property here...
};
But again, I can't imagine you'd really want to do that, because of how it changes how you use the object.
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.
The following could be an original approach to this problem:
var obj = {
emptyValue: null,
get: function(prop){
if(typeof this[prop] == "undefined")
return this.emptyValue;
else
return this[prop];
},
set: function(prop,value){
this[prop] = value;
}
}
In order to use it the properties should be passed as strings.
So here is an example of how it works:
//To set a property
obj.set('myProperty','myValue');
//To get a property
var myVar = obj.get('myProperty');
Edit:
An improved, more object-oriented approach based on what I proposed is the following:
function MyObject() {
var emptyValue = null;
var obj = {};
this.get = function(prop){
return (typeof obj[prop] == "undefined") ? emptyValue : obj[prop];
};
this.set = function(prop,value){
obj[prop] = value;
};
}
var newObj = new MyObject();
newObj.set('myProperty','MyValue');
alert(newObj.get('myProperty'));
You can see it working here.
I was looking for something and I figured out on my own.
/*
This function takes an object and converts to a proxy object.
It also takes care of proxying nested objectsa and array.
*/
let getProxy = (original) => {
return new Proxy(original, {
get(target, name, receiver) {
let rv = Reflect.get(target, name, receiver);
return rv;
},
set(target, name, value, receiver) {
// Proxies new objects
if(typeof value === "object"){
value = getProxy(value);
}
return Reflect.set(target, name, value, receiver);
}
})
}
let first = {};
let proxy = getProxy(first);
/*
Here are the tests
*/
proxy.name={} // object
proxy.name.first={} // nested object
proxy.name.first.names=[] // nested array
proxy.name.first.names[0]={first:"vetri"} // nested array with an object
/*
Here are the serialised values
*/
console.log(JSON.stringify(first)) // {"name":{"first":{"names":[{"first":"vetri"}]}}}
console.log(JSON.stringify(proxy)) // {"name":{"first":{"names":[{"first":"vetri"}]}}}
var x={}
var propName = 'value'
var get = Function("return this['" + propName + "']")
var set = Function("newValue", "this['" + propName + "'] = newValue")
var handler = { 'get': get, 'set': set, enumerable: true, configurable: true }
Object.defineProperty(x, propName, handler)
this works for me

Categories