如何像数组析构函数一样重写ES类的对象析构函数



TL;DR

如何使{...myCls}返回{...myCls.instanceVar}

实际问题

我正在尝试实现*[Symbol.iterator]() { yield this.internalObj; }的自定义版本,以便类的对象扩展对myClass.instanceVar执行对象扩展操作。具体来说,我想让{...(new MyClass('a', 'b'}))}返回{...(new MyClass('a', 'b'})).innerVal}。然而,我们似乎无法覆盖对象扩展逻辑,只能覆盖数组扩展逻辑。

例如,这是一个创建Array包装的简单类

class MyArray {
innerArray = [];
getItem(index) {
return (index < this.innerArray.length) ? this.innerArray[index] : null;
}
setItem(index, val) {
const innerArrayLength = this.innerArray.length;
return (index < innerArrayLength)
?
this.innerArray[index] = val
:
this.innerArray.push(val)
;
}
removeItem(index) {
return this.innerArray.splice(index, 1);
}
clear() {
this.innerArray = [];
}
get length() {
return this.innerArray.length;
}
*[Symbol.iterator]() {
return yield* this.innerArray;
}
}
// Usage:
let myArr = new MyArray()   // undefined
myArr.setItem(0, 'a')   // 1
myArr.setItem(10, 'b')   // 2
console.log([...myArr])   // (2) [ 0 => "a", 1 => "b" ]

然而,我想要的是用对象类实例变量而不是数组类别实例变量来实现这一点。

例如,当我尝试实现StorageMock类时,就会发生这种情况

class StorageMock {
storage = {};
setItem(key, val) {
this.storage[key] = val;
}
getItem(key) {
return (key in this.storage) ? this.storage[key] : null;
}
removeItem(key) {
delete this.storage[key];
}
clear() {
this.storage = {};
}
get length() {
return Object.keys(this.storage).length;
}
key(index) {
return Object.keys(this.storage)[index] || null;
}
*[Symbol.iterator]() {
return yield* Object.entries(this.storage).map(([ k, v ]) => ({ [k]: v }));
}
}
let myStore = new StorageMock()    // undefined
myStore.setItem('a', 'hello');    // undefined
myStore.setItem('b', 'world');    // undefined
console.log({...myStore});   // { storage: { a: "hello", b: "world" } }  <== PROBLEM
// Doing the same with localStorage prints out:
// { a: "hello", b: "world" }
// instead of
// { storage: { a: "hello", b: "world" } }

在这种情况下,当扩展(local|session)Storage时,Storage API可以扩展存储条目,但创建特殊的StorageMock类则不行。

重点是我不能做{...storageMockInstance} === {...(storageMockInstance.storage)}。那么,如何覆盖ES类的对象扩展语法呢?

参考/尝试

我尝试了Object.create()Object.definePropert(y|ies)()in运算符变体的各种组合(所有这些都具有此处定义的相关访问能力),所有这些都取决于通用扩展语法建议中的for...in语法定义。但我所发现的只是;标准";可以根据参考文献1、2和3使用析构函数。

但是必须有一种方法通过ESNext类来实现这一点。我没有尝试使用实际的本机类功能,而是通过AMD模块语法提供的功能。我不能以类似于其他语言的方式重写这些字段,这似乎是不合理的。例如,如果我只能以Python允许重写JSfor..in循环的方式重写它,那么我可以像toString()toJSON()一样通过forIn()方法扩展内部变量。

注意

请不要用@babel/polyfillcore-jsbabel-jest回答此问题。它不仅适用于(local|session)Storage,而且只是一个关于高级问题的问题。

简短回答

你不能。

除非你作弊。但可能不值得。

实际答案

术语";数组破坏";可能有点误导。实际上,总是从获取对象的迭代器开始,并从中提取值,直到满足所有绑定。事实上,它不应该只用于数组。

const obj = { 
*[Symbol.iterator]() { 
yield 1; 
yield 2; 
yield 3; 
yield 4; 
} 
};
//1. take iterator
//2. take first three values
const [a, b, c] = obj;
//1. take iterator (do not continue the old one)
//2. take the first value 
const [x] = obj;
console.log(a, b, c, x); // 1, 2, 3, 1

然而,对象销毁没有类似的机制。当使用CCD_ 24时,执行抽象运算CCD_。顾名思义,它将复制属性,而不是调用某种机制来获取要复制的数据。

将被复制的属性将是

  1. 自己的-不是来自原型
  2. 与访问器属性(由getter和/或setter定义的属性)相对的数据属性
  3. 可枚举
  4. 不是抽象操作的excludedNames参数的一部分。注意:这仅在将排列与休息目标(如const {foo, bar, ...rest} = obj;)一起使用时才相关

可以做的是向运行时撒谎。这可以使用代理来完成,您需要更改以下内容:

  1. ownKeys陷阱,用于切换用于销毁的密钥
  2. getOwnPropertyDescriptor陷阱以确保属性是可枚举的
  3. get陷阱,以给出属性的值

例如,这可以作为这样的函数来完成,并将使对象的行为就像您正在使用其属性值之一:

const obj = {
a: 1,  
b: 2, 
c: 3, 
myProp: {
d: 4, 
e: 5, 
f: 6
}
};
const x = { ...lieAboutSpread("myProp", obj) };
console.log(x);
function lieAboutSpread(prop, obj) {
//this will be the false target for spreading
const newTarget = obj[prop];

const handler = {
// return the false target's keys
ownKeys() {
return Reflect.ownKeys(newTarget);
},
// return the false target's property descriptors
getOwnPropertyDescriptor(target, property) {
return Reflect.getOwnPropertyDescriptor(newTarget, property);
},
// return the false target's values
get(target, property, receiver) { 
return Reflect.get(newTarget, property, receiver);
}
}

return new Proxy(obj, handler);
}

因此,这是可能的。然而,我不确定简单地做{ ...obj.myProp }有多大好处。此外,上述函数可以被重写;谎言;完全但它变得非常无聊:

const obj = {
a: 1,  
b: 2, 
c: 3, 
myProp: {
d: 4, 
e: 5, 
f: 6
}
};
const x = { ...lieAboutSpread("myProp", obj) };
console.log(x);
function lieAboutSpread(prop, obj) {
//this will be the target for spreading
return obj[prop];
}

在我看来,这突出了为什么人为掩盖物体的方式是过度的。

最新更新