Пример:
let foo = {bar: 'baz', method() { this.bar = 'baz2' }}
let fooProxy = new Proxy(foo, {set(target, key, val) { console.info('set trap triggered!') }})
fooProxy.bar = 'any value' // as expected: set trap triggered!
foo.method() // trap not triggered
Почему это происходит? Как можно принудительно запустить ловушку даже изнутри целевого объекта?
Отредактируйте, в основном, чтобы объяснить это @Bergi:
Моя основная цель - перехватить любые изменения в объекте foo, поэтому я могу установить свойство, например. foo. изменяется на true. Кроме того, я хочу перехватывать изменения свойств foo, которые имеют тип массива / объекта. Вы знаете, если я устанавливаю свойство foo, все в порядке, но когда я, например, нажмите на тот, который является массивом, тогда прокси не сможет его перехватить. Поэтому мне также нужно преобразовать свойства массива / объекта в прокси (я назвал их ArrayProxy и ObjectProxy).
Вот мой код (машинописный текст):
// Category.ts
class Category extends Model {
title: string = ''
products: Product[] = []
}
// Model.ts
abstract class Model extends BaseModel {
constructor() {
return new Proxy(this, {
set (target, key, val) {
if (Array.isArray(val) {
target[key] = new ArrayProxy(val) // I WANT all array properties to be ArrayProxy but the problem (see below) not let me do that
}
}
})
}
}
// BaseModel.ts
abstract class BaseModel {
constructor(attributes) {
this.setAttributes(attributes)
}
setAttributes(attributes) {
Object.keys(attributes).forEach((key) => {
this[key] = attributes[key] // THE PROBLEM
})
}
}
Я удалил код, который не имеет значения (например, аналогичный случай для свойств объекта и ObjectProxy).
Я буду очень признателен, если есть более элегантный способ сделать то, что я сделал.
Проблема в том, что метод () вызывается в конструкторе целевого объекта (foo).
Что ж, конструктор для обычного объекта ничего не знает о прокси (и не должен). Что такое актуальная проблема, который вы пытаетесь решить с помощью прокси, и каков ваш настоящий код?
@Phil: Вы не должны редактировать чужой код. Я не знаю, как это изменение было одобрено. Добавление точек с запятой и использование const навязывают кому-то свой личный стиль.
@Bergi, моя основная цель - перехватить любые изменения в объекте foo, поэтому я могу установить свойство, например. foo. изменяется на true. Кроме того, я хочу перехватывать изменения свойств foo, которые имеют тип массива / объекта. Вы знаете, если я устанавливаю свойство foo, все в порядке, но когда я, например, нажмите на тот, который является массивом, тогда прокси не сможет его перехватить. Поэтому мне также нужно преобразовать свойства массива / объекта в прокси (я назвал их ArrayProxy и ObjectProxy). Я вижу, что это становится трудным для понимания, поэтому я продолжу в этом посте.
@NurbolAlpysbayev Что ж, вы можете перехватить изменения объекта только после того, как вы обернули его прокси, что произойдет после создания объекта. И нет, перехват изменений во вложенных массивах / объектах - это совершенно другая тема (см., Например, здесь и во многих других сообщениях), пожалуйста, не мешайте обсуждению этого.
@Bergi, извини, может уже поздно, я уже сорвал это с рельсов. Я подробно изложил в сообщении, как вы просили, просто чтобы показать актуальную проблему в моем приложении. Однако у меня уже есть несколько решений, спасибо вам всем!
@NurbolAlpysbayev Вы должны просто поменять местами прокси между Model и BaseModel. Как вы, наверное, заметили с Category, он отлично работает в вещах, расширяющих Model (где конструктор возвращает прокси).
@NurbolAlpysbayev Также вы можете использовать ловушку get для перехвата любого, кто обращается к объекту / свойству, имеющему значение массива.
@Bergi Woah, ваше понимание вещей феноменально :) почти так же хорошо, как и мои умения объяснять плохие = D Я занимаюсь рефакторингом кода, знаю, это самое ядро приложения, поэтому я очень ценю ваше влияние на него! Свап штука очень интересная, экспериментирую с ней ...
@NurbolAlpysbayev Или вместо того, чтобы менять местами, просто объедините их в один класс. Хотя я не уверен, могли ли быть другие причины для их разделения.



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


В foo.method вы не устанавливаете свойство через прокси-объект, поэтому очевидно, что ловушка не срабатывает. Прокси-сервер не изменяет исходный объект.
Ловушка не существует на исходном объекте. Если бы это было возможно, вам не понадобился бы отдельный проксируемый объект. Идея состоит в том, чтобы использовать прокси вместо оригинала.
Одна из возможностей - унаследовать ваш исходный объект от проксируемого объекта. Это может сработать, в зависимости от того, что вам действительно нужно сделать.
let fooProxy = new Proxy({}, {
set(target, key, val) { console.info('set trap triggered!') }
})
let foo = Object.create(fooProxy, {
method: {value: function() { this.bar = 'baz2' }}
})
fooProxy.bar = 'any value'
foo.method()Спасибо! Ваше решение кажется немного сложнее, чем принятое решение .call () (работает для меня), по крайней мере, в моей индивидуальной ситуации. В любом случае попробую прямо сейчас.
@NurbolAlpysbayev: На самом деле это просто старое стандартное прототипное наследование, но прототип - это ваш экземпляр Proxy. Я использовал Object.create для настройки наследования, но вы можете использовать старый способ с .prototype конструктора или новый синтаксис class.
Давным-давно я пришел из мира PHP, и у меня всегда были проблемы с прототипным наследованием. Синтаксис класса для меня мысленно проще, но теперь, спустя годы, конечно, только немного проще, но все же проще. Так что это только я, наверное :)
@NurbolAlpysbayev: Простота прототипного наследования была скрыта различными неуклюжими способами, которые JS предоставляет для его настройки. Это действительно просто «... посмотрите на объект A для этого свойства, и если он не найден, но A наследуется от B, тогда ищите свойство на B». Таким образом, цепочка прототипов - это просто связанный список объектов, который автоматически просматривается, когда искомое свойство не найдено.
"был скрыт различными неуклюжими способами, которые JS предоставляет для его настройки" - Да, это было правдой до появления es6 :)
Установленная ловушка не срабатывает, потому что this, к которому вы обращаетесь в method(), является не прокси-сервером, а исходным объектом foo. Прокси-сервер не изменяет исходный объект. Вы можете убедиться в этом, проверив, что именно this находится внутри method():
let foo = {
method () {
return this === fooProxy
}
}
let fooProxy = new Proxy(foo, {})
document.write(foo.method()) //=> falseВместо этого вы можете явно установить контекст method() при вызове с помощью Function#call:
let foo = {bar: 'baz', method() { this.bar = 'baz2' }}
let fooProxy = new Proxy(foo, {set(target, key, val) { console.info('set trap triggered!') }})
fooProxy.bar = 'any value' //=> set trap triggered!
foo.method.call(fooProxy) //=> set trap triggered!Мне нужна вещь .call (). Спасибо за идею! И за разложение моей проблемы на простые части.
Конечно, вам нужно использовать
fooProxy.method(). Вы не ожидаете, чтоfooProxy.method.call(something)(который устанавливаетsomething.bar) также запустит прокси?