以任何值存储元数据



因此,我在一个对象中有不同类型的值。这些值是使用Object.defineProperty()设置的。现在,假设以下示例:

let fooValue = 1;
let barValue = 'bar';
const obj = {};
Object.defineProperty(obj, 'foo', {
get: () => fooValue,
set: newValue => fooValue = newValue,
});
Object.defineProperty(obj, 'bar', {
get: () => barValue,
set: newValue => barValue = newValue,
});

现在我希望能够将这些值传递给一个函数,然后能够识别它是哪个值

function test(val) {
if(val.descriminator === 'foo')
console.log('foo');
else if(val.descriminator === 'bar')
console.log('bar');
}
test(obj.foo); // print foo
test(obj.bar); // print bar

在伪代码中,我希望能够做到这一点:

const obj = {};
obj.foo = 1;
obj.foo.descriminator = 'foo';
obj.bar = 1;
obj.bar.descriminator = 'bar';

我知道该代码不起作用(严格模式下的TypeError(。我也意识到,我远远超出了通常做事的范围。

JavaScript为字符串和数字等基元提供了包装类。您可以将普通值包装在obj的setter中,并使用defineProperty添加元数据。

let fooValue;
let barValue;

const obj = {};
Object.defineProperty(obj, 'foo', {
get: () => fooValue,
set: newValue => fooValue = Object.defineProperty(
new Number(newValue), 
'descriminator', 
{value: 'foo'}
),
});
Object.defineProperty(obj, 'bar', {
get: () => barValue,
set: newValue => barValue = Object.defineProperty(
new String(newValue), 
'descriminator', 
{value: 'bar'}
),
});
obj.foo = 1;
obj.bar = 'bar';    
function test(val) {
if(val.descriminator === 'foo')
console.log('meta:foo', 'value:'+val);
else if(val.descriminator === 'bar')
console.log('meta:bar', 'value:'+val);
}
test(obj.foo);
test(obj.bar);

在大多数情况下,基元的包装类可以与基元互换使用。它们的.valueOf()将返回原始基元值,因此它们可以用于表达式,编译器将提取它们的基元值来进行计算。然而,您应该注意的副作用之一是typeof obj.foo将是object而不是number

您可以使用智能设置器来扩展此方法,智能设置器将检测要设置的值的类型并使用正确的包装构造函数。或者只定义自己的通用包装类,比如

class Wrapper {
#rawValue = undefined
constructor(rawValue) {
this.#rawValue = rawValue; 
}
[Symbol.toPrimitive](hint) {
return {
'string': String(this.#rawValue),
'number': Number(this.#rawValue),
'default': this.#rawValue
}[hint]
}
valueOf() { return this.#rawValue; }
}

let obj = new class {
#fooValue = undefined
#barValue = undefined
get foo() { return this.#fooValue; }
set foo(val) { this.#fooValue = new Wrapper(val); }
get bar() { return this.#barValue; }
set bar(val) { this.#barValue = new Wrapper(val); }
};
function test(val) {
if(val.descriminator === 'foo')
console.log('foo '+val);
else if(val.descriminator === 'bar')
console.log('bar '+val);
}
obj.foo = 1;
obj.foo.descriminator = 'foo';
obj.bar = 1;    
obj.bar.descriminator = 'bar';

test(obj.foo);
test(obj.bar);

最新更新