Proxy путает `this [toString]` с this [Symbol.toStringTag] ` - PullRequest
0 голосов
/ 30 декабря 2018

Это происходит только с #toString, и только когда я (пытаюсь) получить к нему доступ через missingMethod -подобный trap.

У меня есть фабрика под названием createIterface, которая возвращает Proxy объекта с большим количеством методов.Среди этих методов у меня есть и #toString(), и #id().#id возвращает interface с теми же атрибутами, что и вызывающий, и работает просто отлично;#toString должен преобразовать мой interface в строку, но это не удалось.Все методы interface, включая #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() игнорируется и interface возвращает '[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, который на момент написания этой статьи - v. 71.0.3578.98) Я понятия не имею, почему это происходит или как это исправить.

Может ли кто-нибудь помочь?

1 Ответ

0 голосов
/ 30 декабря 2018

Проблема заключается в том, что доступ к 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') оценитдо true из-за Object.prototype.toString.Затем, вызов этой функции для объекта проходит через операцию получения прокси снова , ища #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' будет in почти любым объектом):

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());
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...