Encapsulación en JavaScript

Esta será mi última publicación en la serie «JavaScript para desarrolladores de (C/)AL» de hoy. Si continuara blogueando sobre cosas casi puras de JavaScript, podrías preguntar razonablemente si esto es, de hecho, un blog de NAVEGACIÓN o uno de JavaScript. Sigue siendo NAV, y aunque las cosas sobre las que estoy a punto de escribir son puramente un concepto de JavaScript, lo encuentro muy relevante para cualquier desarrollador de complementos de control. Así que, sostén mi cerveza y aguanta conmigo por otra.

Una de las quejas que escucho a menudo sobre JavaScript es que en JavaScript no hay encapsulación. Esto es casi completamente cierto, excepto por el hecho de que es completamente falso.

¿Dónde está el problema en primer lugar, y luego cuál es la solución? Vamos a sumergirnos.

El problema

Imagine que está declarando un constructor. Imagine por un segundo que estamos en el mundo ES5 (que, desafortunadamente, tenemos que usar si queremos que nuestros complementos de control sean totalmente compatibles con todas las plataformas en las que se admiten NAV y Business Central).

Este es mi código:

Obviamente, esta muestra de 42 años, que es mi edad actual.

Pronto será mi cumpleaños, por lo que este show 43:

Ahora, mientras me encantaría ser capaz de hacer algo como esto en la vida real:

… eso no va a suceder. En un mundo decente orientado a objetos, » age » debería ser una propiedad encapsulada, y no debería poder llamar a vjeko.edad = 25 años. Los lenguajes orientados a objetos C# y «normales» tienen un concepto de encapsulación, pueden manejar esto usando campos privados, pero JavaScript no tiene un concepto de privado. Cualquier cosa definida en este dentro del constructor de objetos (o más tarde en una instancia de un objeto construido) es completamente accesible para todo el código que tiene acceso a esa instancia. En mi ejemplo anterior, cualquiera puede establecer la edad y salirse con la suya.

Se podría decir que JavaScript no tiene encapsulación. Y como dije anteriormente, estarías completamente equivocado.

La solución

Aunque es obvio que no podemos declarar nada como privado directamente, todavía hay cosas que podemos usar. Un hermoso concepto que es útil se llama cierres. Los cierres se explican en detalle en un millón de blogs, sitios de documentación y ejemplos de código en Internet, y puedes buscar en Google el bejesus de ellos a tu propio ritmo, así que no profundizaré en explicar los cierres aquí. Simplemente me pondré a aplicarlos para resolver el problema de encapsulación.

Hay al menos dos maneras de manejar esto. Primero hagamos la más obvia: una función de acceso.

Imagine el mundo sin propiedades donde no puede hacer objetos.propiedad = valor (como vjeko.edad = 25 en nuestro caso, ¡por mucho que me encantaría!). En ese mundo, tendrías funciones de getter y setter:

(ignora por un momento el hecho de que todavía estoy usando esto.edad para » encapsular «la » propiedad»)

Obviamente, podría llamarlos de esta manera:

Luego, si quería tener la edad como de solo lectura, simplemente eliminaría la función setter de setAge. Si esto.la edad era realmente privada (que no lo es), esto haría el truco para ti. El problema es que nada definido en esto es privado, es accesible para cualquiera que tenga acceso a cualquier instancia del objeto. Es lo más público posible.

Para solucionar este problema, lo primero que tenemos que hacer es mover la declaración de la función getter del prototipo a la instancia. Los miembros prototipo son los más cercanos a lo que llamaríamos estático en C#, aunque en tiempo de ejecución tienen ciertos rasgos de comportamiento tanto de los miembros estáticos como de los de instancia. Sin embargo, primero movamos el miembro lejos del prototipo y hacia la instancia:

Esto resuelve solo la primera parte del problema, el hecho de que getAge se definió en el prototipo en lugar de en la instancia. Sin embargo, con un simple cambio como este:

… resolvemos el problema por completo:

la Edad es ahora completamente encapsulado. Puede acceder a él a través de la función getter, pero no puede configurarlo directamente porque solo es accesible en el ámbito de cierre de este.Función de instancia de getAge.

Para aplicar plenamente la clase de Persona, usted necesita para mover el growOlder función del prototipo en la instancia:

Y esto funciona exactamente como quieres que funcione:

Pero, ¿por qué funciona? Funciona debido a los cierres. El parámetro de edad del constructor se capturó en el ámbito de cierre de las funciones getAge y growOlder, lo que le permite acceder a su valor desde ambas funciones, pero lo hace completamente inaccesible para cualquier otra persona, en cualquier otro lugar.

Una solución aún mejor

Se podría decir que no desea acceder a ella a través de una función getter y que necesita una sintaxis de propiedad de solo lectura completa. Quieres que tu edad sea de solo lectura y encapsulada al mismo tiempo. Gotcha! JavaScript no puede encapsular ese! Excepto que absolutamente puede.

me dirigí al Objeto.defineProperty método en mi post anterior, y si lees ese, puedes ver inmediatamente cómo se puede aplicar aquí.

Por lo tanto, definamos una propiedad de instancia de solo lectura de la clase Person:

Ahí. No me dolió. Y funciona:

Así que ahí lo tienen. Encapsulación completa en JavaScript para ayudarlo a escribir código bueno y aislado y llevar sus complementos de control a un nivel real.

Feliz escritura en Java, y espero tener más tiempo para bloguear sobre otros consejos y trucos de JavaScript interesantes y útiles para desarrolladores de (C/)AL.

Deja una respuesta

Tu dirección de correo electrónico no será publicada.