最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - ES6 Proxy: set() trap not triggering, when setting inside target object's method - Stack Overflow

programmeradmin0浏览0评论

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 expect fooProxy.method.call(something) (that sets something.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 and BaseModel. As you probably noticed with Category, it does work just fine in things that extend Model (where the constructor returns a proxy). – Bergi Commented Apr 4, 2018 at 16:25
 |  Show 6 more ments

4 Answers 4

Reset to default 2

The 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

发布评论

评论列表(0)

  1. 暂无评论