Прокси-сервер Javascript и синтаксис распространения в сочетании с console.log

Итак, я играл с прокси-объектами и, пытаясь увидеть, как они сочетаются с синтаксисом распространения и деструктурированием, я наткнулся на это странное поведение:

const obj = {
  origAttr: 'hi'
}

const handler = {
  get(target, prop) {
    console.log(prop);
    return 1;
  },
  has(target, prop) {
    return true;
  },
  ownKeys(target) {
    return [...Reflect.ownKeys(target), 'a', 'b'];
  },
  getOwnPropertyDescriptor(target, key) {
    return {
      enumerable: true,
      configurable: true
    };
  }
}

const test = new Proxy(obj, handler);
const testSpread = { ...test};

console.log('Iterate test');
// Works OK, output as expected
for (const i in test) {
  console.log(i, ' -> ', test[i]);
}

console.log('Iterate testSpread');
// Also works OK, output as expected
for (const i in testSpread) {
  console.log(i, ' -> ', testSpread[i]);
}

console.log('Here comes the unexpected output from console.log:');
console.log(test); // All attributes are 'undefined'
console.log(testSpread); // This is OK for some wierd reason

Приведенный выше вывод скрипта (на узле v10.15.1):

Вот неожиданный вывод из журнала консоли:

Symbol(nodejs.util.inspect.custom)
Symbol(Symbol.toStringTag)
Symbol(Symbol.iterator)
{ origAttr: undefined, a: undefined, b: undefined }
{ origAttr: 1, a: 1, b: 1 }

Почему console.log(test); вывод показывает, что все атрибуты объекта не определены? Это может вызвать серьезную головную боль, если это произойдет при отладке чего-либо.

Это ошибка в самом узле или, возможно, в реализации console.log?


person Pärt Johanson    schedule 11.03.2019    source источник
comment
Это похоже на ошибку; Я получаю ожидаемый результат в текущей версии Chrome.   -  person Patrick Roberts    schedule 11.03.2019
comment
Как и я на Safari (Mac) 12.0.3 (все хорошо)   -  person Randy Casburn    schedule 11.03.2019
comment
Также отлично работает на FF 65.0.1 на Mac.   -  person Randy Casburn    schedule 11.03.2019
comment
источник console.log   -  person Jonas Wilms    schedule 11.03.2019
comment
Поскольку у меня нет доступа для тестирования на узле v10.15.1, не могли бы вы добавить console.log(prop) внутрь ловушки get, чтобы увидеть, какие свойства она перехватывает и сколько раз она это делает?   -  person Patrick Roberts    schedule 11.03.2019
comment
@JonasWilms Я думаю, что это связано не столько с самим console.log(), сколько с тем, как прокси-объект перехватывает свойства, к которым неявно обращаются через синтаксис распространения объекта в Node v10.15.1, по крайней мере, это кажется источником ошибки.   -  person Patrick Roberts    schedule 11.03.2019
comment
@патрик я так не думаю. Если бы это было так, testSpread выглядело бы странно. Или распространение каким-то образом уничтожает прокси.   -  person Jonas Wilms    schedule 11.03.2019
comment
Я добавил console.log(реквизит), чтобы получить. Но похоже, что это может быть ошибка в конкретной версии nodejs, позже я попробую и другие версии, тогда я буду знать, чего следует избегать в производстве при запуске кода с прокси @Patrick Roberts   -  person Pärt Johanson    schedule 11.03.2019
comment
Я бы рекомендовал вообще избегать прокси-серверов в продакшене. Любой шаблон, который требует от вас использования прокси, почти всегда имеет запах кода. Они неинтуитивны, и нередко можно увидеть подобные ошибки, приводящие к непоследовательному поведению в разных реализациях. Это не первый отчет об ошибке, который я видел на Stack Overflow для использования прокси на v8.   -  person Patrick Roberts    schedule 11.03.2019
comment
Нашел источник странного поведения и опубликовал его как ответ @Patrick Roberts   -  person Pärt Johanson    schedule 12.03.2019
comment
Этот вопрос совершенно не по теме, поскольку ответ ОП указывает, что проблема была явно вызвана ошибкой в ​​его коде и не является ошибкой/ни чем-то чрезмерно воспроизводимым вне контекста только его вопроса. Кроме того, этот вопрос мог быть создан исключительно для того, чтобы он мог принять свой собственный ответ для представителя.   -  person Hybrid web dev    schedule 25.06.2019


