reflect es6 javascript proxy ecmascript-6 es6-proxy

es6 - javascript reflect



Cómo usar el proxy javascript para objetos anidados (5)

Publiqué una biblioteca en GitHub que también hace esto. También informará a una función de devolución de llamada qué modificaciones se han realizado junto con su ruta completa.

La respuesta de Michal es buena, pero crea un nuevo Proxy cada vez que se accede a un objeto anidado. Dependiendo de su uso, eso podría llevar a una sobrecarga de memoria muy grande.

Tengo este código en js bin:

var validator = { set (target, key, value) { console.log(target); console.log(key); console.log(value); if(isObject(target[key])){ } return true } } var person = { firstName: "alfred", lastName: "john", inner: { salary: 8250, Proffesion: ".NET Developer" } } var proxy = new Proxy(person, validator) proxy.inner.salary = ''foo''

si hago proxy.inner.salary = 555; No funciona.

Sin embargo, si hago proxy.firstName = "Anne" , entonces funciona muy bien.

No entiendo por qué no funciona recursivamente.

http://jsbin.com/dinerotiwe/edit?html,js,console


Puede agregar una captura de captura y devolver un nuevo proxy con validator como controlador:

var validator = { get(target, key) { if (typeof target[key] === ''object'' && target[key] !== null) { return new Proxy(target[key], validator) } else { return target[key]; } }, set (target, key, value) { console.log(target); console.log(key); console.log(value); return true } } var person = { firstName: "alfred", lastName: "john", inner: { salary: 8250, Proffesion: ".NET Developer" } } var proxy = new Proxy(person, validator) proxy.inner.salary = ''foo''


También he creado una función de tipo de biblioteca para observar actualizaciones en objetos proxy profundamente anidados (lo creé para usarlo como un modelo de datos enlazado en una sola dirección). Comparado con la biblioteca de Elliot, es un poco más fácil de entender en <100 líneas. Además, creo que la preocupación de Elliot sobre la creación de nuevos objetos Proxy es una optimización prematura, por lo que mantuve esa característica para simplificar el razonamiento acerca de la función del código.

observable-model.js

let ObservableModel = (function () { /* * observableValidation: This is a validation handler for the observable model construct. * It allows objects to be created with deeply nested object hierarchies, each of which * is a proxy implementing the observable validator. It uses markers to track the path an update to the object takes * <path> is an array of values representing the breadcrumb trail of object properties up until the final get/set action * <rootTarget> the earliest property in this <path> which contained an observers array * */ let observableValidation = { get(target, prop) { this.updateMarkers(target, prop); if (target[prop] && typeof target[prop] === ''object'') { target[prop] = new Proxy(target[prop], observableValidation); return new Proxy(target[prop], observableValidation); } else { return target[prop]; } }, set(target, prop, value) { this.updateMarkers(target, prop); // user is attempting to update an entire observable field // so maintain the observers array target[prop] = this.path.length === 1 && prop !== ''length'' ? Object.assign(value, { observers: target[prop].observers }) : value; // don''t send events on observer changes / magic length changes if(!this.path.includes(''observers'') && prop !== ''length'') { this.rootTarget.observers.forEach(o => o.onEvent(this.path, value)); } // reset the markers this.rootTarget = undefined; this.path.length = 0; return true; }, updateMarkers(target, prop) { this.path.push(prop); this.rootTarget = this.path.length === 1 && prop !== ''length'' ? target[prop] : target; }, path: [], set rootTarget(target) { if(typeof target === ''undefined'') { this._rootTarget = undefined; } else if(!this._rootTarget && target.hasOwnProperty(''observers'')) { this._rootTarget = Object.assign({}, target); } }, get rootTarget() { return this._rootTarget; } }; /* * create: Creates an object with keys governed by the fields array * The value at each key is an object with an observers array */ function create(fields) { let observableModel = {}; fields.forEach(f => observableModel[f] = { observers: [] }); return new Proxy(observableModel, observableValidation); } return {create: create}; })();

Entonces es trivial crear un modelo observable y registrar observadores:

app.js

// give the create function a list of fields to convert into observables let model = ObservableModel.create([ ''profile'', ''availableGames'' ]); // define the observer handler. it must have an onEvent function // to handle events sent by the model let profileObserver = { onEvent(field, newValue) { console.log( ''handling profile event: /n/tfield: %s/n/tnewValue: %s'', JSON.stringify(field), JSON.stringify(newValue)); } }; // register the observer on the profile field of the model model.profile.observers.push(profileObserver); // make a change to profile - the observer prints: // handling profile event: // field: ["profile"] // newValue: {"name":{"first":"jonny","last":"brooks"},"observers":[{} // ]} model.profile = {name: {first: ''jonny'', last: ''brooks''}}; // make a change to available games - no listeners are registered, so all // it does is change the model, nothing else model.availableGames[''1234''] = {players: []};

Espero que esto sea útil!


También publiqué una pequeña biblioteca en GitHub que puede interceptar y validar cambios en objetos anidados utilizando Proxies. Al igual que la biblioteca de Elliot , también guarda los Proxies generados para que no tengan que generarse cada vez.

La biblioteca de Elliot es genial, pero la forma en que informa los cambios cuando se involucran funciones no fue de mi agrado. La mayor diferencia entre el mío y el suyo es que los cambios se informan de forma atómica con mi biblioteca. Es decir, una acción desencadena exactamente un informe de cambios, y no más.


Una ligera modificación en el ejemplo de Michał Perłakowski con el beneficio de este enfoque es que el proxy anidado solo se crea una vez en lugar de cada vez que se accede a un valor.

Si la propiedad del proxy al que se accede es un objeto o una matriz, el valor de la propiedad se reemplaza por otro proxy. La propiedad isProxy en el getter se usa para detectar si el objeto al que se accede actualmente es un proxy o no. Es posible que desee cambiar el nombre de isProxy para evitar nombrar colisiones con propiedades de objetos almacenados.

Nota: el proxy anidado se define en el getter en lugar del setter, por lo que solo se crea si los datos realmente se usan en algún lugar. Esto puede o no puede adaptarse a su caso de uso.

const handler = { get(target, key) { if (key == ''isProxy'') return true; const prop = target[key]; // return if property not found if (typeof prop == ''undefined'') return; // set value as proxy if object if (!prop.isBindingProxy && typeof prop === ''object'') target[key] = new Proxy(prop, handler); return target[key]; }, set(target, key, value) { console.log(''Setting'', target, `.${key} to equal`, value); // todo : call callback target[key] = value; return true; } }; const test = { string: "data", number: 231321, object: { string: "data", number: 32434 }, array: [ 1, 2, 3, 4, 5 ], }; const proxy = new Proxy (test, handler); console.log(proxy); console.log(proxy.string); // "data" proxy.string = "Hello"; console.log(proxy.string); // "Hello" console.log(proxy.object); // { "string": "data", "number": 32434 } proxy.object.string = "World"; console.log(proxy.object.string); // "World"