javascript - Object.assign 和代理

标签 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";
}});

是否可以“保留” ProxyObject.assign() 之后(或对象扩展运算符,afaik 只是 Object.assign() 的语法糖)?
let objProxyNew = Object.assign({}, objProxy); // i.e. {...objProxy};

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

最佳答案

好像我是第三个人遇到完全相同的问题,这是我发现的关于 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"


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

实现 {...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}

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

我发现,这是因为我们没有为“ownKeys”和 Object.getOwnPropertyNames(target) 定义陷阱。默认调用。

扩展代理:
    ownKeys(target) { return target.keys; }

更糟糕的是,因为现在根本没有克隆任何属性:
console.log({...objProxy}); // {}

现在发生的是 Object.assign 调用 Object.getOwnPropertyDescriptor对于“ownKeys”函数返回的每个键。默认情况下,属性描述符是从“target”检索的,但我们可以使用另一个名为“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 Uncaught TypeError: 'getOwnPropertyDescriptor' on proxy: trap reported non-configurability for property 'a' which is either non-existent or configurable in the proxy target at :1:1



我们还需要将“可写”设置为“真”才能在严格模式下设置属性。
value似乎没有被 Object.assign 使用但可能被其他一些框架或实现使用。如果实际获取值的成本很高,我们可以将其定义为 getter:
    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}


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

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

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);

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

关于javascript - Object.assign 和代理,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43185453/

相关文章:

javascript - 如何从复杂对象数组中获取唯一属性数组

javascript - 调用模块函数时注入(inject)的模块未定义

c# - 在什么情况下 `RealProxy.GetTransparentProxy()` 会返回 `null` ?

javascript - jquery、node - 多个 get 请求

javascript - 我正在尝试将一个 JSP 页面以及输入文本值重定向到另一个页面

javascript - 如何使用 javascript 创建 soap 消息进行文件上传?

javascript - 为什么 Ember.js/EAK 会自动生成 ApplicationController 而不是使用我的显式 ApplicationController?

java - 指定正确的 spring-instrument agent jar 的最佳实践

java - spring,如何更改 cglib 命名策略

javascript - 将巨大图像加载到 D3 map 背景中的高效方法