Object.assign和代理

Phi*_*sky 6 javascript proxy-classes ecmascript-6

具有以下目的:

let obj = { id: 0 };
Run Code Online (Sandbox Code Playgroud)

和以下内容Proxy

let objProxy = new Proxy(obj, {
  get: (target, name) => {
    if (name == "id")
      return "id from proxy";
}});
Run Code Online (Sandbox Code Playgroud)

是否可以ProxyObject.assign()(或对象传播算子,其afaik只是其语法糖Object.assign())之后“保留” ?

let objProxyNew = Object.assign({}, objProxy); // i.e. {...objProxy};
Run Code Online (Sandbox Code Playgroud)

这样objProxyNew.id返回"id from proxy"

Ada*_*sko 7

似乎我是第三个遇到完全相同问题的人,这是我在 stackoverflow 上找到的最接近的问题,但它没有真正的答案,所以我不得不自己调查。

偶然地,菲利普在他的例子中想要的行为是默认行为,所以没有必要改变:

let obj = { id: 0 };
let objProxy = new Proxy(obj, {
  get: (target, name) => {
    if (name == "id")
      return "id from proxy";
}});
    
let objProxyNew = Object.assign({}, objProxy);

console.log(objProxyNew.id); // "id from proxy"
Run Code Online (Sandbox Code Playgroud)

但这仅适用于代理对象上的属性名称与最终对象相同的简单代理。

实现{...obj}javascript 代理对象

让我们举一个更复杂的例子,一个“zip”操作的代理(将单独的键和值数组组合成一个对象):

let objProxy = new Proxy({
    keys: ["a", "b", "c", "d"],
    values: [1, 3, 5, 7]
}, {
    get(target, name) {
        var index = target.keys.indexOf(name);
        return index >= 0 ? target.values[target.keys.indexOf(name)] : false
    }
});

console.log(objProxy.c); // 5   

console.log({...objProxy}); // {keys: undefined, values: undefined}
Run Code Online (Sandbox Code Playgroud)

现在我们从原始对象中获得了属性,但没有它们的值,因为代理不为“键”和“值”属性返回任何值。

正如我发现的那样,发生这种情况是因为我们没有为“ownKeys”定义陷阱,并且Object.getOwnPropertyNames(target)默认情况下会被调用。

扩展代理:

    ownKeys(target) { return target.keys; }
Run Code Online (Sandbox Code Playgroud)

更糟糕的是,现在根本没有克隆任何属性:

console.log({...objProxy}); // {}
Run Code Online (Sandbox Code Playgroud)

现在发生的是 Object.assign 调用Object.getOwnPropertyDescriptor“ownKeys”函数返回的每个键。默认情况下,属性描述符是从“目标”检索的,但我们可以使用另一个名为“getOwnPropertyDescriptor”的陷阱再次更改它:

let objProxy = new Proxy({
    keys: ["a", "b", "c", "d"],
    values: [1, 3, 5, 7]
}, {
    get(target, name) {
        var index = target.keys.indexOf(name);
        return index >= 0 ? target.values[index] : false
    },
    ownKeys(target) {
        return target.keys;
    },
    getOwnPropertyDescriptor(target, name) {
        return { value: this.get(target, name), configurable: true, enumerable: true };
    }
});
Run Code Online (Sandbox Code Playgroud)

enumerable控制哪些属性将被克隆并在控制台中可见。 configurable必须为代理属性设置,否则我们会得到错误:

VM1028:1 未捕获的类型错误:代理上的“getOwnPropertyDescriptor”:陷阱报告了属性“a”的不可配置性,该属性在代理目标中不存在或可配置:1:1

我们还需要将“writable”设置为“true”才能在严格模式下设置属性。

value似乎不被Object.assign其他框架或实现使用但可能被其他框架或实现使用。如果实际获取值的成本很高,我们可以将其定义为 getter:

    get value() { return this.get(target, name); }
Run Code Online (Sandbox Code Playgroud)

为了支持in操作符并有一致的实现,我们还应该实现“has”陷阱。所以最终的实现可以是这样的:

let objProxy = new Proxy({
    keys: ["a", "b", "c", "d"],
    values: [1, 3, 5, 7]
}, {
    get(target, name) {
        var index = target.keys.indexOf(name);
        return index >= 0 ? target.values[target.keys.indexOf(name)] : false
    }
});

console.log(objProxy.c); // 5   

console.log({...objProxy}); // {keys: undefined, values: undefined}
Run Code Online (Sandbox Code Playgroud)

实现[...obj]javascript 代理对象

另一个故事是支持[...objProxy]- 在这里,[Symbol.iterator]被调用,我们需要在 getter 中定义:

    ownKeys(target) { return target.keys; }
Run Code Online (Sandbox Code Playgroud)

我们也可以将“Symbol.iterator”代理到原始对象:

return () => target.values[Symbol.iterator]();
Run Code Online (Sandbox Code Playgroud)

或者

return target.values[Symbol.iterator].bind(target.values);
Run Code Online (Sandbox Code Playgroud)

我们需要重新绑定原始上下文,否则将为 Proxy 对象执行迭代器