Example:
let foo = {bar: 'baz', method() { this.bar = 'baz2' }}
let fooProxy = new Proxy(foo, {set(target, key, val) { console.log('set trap triggered!') }})
fooProxy.bar = 'any value' // as expected: set trap triggered!
foo.method() // trap not triggered
Why does this happen? How can triggering trap be enforced even from inside target object?
Edit, mainly in order to explain this to @Bergi:
My main goal is to intercept any changes to foo object, so i can set property e.g. foo.changed to true. Also, I want intercept changes to foo's properties which have array/object type. You know, if I am setting foo's property, everything is ok, but when I e.g. push to one that is array, then proxy cannot intercept that. So I need to convert array/object properties to proxies too (I called them ArrayProxy and ObjectProxy).
Here's my code (typescript):
// Category.ts
class Category extends Model {
title: string = ''
products: Product[] = []
}
// Model.ts
abstract class Model extends BaseModel {
constructor() {
return new Proxy(this, {
set (target, key, val) {
if (Array.isArray(val) {
target[key] = new ArrayProxy(val) // I WANT all array properties to be ArrayProxy but the problem (see below) not let me do that
}
}
})
}
}
// BaseModel.ts
abstract class BaseModel {
constructor(attributes) {
this.setAttributes(attributes)
}
setAttributes(attributes) {
Object.keys(attributes).forEach((key) => {
this[key] = attributes[key] // THE PROBLEM
})
}
}
I've removed code, that does not matter (e.g. similar case for object properties and ObjectProxy).
I will appreciate very much, if there is more elegant way to do what I've done.
Example:
let foo = {bar: 'baz', method() { this.bar = 'baz2' }}
let fooProxy = new Proxy(foo, {set(target, key, val) { console.log('set trap triggered!') }})
fooProxy.bar = 'any value' // as expected: set trap triggered!
foo.method() // trap not triggered
Why does this happen? How can triggering trap be enforced even from inside target object?
Edit, mainly in order to explain this to @Bergi:
My main goal is to intercept any changes to foo object, so i can set property e.g. foo.changed to true. Also, I want intercept changes to foo's properties which have array/object type. You know, if I am setting foo's property, everything is ok, but when I e.g. push to one that is array, then proxy cannot intercept that. So I need to convert array/object properties to proxies too (I called them ArrayProxy and ObjectProxy).
Here's my code (typescript):
// Category.ts
class Category extends Model {
title: string = ''
products: Product[] = []
}
// Model.ts
abstract class Model extends BaseModel {
constructor() {
return new Proxy(this, {
set (target, key, val) {
if (Array.isArray(val) {
target[key] = new ArrayProxy(val) // I WANT all array properties to be ArrayProxy but the problem (see below) not let me do that
}
}
})
}
}
// BaseModel.ts
abstract class BaseModel {
constructor(attributes) {
this.setAttributes(attributes)
}
setAttributes(attributes) {
Object.keys(attributes).forEach((key) => {
this[key] = attributes[key] // THE PROBLEM
})
}
}
I've removed code, that does not matter (e.g. similar case for object properties and ObjectProxy).
I will appreciate very much, if there is more elegant way to do what I've done.
Share Improve this question edited Apr 4, 2018 at 16:03 Nurbol Alpysbayev asked Apr 4, 2018 at 14:49 Nurbol AlpysbayevNurbol Alpysbayev 22k5 gold badges67 silver badges100 bronze badges 11-
1
You need to use
fooProxy.method()
of course. You wouldn't expectfooProxy.method.call(something)
(that setssomething.bar
) to trigger the proxy either? – Bergi Commented Apr 4, 2018 at 15:09 - 1 Well the constructor for a normal object doesn't know anything about the proxy (and shouldn't need to). What is the actual problem you are trying to solve with proxies, and what's your real code? – Bergi Commented Apr 4, 2018 at 15:17
-
2
@Phil: You shouldn't edit other people's code. I don't know how that edit got approved. Adding semicolons and using
const
is imposing your personal style on someone else. – user2437417 Commented Apr 4, 2018 at 15:24 - 1 @NurbolAlpysbayev Well you can only intercept changes to the object after you wrapped the proxy around it, which will happen after the construction of the object. And no, intercepting changes to nested arrays/objects is a pletely different topic (see e.g. here and in many other posts), please don't derail the discussion towards that. – Bergi Commented Apr 4, 2018 at 15:32
-
1
@NurbolAlpysbayev You should simply swap the proxy between
Model
andBaseModel
. As you probably noticed withCategory
, it does work just fine in things that extendModel
(where the constructor returns a proxy). – Bergi Commented Apr 4, 2018 at 16:25
4 Answers
Reset to default 2The trap doesn't exist on the original object. If it could, you wouldn't need a separate proxied object. The idea is to use the proxy instead of the original.
One possibility is to have your original object inherit from a proxied object. This could work, depending on what you actually need to acplish.
let fooProxy = new Proxy({}, {
set(target, key, val) { console.log('set trap triggered!') }
})
let foo = Object.create(fooProxy, {
method: {value: function() { this.bar = 'baz2' }}
})
fooProxy.bar = 'any value'
foo.method()
The set trap is not triggered because the this
that you are accessing within method()
is not the proxy but the original object foo
. A proxy does not change the original object. You can see this by checking what this
is inside method()
:
let foo = {
method () {
return this === fooProxy
}
}
let fooProxy = new Proxy(foo, {})
document.write(foo.method()) //=> false
You could instead explicitly set the context of method()
on invocation by using Function#call
:
let foo = {bar: 'baz', method() { this.bar = 'baz2' }}
let fooProxy = new Proxy(foo, {set(target, key, val) { console.log('set trap triggered!') }})
fooProxy.bar = 'any value' //=> set trap triggered!
foo.method.call(fooProxy) //=> set trap triggered!
In foo.method
you're not setting a propery through the proxy object, so obviously no trap is triggered. A Proxy does not modify the original object.
You can also add the proxy to the object prototype.
SomeObject.prototype = new Proxy(
SomeObject.prototype,
SomeObjectHandlerProxy
);
instance = new SomeObject();
something like should work