Atravesando la relación entre padres y hijos entre objetos: Parte 2 -- javascript campo con object-oriented campo con tree campo con ecmascript-6 camp codereview Relacionados El problema

Traversing the Parent-child relationship between objects: Part 2


1
vote

problema

Español

Este código se basa en el código en la iteración anterior . Todas las siguientes pruebas deben funcionar:

  let myTree = Tree.from({a:{b:{k:2}}}); myTree.a.b;                         // { k: 2 } myTree.a === myTree.a.b.__parent__; // true // adding properties myTree.c = {d:{l:5}};               // {d: {l: 5}} myTree.c.__parent__ === myTree;     // true myTree.a.b.__parent__ = myTree.c;   // {d: {l: 5}} myTree.c.b.k;                       // 2 myTree.a.b;                         // undefined // moving branches to other trees. let anotherTree = new Tree(); myTree.a.l = 3; myTree.a.l;     // 3 myTree.a.__parent__ = anotherTree; anotherTree.a;  // {l: 3} myTree.a;       // undefined anotherTree.a.__parent__ === anotherTree; // true   

También introduce la clase Fragment , que se comporta como un árbol y una sucursal en el hecho de que se puede asignar a ambos y tener propiedades infantiles.

Sobre un objeto o primitivo que se está asignando, se convierte inmediatamente a una rama o una hoja, respectivamente.

Todas las hojas se heredan dinámicamente.

