Object.assign和代理

时间:2017-04-03 13:06:11

标签: javascript ecmascript-6 proxy-classes

有以下对象:

let obj = { id: 0 };

以及以下Proxy

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

是否可以在Proxy之后“保留”Object.assign()(或者对象扩展运算符,其中afaik只是Object.assign()的语法糖)?

let objProxyNew = Object.assign({}, objProxy); // i.e. {...objProxy};

以便objProxyNew.id返回"id from proxy"

1 个答案:

答案 0 :(得分:2)

好像我是完全相同的问题的第三个人,这是我发现的关于stackoverflow的最接近的问题,但是它没有真正的答案,所以我必须自己进行调查。

顺便说一句,Philip在他的示例中想要的行为是默认行为,因此无需进行任何更改:

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"

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

为JavaScript代理对象实施{...obj}

让我们举一个更复杂的示例,一个“ 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}

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

正如我所发现的,之所以发生这种情况,是因为我们尚未为“ ownKeys”定义陷阱,并且默认情况下调用了Object.getOwnPropertyNames(target)

扩展代理:

    ownKeys(target) { return target.keys; }

甚至更糟,因为现在根本没有克隆任何属性:

console.log({...objProxy}); // {}

现在正在发生的事情是Object.assign为“ ownKeys”函数返回的每个键调用Object.getOwnPropertyDescriptor。默认情况下,从“目标”中检索属性描述符,但是我们可以使用另一个名为“ 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 };
    }
});

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

  

VM1028:1代理:陷阱上未捕获的TypeError:'getOwnPropertyDescriptor'   报告的属性“ a”的不可配置性为   代理目标中不存在或不可配置       在:1:1

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

value似乎没有被Object.assign使用,但是可能被其他一些框架或实现所使用。如果实际获取值成本很高,我们可以将其定义为获取方法:

    get value() { return this.get(target, name); }

要支持in运算符并实现一致的实现,我们还应该实现“具有”陷阱。因此最终的实现如下所示:

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) => target.keys,
	getOwnPropertyDescriptor(target, name) {
		const proxy = this;
		return { get value() { return proxy.get(target, name); }, configurable: true, enumerable: true };
	},
	has: (target, name) => target.keys.indexOf(name) >= 0
});

console.log({...objProxy}); // {a: 1, b: 3, c: 5, d: 7}

为JavaScript代理对象实施[...obj]

另一个故事是支持[...objProxy]-在这里,我们需要在吸气剂中定义[Symbol.iterator]

let objProxy = new Proxy({
	values: [1, 2, 3, 4],
	delta: [9, 8, 7, 6]
}, {
	get(target, name){
		if (name === Symbol.iterator) {
			return function*() {
				for (let i = 0; i < target.values.length; i ++) { yield target.values[i] + target.delta[i]; }
			}
		}
		return target.values[name] + target.delta[name];
	}
});

console.log([...objProxy]); // [10, 10, 10, 10]

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

return () => target.values[Symbol.iterator]();

return target.values[Symbol.iterator].bind(target.values);

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