Javascript代理在继承对象上设置本地属性的set()方法

4
根据MDNhandler.set()可以捕获继承属性分配:
Object.create(proxy)[foo] = bar;

在这种情况下,如何同时监视和允许继承对象上的本地赋值?

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个回答

6

经过进一步思考,我注意到它会拦截三个操作:

属性赋值:proxy[foo] = bar 和 proxy.foo = bar 继承属性赋值:Object.create(proxy)[foo] = bar
Reflect.set()

但不包括Object.defineProperty(),这似乎比=运算符还要低级。

因此,以下内容有效:

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


1
为什么我没想到呢? :-) 你可能想在里面加上 writable: true, enumerable: true, configurable: true,使其等价。 - T.J. Crowder
你的那条评论帮我找到了一个bug。我不知道可枚举(和所有其他属性)默认是false。X.X - blackening

1
我看到有两个选择(也许):
  1. 将属性存储在一个 Map 中,将不同接收者的 Map 存储在以接收者为键的 WeakMap 中。通过检查 Map 并返回其中的映射而不是从对象中返回来满足 get(还有 has)。稍微有点问题的是,您还需要代理接收器(而不仅仅是 base)以处理 ownKeys。因此,这可能行不通。
  2. 在设置时暂时将代理从继承链中移出。
以下是第二个选项:

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


抱歉,我想到了另一种使用Object.defineProperty()的答案。如果代理捕获的内容没有漏洞,我认为你的答案会是最好的。 - blackening
@blackening:毫无疑问,你的方法更好。 - T.J. Crowder

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接