Código:

  const [Tree, Fragment] = ( function(){     const LeafCache = Object.create( null ),      genLeaf = ( CLASS ) => {        return LeafCache[CLASS.name] || LeafCache[CLASS.name] = eval( `class Leaf extends ${CLASS.name} {     constructor(val) {         super(val);     } }; Leaf.prototype.__isleaf__ = true; Leaf` );         },      TARGET = Symbol( '__target__' ),         HANDLER = Symbol( '__handler__' ),         PROXY = Symbol( '__proxy__' ),         ISTREE = Symbol( '__istree__' ),         { assign, defineProperty, entries, setPrototypeOf } = Object,         { hasOwnProperty } = {},         convert = ( obj ) => {           let res = obj instanceof Branch ? obj : new Branch( obj );           for( const key in obj ) {             const value = obj[key];             if( hasOwnProperty.call( obj, key ) ) {               if( '__isfragment__' !== key ) {                 if( typeof value === 'object' ) {                     res[key] = convert( value );                     Object.defineProperty( res[key], '__parent__', {                       value: res[PROXY],                       configurable: false,                       protected: false                     } );                 } else {                   let val;                   res[key] = new Proxy( val = new ( genLeaf( value.constructor ) )( value ), genHandler( val ) );                 }               }             }           }           return res;         },         getKey = ( obj, val ) => {           for( const key in obj ) {             const value = obj[key];             if( value[TARGET] === val ) {               return key;             }           }         };     let genHandler = ( _target ) => {         return ( function(){             let res;             const _raw = __raw__.bind( _target ),                 _keys = {                     '__raw__': _raw,                     [ISTREE]: true,                     [TARGET]: _target,                     get [PROXY]() {                         return res.proxy;                     },                     get [HANDLER]() {                         return res;                     }                 };             res = {                 set: ( target, prop, value ) => {                     if( prop === '__parent__' ) {                         if( _keys[PROXY] instanceof Fragment ) {                             throw TypeError( 'Cannot set __parent__ on fragments.' );                         } else if( typeof value === 'object' && value[ISTREE] ) {                             const key = getKey( target.__parent__, target );                             if( target.__parent__[key] ) {                                 delete target.__parent__[key];                             }                             value[key] = target;                             return value;                         } else {                             throw TypeError( 'Cannot assign __parent__ to a non-tree value' );                         }                     }                     if( typeof value === 'object' && value.constructor.name !== 'Leaf' ) {                         value = convert( value );                         if( value[PROXY] instanceof Tree ) {                             throw TypeError( 'Cannot have a tree as a child of another tree.' );                         }                         value = convert( value );                         defineProperty( value, '__parent__', {                             value: _keys[PROXY],                             configurable: false,                             writable: true                         } );                     } else if ( typeof value !== 'object' ) {                         let val;                         value = new Proxy( val = new ( genLeaf( value.constructor ) )( value ), genHandler( val ) );                     }                     target[prop] = value;                     return value;                 },                 get: ( target, prop ) => {                    if( prop === 'toJSON' ) {                      return _raw;                    }                    if( [HANDLER, PROXY, '__raw__', ISTREE, TARGET].includes( prop ) ) {                      return _keys[prop];                    }                     return target[prop];                 }             };             return res;         } )();     };     /**      * Get the raw value of the tree, without all that proxy stuff.      * @returns {Object} The raw object. Please not that objects will not be the same instances.      * @memberof Tree#      */     function __raw__() {         let res = setPrototypeOf( {}, this.__proto__ );         for( const key in this ) {             if( key.slice( 0, 2 ) === key.slice( -2 ) && key.slice( -2 ) === '__' ) {             continue;         } else {                 const value = this[key];                 if( hasOwnProperty.call( this, key ) ) {                     res[key] = typeof value === 'object' ? __raw__.call( value[TARGET] ) : value;                 }             }         }         return res;     }     /**      * A class that enables navigation from child properties to parent. WIP - currently figuring out how to make new properties.      * For all purposes this functions as intended, but it doesn't print well in the console. It even perserves prototypes.      * @property {(Branch|Leaf)} * Properties.      */     class Tree {         /**          * Constructs a new Tree instance.          * @constructs Tree          */         constructor() {             return Tree.from( {} );         }         /**          * Attempt to create a tree from an existing object.          * @param {Object} obj The object to convert to a tree.          * @throws {TypeError} You probably passed it a primitive.          * @returns {Tree} The resulting tree.          */         static from( obj ) {             const self = {},                 res = new Proxy( setPrototypeOf( self, obj.__proto__ ), genHandler( self ) );             defineProperty( res[HANDLER], 'proxy', {                 value: res,                 configurable: false,                 protected: true             } );             if( typeof obj !== 'object' ) {                 throw TypeError( 'Tree expects an object' );             } else {                 for( const key in obj ) {                     const value = obj[key];                     let val;                     res[key] = typeof value === 'object' ? convert( value ) : new Proxy( val = new ( genLeaf( value.constructor ) )( value ), genHandler( val ) );                     console.log( res[key][TARGET] );                 }             }             defineProperty( res, '__istree__', {                 value: true,                 configurable: false,                 protected: true             } );             return res;         }         static [Symbol.hasInstance]( obj ) {           return obj[ISTREE] && obj.__istree__ || false;         }     }     /**      * A class that behaves similar to a tree and similar to a branch. It can be added to a tree like a branch.      * @class      */     class Fragment {         /**          * Construct a new fragment.          * @constructs Fragment          */         constructor() {             return Fragment.from( {} );         }         /**          * Attempt to make a fragment from an existing object.          * @param {Object} obj The object to use.          * @returns {Fragment} The resulting fragment.          */         static from( obj ) {             const self = {},                 res = new Proxy( setPrototypeOf( self, obj.__proto__ ), genHandler( self ) );             defineProperty( res[HANDLER], 'proxy', {              value: res,              configurable: false,              protected: true             } );             if( typeof obj !== 'object' ) {                 throw TypeError( 'Tree expects an object' );             } else {                   for( let key in obj ) {                     let value = obj[key];                     res[key] = typeof value === 'object' ? convert( value ) : value;                   }             }             defineProperty( res, '__isfragment__', {                 value: true,                 configurable: false,                 protected: true             } );             return res;         }         static [Symbol.hasInstance]( obj ) {           return obj[ISTREE] && obj.__isfragment__ || false;         }     }     class Branch {         constructor( obj ) {           let self = {},                 res = new Proxy( setPrototypeOf( self, obj.__proto__ ), genHandler( self ) );             defineProperty( res[HANDLER], 'proxy', {                 value: res,                 configurable: false,                 protected: true             } );             defineProperty( res, '__isbranch__', {                 value: true,                 configurable: false,                 protected: true             } );             return res;         }         static [Symbol.hasInstance]( obj ) {           return obj[ISTREE] && obj.__isbranch__ || false;         }     }     return [Tree, Fragment]; } )(); /**  * A class that shows that an item is a terminal node. Parent properties cannot be accessed by this node.  * @alias Brance  * @class Leaf  * @extends Primitive  */ /**  * A class that simply shows that it is an inner object of a Tree.  * @alias Branch  * @class Branch  * @property {(Tree|Branch)} __parent__ The parent element. This can be changed to move the object to another tree or branch.  */   