Ответы (2)


Хорошо, я еще немного покопался и проследил все это до Object.getOwnPropertyDescriptor, вызываемого в моем прокси-объекте для получения значений его атрибутов.

Но атрибут «значение», очевидно, не определен в моем случае, поскольку у меня есть ловушка для getOwnPropertyDescriptor, которая указывает только перечисляемые и настраиваемые атрибуты (что позволяет перебирать массив, использовать его с операторами распространения и т. д.). Поскольку не существует стандартного способа вызвать ловушку get из ловушки getOwnPropertyDescriptor, это не может быть исправлено, ИМХО. Хотя было бы интересно оказаться неправым :)

Что ж, как указал Берги в комментариях, есть стандартный способ.

Также в документах https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/handler/getOwnPropertyDescriptor#Parameters "это связано с обработчиком"

Отредактировал мой код, чтобы отразить это.

Код, демонстрирующий поведение getOwnPropertyDescriptor, приведен ниже:

const obj = {
  origAttr: 'hi'
}

const handler = {
  get(target, prop) {
    return 1;
  },
  has(target, prop) {
    return true;
  },
  ownKeys(target) {
    return [...Reflect.ownKeys(target), 'a', 'b'];
  },
  getOwnPropertyDescriptor(target, key) {
    return {
      value: this.get(target, key),
      enumerable: true,
      configurable: true
    };
  }
}

const test = new Proxy(obj, handler);
const testSpread = { ...test
};

// Defined, due to trapped getOwnPropertyDescriptor which returns a value attribute
console.log(Object.getOwnPropertyDescriptor(test, 'origAttr'))

// Defined, because it is a regular object, not a proxy with a getOwnPropertyDescriptor trap
console.log(Object.getOwnPropertyDescriptor(testSpread, 'origAttr'))

person Pärt Johanson    schedule 12.03.2019
comment
Что вы подразумеваете под Поскольку нет стандартного способа вызвать ловушку get из ловушки getOwnPropertyDescriptor? Похоже, вы ищете Reflect. Или просто this.get(target, key) может быть? - person Bergi; 13.03.2019
comment
@ Берги Ты прав! Я использую javascript в течение достаточно долгого времени, но ответы на вопросы о JavaScript здесь в течение короткого времени научили меня гораздо большему об этом языке, чем я смог узнать самостоятельно. Спасибо! - person Pärt Johanson; 13.03.2019
comment
this — это объект-обработчик, обработчики ловушек вызываются как методы. - person Bergi; 13.03.2019

объект Proxy согласно определению Proxy это не что иное, как виртуализация объекта, который вы с ним проксируете.

Поэтому сам объект Proxy имеет только атрибуты объекта, который вы проксируете, если вы попытаетесь запустить console.log(test), вы увидите, что консоль выведет Proxy {origAttr: "hi"}, но у него также будет обработчик и цель внутри, которые вы определили выше.

Когда вместо этого вы используете оператор распространения, вы создаете новый объект, который создается так же, как и путем повторения свойств вашего объекта Proxy следующим образом:

Object.keys(test) --> ["origAttr", "a", "b"], потому что это то, что вы определили в ownKeys(target) { return [...Reflect.ownKeys(target), 'a', 'b']; }.

Затем он получит доступ к test["origAttr"], затем к test["a"] и test["b"], используя прокси-функцию get, которая всегда возвращает 1.

В результате ваш объект testSpread действительно содержит эти атрибуты, а test — нет.

И когда вы запускаете console.log(testSpread) --> {origAttr: 1, a: 1, b: 1}

person gigaDIE    schedule 11.03.2019
comment
Если это объяснение верно, то почему все остальные видят ожидаемое поведение при попытке использовать код в других версиях или средах? Тот факт, что существует наблюдаемая разница между реализациями, говорит о том, что по крайней мере в одной из них есть ошибка, потому что наблюдаемое поведение прокси четко определено. - person Patrick Roberts; 12.03.2019
comment
Привет, @PatrickRoberts, спасибо за ваш отзыв. На самом деле я сообщал о поведении моей консоли Chrome и немного тестировал, как она работает локально. Рад, что им удалось понять, в чем причина в ответе выше. - person gigaDIE; 15.03.2019