javascript-“代理”将“ this [toString]”与“ this [Symbol.toStringTag]”混淆

它仅在#toString中发生,并且仅当我(尝试)通过类似于missingMethod的陷阱访问它时才发生.

我有一个名为createIterface的工厂,该工厂使用大量方法返回对象的代理.在这些方法中,我同时拥有#toString()和#id(). #id返回一个具有与调用者相同属性的接口,并且工作正常; #toString应该将我的接口转换为String,但是失败.
所有接口的方法-包括#id和#toString-都在#Symbol.for(“ __ methods”)属性中.我这样做是为了调试目的:

const __methods = Symbol.for("__methods");

const missingMethod = ({
    get: (obj, prop) => Reflect.has(obj, prop)
        ? Reflect.get(obj, prop)
        : Reflect.has(obj[__methods], prop)
            ? Reflect.get(obj[__methods], prop)
            : console.log(`No #${prop} property exists.`)
});

const createInterface = (...props) => new Proxy({
    ...props,
    [__methods]: {
        id: () => createInterface (...props),
        toString: () => `Interface(${ props.toString() })`
    }
}, missingMethod);

const interface = createInterface(0, 1, 2);
interface.id(); //works
interface.toString(); //error: Cannot convert a Symbol value to a string

引发的错误表明它无法(隐式)将Symbol转换为String(这是正确的).事实是,#toString不是符号.但是,有一个著名的符号#toStringTag定义了Object#toString()行为.当我用其他方法实现它时,我的#toString()被忽略,并且接口返回'[object Object]’:

// see code above
const createInterface = (...props) => new Proxy({
    ...props,
    [__methods]: {
        id: () => createInterface (...props),
        toString: () => `Interface(${ props.toString() })`,
        [Symbol.toStringTag]: () => "Interface"
    }
}, missingMethod);

const interface = createInterface(0, 1, 2);
interface.id(); //works
interface.toString(); //bug: '[object Object]'

如果我在__methods之外对方法进行编码,那么一切都可以正常工作:

// see code above
const createInterface = (...props) => new Proxy({
    ...props,
    id: () => createInterface (...props),
    toString: () => `Interface(${ props.toString() })`
}, missingMethod);

const interface = createInterface(0, 1, 2);
const copycat = interface.id();
interface.toString() === copycat.toString(); //true

除了一些奇怪的浏览器错误(我正在运行最新的Chrome,在撰写本文时,它是第71.0.3578.98版),我不知道为什么会这样或如何解决.

有人可以帮忙吗?

解决方法:

问题是访问interface.toString首先要通过

get: (obj, prop) => Reflect.has(obj, prop)
    ? Reflect.get(obj, prop)
    : Reflect.has(obj[__methods], prop)
        ...

您期望interface.toString在这里进入三进制并到达_methods,但是Reflect.has(obj,’toString’)由于Object.prototype.toString的计算结果将为true.然后,在对象上调用该函数将再次通过代理的getter操作,以查找要调用的#toStringTag.吸气剂经过所有三元分析,一无所获,因此抛出异常

console.log(`No #${prop} property exists.`)

因为prop是一个符号,不能被串联.

一种可能性是使用不继承自Object.prototype的对象:

const obj = Object.create(null);
const createInterface = (...props) => new Proxy(
  Object.assign(obj, {
    ...props,
    [__methods]: {
      id: () => createInterface (...props),
      toString: () => `Interface(${ props.toString() })`
    }
  })
  , missingMethod
);
const __methods = Symbol.for("__methods");

const missingMethod = ({
    get: (obj, prop) => Reflect.has(obj, prop)
        ? Reflect.get(obj, prop)
        : Reflect.has(obj[__methods], prop)
            ? Reflect.get(obj[__methods], prop)
            : console.log(`No #${prop} property exists.`)
});

    const obj = Object.create(null);
    const createInterface = (...props) => new Proxy(
      Object.assign(obj, {
        ...props,
        [__methods]: {
          id: () => createInterface (...props),
          toString: () => `Interface(${ props.toString() })`
        }
      })
      , missingMethod
    );

const interface = createInterface(0, 1, 2);
interface.id(); //works
console.log(interface.toString());

另一种可能是让吸气剂执行hasOwnProperty检查,而不是Reflect.has检查(Reflect.has与in基本上相同,并且’toString’将在几乎所有对象中):

get: (obj, prop) => obj.hasOwnProperty(prop)
const __methods = Symbol.for("__methods");

const missingMethod = ({
    get: (obj, prop) => obj.hasOwnProperty(prop)
        ? Reflect.get(obj, prop)
        : Reflect.has(obj[__methods], prop)
            ? Reflect.get(obj[__methods], prop)
            : console.log(`No #${prop} property exists.`)
});
const createInterface = (...props) => new Proxy({
    ...props,
    [__methods]: {
        id: () => createInterface (...props),
        toString: () => `Interface(${ props.toString() })`,
    }
}, missingMethod);

const interface = createInterface(0, 1, 2);
interface.id(); //works
console.log(interface.toString());

第三种可能性是确保由初始Reflect.has找到的属性不是来自Object.prototype方法:

get: (obj, prop) => Reflect.has(obj, prop) && Reflect.get(obj, prop) !== Object.prototype[prop]
const __methods = Symbol.for("__methods");

const missingMethod = ({
    get: (obj, prop) => Reflect.has(obj, prop) && Reflect.get(obj, prop) !== Object.prototype[prop]
        ? Reflect.get(obj, prop)
        : Reflect.has(obj[__methods], prop)
            ? Reflect.get(obj[__methods], prop)
            : console.log(`No #${prop} property exists.`)
});

const createInterface = (...props) => new Proxy({
    ...props,
    [__methods]: {
        id: () => createInterface (...props),
        toString: () => `Interface(${ props.toString() })`
    }
}, missingMethod);

const interface = createInterface(0, 1, 2);
interface.id(); //works
console.log(interface.toString());
上一篇:C与静态lib中的std :: string相关的未定义符号


下一篇:手动指定特定链接符号的重新映射