Esta es una revisión iterativa. Iteración anterior: Atravesando la relación padre-hijo entre objetos

Original en ingles

This code builds on the code in the previous iteration. All of the following tests should work:

let myTree = Tree.from({a:{b:{k:2}}}); myTree.a.b;                         // { k: 2 } myTree.a === myTree.a.b.__parent__; // true // adding properties myTree.c = {d:{l:5}};               // {d: {l: 5}} myTree.c.__parent__ === myTree;     // true myTree.a.b.__parent__ = myTree.c;   // {d: {l: 5}} myTree.c.b.k;                       // 2 myTree.a.b;                         // undefined // moving branches to other trees. let anotherTree = new Tree(); myTree.a.l = 3; myTree.a.l;     // 3 myTree.a.__parent__ = anotherTree; anotherTree.a;  // {l: 3} myTree.a;       // undefined anotherTree.a.__parent__ === anotherTree; // true 

It also introduces the Fragment class, which behaves both like a tree and a branch in the fact that it can be assigned to both and have child properties.

Upon an object or primitive being assigned, it is immediately converted to a branch or a leaf, respectively.

All leaves are dynamically inherited.

Code:

const [Tree, Fragment] = ( function(){     const LeafCache = Object.create( null ),      genLeaf = ( CLASS ) => {        return LeafCache[CLASS.name] || LeafCache[CLASS.name] = eval( `class Leaf extends ${CLASS.name} {     constructor(val) {         super(val);     } }; Leaf.prototype.__isleaf__ = true; Leaf` );         },      TARGET = Symbol( '__target__' ),         HANDLER = Symbol( '__handler__' ),         PROXY = Symbol( '__proxy__' ),         ISTREE = Symbol( '__istree__' ),         { assign, defineProperty, entries, setPrototypeOf } = Object,         { hasOwnProperty } = {},         convert = ( obj ) => {           let res = obj instanceof Branch ? obj : new Branch( obj );           for( const key in obj ) {             const value = obj[key];             if( hasOwnProperty.call( obj, key ) ) {               if( '__isfragment__' !== key ) {                 if( typeof value === 'object' ) {                     res[key] = convert( value );                     Object.defineProperty( res[key], '__parent__', {                       value: res[PROXY],                       configurable: false,                       protected: false                     } );                 } else {                   let val;                   res[key] = new Proxy( val = new ( genLeaf( value.constructor ) )( value ), genHandler( val ) );                 }               }             }           }           return res;         },         getKey = ( obj, val ) => {           for( const key in obj ) {             const value = obj[key];             if( value[TARGET] === val ) {               return key;             }           }         };     let genHandler = ( _target ) => {         return ( function(){             let res;             const _raw = __raw__.bind( _target ),                 _keys = {                     '__raw__': _raw,                     [ISTREE]: true,                     [TARGET]: _target,                     get [PROXY]() {                         return res.proxy;                     },                     get [HANDLER]() {                         return res;                     }                 };             res = {                 set: ( target, prop, value ) => {                     if( prop === '__parent__' ) {                         if( _keys[PROXY] instanceof Fragment ) {                             throw TypeError( 'Cannot set __parent__ on fragments.' );                         } else if( typeof value === 'object' && value[ISTREE] ) {                             const key = getKey( target.__parent__, target );                             if( target.__parent__[key] ) {                                 delete target.__parent__[key];                             }                             value[key] = target;                             return value;                         } else {                             throw TypeError( 'Cannot assign __parent__ to a non-tree value' );                         }                     }                     if( typeof value === 'object' && value.constructor.name !== 'Leaf' ) {                         value = convert( value );                         if( value[PROXY] instanceof Tree ) {                             throw TypeError( 'Cannot have a tree as a child of another tree.' );                         }                         value = convert( value );                         defineProperty( value, '__parent__', {                             value: _keys[PROXY],                             configurable: false,                             writable: true                         } );                     } else if ( typeof value !== 'object' ) {                         let val;                         value = new Proxy( val = new ( genLeaf( value.constructor ) )( value ), genHandler( val ) );                     }                     target[prop] = value;                     return value;                 },                 get: ( target, prop ) => {                    if( prop === 'toJSON' ) {                      return _raw;                    }                    if( [HANDLER, PROXY, '__raw__', ISTREE, TARGET].includes( prop ) ) {                      return _keys[prop];                    }                     return target[prop];                 }             };             return res;         } )();     };     /**      * Get the raw value of the tree, without all that proxy stuff.      * @returns {Object} The raw object. Please not that objects will not be the same instances.      * @memberof Tree#      */     function __raw__() {         let res = setPrototypeOf( {}, this.__proto__ );         for( const key in this ) {             if( key.slice( 0, 2 ) === key.slice( -2 ) && key.slice( -2 ) === '__' ) {             continue;         } else {                 const value = this[key];                 if( hasOwnProperty.call( this, key ) ) {                     res[key] = typeof value === 'object' ? __raw__.call( value[TARGET] ) : value;                 }             }         }         return res;     }     /**      * A class that enables navigation from child properties to parent. WIP - currently figuring out how to make new properties.      * For all purposes this functions as intended, but it doesn't print well in the console. It even perserves prototypes.      * @property {(Branch|Leaf)} * Properties.      */     class Tree {         /**          * Constructs a new Tree instance.          * @constructs Tree          */         constructor() {             return Tree.from( {} );         }         /**          * Attempt to create a tree from an existing object.          * @param {Object} obj The object to convert to a tree.          * @throws {TypeError} You probably passed it a primitive.          * @returns {Tree} The resulting tree.          */         static from( obj ) {             const self = {},                 res = new Proxy( setPrototypeOf( self, obj.__proto__ ), genHandler( self ) );             defineProperty( res[HANDLER], 'proxy', {                 value: res,                 configurable: false,                 protected: true             } );             if( typeof obj !== 'object' ) {                 throw TypeError( 'Tree expects an object' );             } else {                 for( const key in obj ) {                     const value = obj[key];                     let val;                     res[key] = typeof value === 'object' ? convert( value ) : new Proxy( val = new ( genLeaf( value.constructor ) )( value ), genHandler( val ) );                     console.log( res[key][TARGET] );                 }             }             defineProperty( res, '__istree__', {                 value: true,                 configurable: false,                 protected: true             } );             return res;         }         static [Symbol.hasInstance]( obj ) {           return obj[ISTREE] && obj.__istree__ || false;         }     }     /**      * A class that behaves similar to a tree and similar to a branch. It can be added to a tree like a branch.      * @class      */     class Fragment {         /**          * Construct a new fragment.          * @constructs Fragment          */         constructor() {             return Fragment.from( {} );         }         /**          * Attempt to make a fragment from an existing object.          * @param {Object} obj The object to use.          * @returns {Fragment} The resulting fragment.          */         static from( obj ) {             const self = {},                 res = new Proxy( setPrototypeOf( self, obj.__proto__ ), genHandler( self ) );             defineProperty( res[HANDLER], 'proxy', {              value: res,              configurable: false,              protected: true             } );             if( typeof obj !== 'object' ) {                 throw TypeError( 'Tree expects an object' );             } else {                   for( let key in obj ) {                     let value = obj[key];                     res[key] = typeof value === 'object' ? convert( value ) : value;                   }             }             defineProperty( res, '__isfragment__', {                 value: true,                 configurable: false,                 protected: true             } );             return res;         }         static [Symbol.hasInstance]( obj ) {           return obj[ISTREE] && obj.__isfragment__ || false;         }     }     class Branch {         constructor( obj ) {           let self = {},                 res = new Proxy( setPrototypeOf( self, obj.__proto__ ), genHandler( self ) );             defineProperty( res[HANDLER], 'proxy', {                 value: res,                 configurable: false,                 protected: true             } );             defineProperty( res, '__isbranch__', {                 value: true,                 configurable: false,                 protected: true             } );             return res;         }         static [Symbol.hasInstance]( obj ) {           return obj[ISTREE] && obj.__isbranch__ || false;         }     }     return [Tree, Fragment]; } )(); /**  * A class that shows that an item is a terminal node. Parent properties cannot be accessed by this node.  * @alias Brance  * @class Leaf  * @extends Primitive  */ /**  * A class that simply shows that it is an inner object of a Tree.  * @alias Branch  * @class Branch  * @property {(Tree|Branch)} __parent__ The parent element. This can be changed to move the object to another tree or branch.  */ 

This is an iterative review. Previous iteration: Traversing the parent-child relationship between objects

           

Lista de respuestas

1
 
vote
vote
La mejor respuesta
 

Errores

Intenté esto tanto en JSFiddle (con y sin JS 1.7 y Babel), así como los nodejs, pero vieron algunos errores. El primero fue:

    return LeafCache[CLASS.name] || LeafCache[CLASS.name] = eval( `class Leaf extends ${CLASS.name} {           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^   

ReferenceError: lado izquierdo no válido en la asignación

Entonces, intenté envolver el segundo condicional entre paréntesis y que parecía pasar por ese error:

  return LeafCache[CLASS.name] || (LeafCache[CLASS.name] = eval( `class Leaf extends ${CLASS.name} {     constructor(val) {         super(val);     } }; Leaf.prototype.__isleaf__ = true; Leaf` ));   

Pero luego veo otro error:

                 Object.defineProperty( res[key], '__parent__', {                        ^   

TypeError: No se puede redefinir la propiedad: __Parent __

Otros comentarios

Aparte de esos errores que no veo mucho que cambiaría, quizás algunos lugares donde las variables se pueden declarar usando NSArray *arraysWith9Letters = [letters arraysBySplittingWithMaximumSize:9]; 0 en lugar de NSArray *arraysWith9Letters = [letters arraysBySplittingWithMaximumSize:9]; 1 - e.g. NSArray *arraysWith9Letters = [letters arraysBySplittingWithMaximumSize:9]; 2 , NSArray *arraysWith9Letters = [letters arraysBySplittingWithMaximumSize:9]; 3 Dentro de la función NSArray *arraysWith9Letters = [letters arraysBySplittingWithMaximumSize:9]; 4 , para evitar la reconsideración involuntaria ...

Parece que algunos de este Código parecen ser idénticos a los cambios agregados en revisión 7 a Su post anterior Pero con el NSArray *arraysWith9Letters = [letters arraysBySplittingWithMaximumSize:9]; 5 clase agregada. Fue que en respuesta a la sugerencia al final de la respuesta de RM-?

Pero aún así el código podría simplificarse un poco, dividirse en funciones de utilidad pequeñas separadas, organizadas en una forma de ser fácil de leer de arriba a abajo, y tal. 1

1 https://codereview.stackexchange.com/a/200942/ 120114

 

Errors

I tried this both in jsFiddle (with and without JS 1.7 and Babel) as well as NodeJS but saw a few errors. The first one was:

  return LeafCache[CLASS.name] || LeafCache[CLASS.name] = eval( `class Leaf extends ${CLASS.name} {           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 

ReferenceError: Invalid left-hand side in assignment

So then I tried wrapping the second conditional in parentheses and that appeared to get me past that error:

return LeafCache[CLASS.name] || (LeafCache[CLASS.name] = eval( `class Leaf extends ${CLASS.name} {     constructor(val) {         super(val);     } }; Leaf.prototype.__isleaf__ = true; Leaf` )); 

But then I see another error:

               Object.defineProperty( res[key], '__parent__', {                        ^ 

TypeError: Cannot redefine property: __parent__

Other feedback

Other than those errors I don't see much I would change - maybe a few places where variables can be declared using const instead of let - e.g. genHandler, res within the function __raw__, so as to avoid unintentional re-assignment...

Some of this code appears to be identical to the changes added in revision 7 to your previous post but with the Leaf class added. Was that in response to the suggestion at the end of rm-'s answer?

but still the code could be simplified a little bit, split into separate small utility functions, organized in a way to be easy to read from top to bottom, and such.1

1https://codereview.stackexchange.com/a/200942/120114

 
 
 
 

Relacionados problema

2  Función para filtrar comentarios y calificaciones, con soporte de paginación  ( Function to filter comments and ratings with pagination support ) 
Tengo un componente web que está haciendo una lista de elementos y tengo este método para filtrar estos elementos basados ​​en un objeto 998877666554433320 ...

0  Serialización de datos JSON [cerrada]  ( Json data serialization ) 
cerrado . Esta pregunta necesita detalles o claridad . Actualmente no está aceptando respuestas. ...

10  "Stardust" Simulador 2D Gravity - Seguimiento 1: Los planetas  ( Stardust 2d gravity simulator follow up 1 the planets ) 
Este es un seguimiento para el juego de gravedad del simulador 2D pregunta. Desde entonces, he desarrollado mis habilidades de JavaScript, y ahora esto...

13  Servidor de chat básico y cliente usando WebSocket  ( Basic chat server and client using websocket ) 
Basado en nodo.js en acción (Publicaciones de Manning) Capítulo 2 "Construyendo una aplicación de chat de múltiples habitaciones" , he tomado el modelo que...

1  Iterador de secuencia numérico bidireccional (punto flotante)  ( Bidirectional numeric floating point sequence iterator ) 
Quería un iterador limpio que siempre me dará una secuencia limpia de step Números espaciados. Esto es lo que acompañé, pero soy un poco escéptico que cubrí...

6  Vanilla ES6 TODO / LISTA DE TAREA CON LOCALSTORAGE  ( Vanilla es6 todo task list with localstorage ) 
Estoy haciendo una aplicación simple de lista de TODO con ES6. Estoy buscando algunos comentarios sobre el código, siento que hay alguna mejora posible. Tal v...

3  Comprobando si los paréntesis están equilibrados  ( Checking if parentheses are balanced ) 
Este script comprueba si los paréntesis están equilibrados. Me pregunto si hay algo que puede mejorarse aquí, incluyendo las características ES6. function ...

7  Función recursiva para obtener propiedades únicas  ( Recursive function to get unique properties ) 
Solo quería compartir mi código JavaScript recién creado. Este código es responsable de pasar solo los elementos únicos para una matriz dada. Este es bastante...

7  Aplicar acción arbitraria durante la función de generador recursivo  ( Apply arbitrary action during recursive generator function ) 
Tengo esta función de generador recursivo. Traerá un objeto en busca de "hojas", cualquier tecla que no apunte a sub-objetos. Luego aplica una de las dos ac...

8  Árbol fractal orientado a objetos  ( Object oriented fractal tree ) 
Hice un árbol fractal orientado a objetos en JavaScript utilizando la biblioteca P5, consta de tres archivos: fraternal tree.js sucursal.js flower.js ...




© 2022 respuesta.top Reservados todos los derechos. Centro de preguntas y respuestas reservados todos los derechos