informatica:programacion:cursos:programacion_avanzada_javascript:simbolos
Diferencias
Muestra las diferencias entre dos versiones de la página.
| Ambos lados, revisión anteriorRevisión previaPróxima revisión | Revisión previa | ||
| informatica:programacion:cursos:programacion_avanzada_javascript:simbolos [2024/10/16 13:02] – tempwin | informatica:programacion:cursos:programacion_avanzada_javascript:simbolos [2024/10/30 13:03] (actual) – [Variables privadas usando símbolos] tempwin | ||
|---|---|---|---|
| Línea 5: | Línea 5: | ||
| ===== Introducción ===== | ===== Introducción ===== | ||
| + | Los símbolos son un elemento nuevo en ECMAScript que **ofrece la posibilidad de crear valores únicos**. Los símbolos son objetos de un tipo básico nuevo llamado **symbol**. Esos objetos son inmutables (el valor asociado a un símbolo no puede cambiarse después de su creación) y **únicos** (no hay dos símbolos con el mismo valor). | ||
| + | |||
| + | ==== Creación de símbolos ==== | ||
| + | |||
| + | Los símbolos se crean usando la función '' | ||
| + | |||
| + | <code javascript> | ||
| + | var s1 = Symbol(" | ||
| + | </ | ||
| + | |||
| + | A la función '' | ||
| + | |||
| + | <code jvascript> | ||
| + | var s1 = Symbol(" | ||
| + | var s2 = Symbol(" | ||
| + | s1 === s2; // false | ||
| + | s1 == s2; // false | ||
| + | </ | ||
| + | |||
| + | Tampoco hay conversión alguna entre el símbolo y la cadena que se haya usado para crearlo: | ||
| + | |||
| + | <code javascript> | ||
| + | var s1 = Symbol(" | ||
| + | s1 === " | ||
| + | s1 == " | ||
| + | </ | ||
| + | |||
| + | A partir de la cadena que se haya usado como identificación del símbolo ('' | ||
| + | |||
| + | <WRAP center round info 60%> | ||
| + | Fíjate en que la función '' | ||
| + | </ | ||
| + | |||
| + | Los **símbolos son un tipo nuevo**, es decir usar '' | ||
| + | |||
| + | Los símbolos en general no se convierten de forma automática a ningún tipo, e intentar concatenar un símbolo con una cadena da error: | ||
| + | |||
| + | <code javascript> | ||
| + | var s1 = Symbol(" | ||
| + | var str = s1 + " | ||
| + | </ | ||
| + | |||
| + | Un símbolo básicamente solo es igual a sí mismo o bien a un objeto creado con '' | ||
| + | |||
| + | <code javascript> | ||
| + | var s1 = Symbol(" | ||
| + | var obj = Object(s1); | ||
| + | typeof(s1); | ||
| + | typeof(obj); | ||
| + | s1 == obj; // true | ||
| + | s1 === obj; // false | ||
| + | </ | ||
| + | |||
| + | Para convertir un símbolo en una cadena puede usarse '' | ||
| + | |||
| + | <code javascript> | ||
| + | var s1 = Symbol(" | ||
| + | var str = String(s1); | ||
| + | str == s1; // false | ||
| + | str === s1; // false | ||
| + | str == " | ||
| + | str === " | ||
| + | </ | ||
| ===== Variables privadas ===== | ===== Variables privadas ===== | ||
| + | |||
| + | Una de las cosas que mucha gente achaca a JavaScript es la imposibilidad de declarar métodos o propiedades privadas dentro de un objeto. Es cierto que el lenguaje no tiene ninguna palabra clave para declarar métodos o propiedades privadas, pero eso no implica que no se puedan crear: solo significa que no es tan directo como en otros lenguajes. Sí que es cierto que la notación de objeto no permite hacerlo, por lo que el mecanismo siempre pasa por crear el objeto a través de una función (sea o no constructora). | ||
| + | |||
| + | ==== Revealing module pattern ==== | ||
| + | |||
| + | Este patrón se usa en JavaScript para crear un // | ||
| + | |||
| + | <WRAP center round info 60%> | ||
| + | **Factoría**: | ||
| + | </ | ||
| + | |||
| + | |||
| + | El //revealing module pattern// consiste en una función anónima autoejecutable que termina devolviendo un objeto. Este objeto devuelto es el que actúa como // | ||
| + | |||
| + | <code javascript> | ||
| + | var Modulo = (function() { | ||
| + | var p1 = function() { console.log(" | ||
| + | var p2 = function() { console.log(" | ||
| + | return { | ||
| + | pub1: p1 | ||
| + | }; | ||
| + | })(); | ||
| + | Modulo.p1(); | ||
| + | Modulo.p2(); | ||
| + | Modulo.pub1(); | ||
| + | </ | ||
| + | |||
| + | Observa como todas las variables declaradas dentro de la función anónima no son accesibles desde fuera: las funciones '' | ||
| + | |||
| + | El //revealing module pattern// se basa simplemente en el hecho de que todas las variables locales no son accesibles desde el exterior: '' | ||
| + | |||
| + | <WRAP center round info 60%> | ||
| + | Observa que la variable '' | ||
| + | <code javascript> | ||
| + | (function() {...})(); | ||
| + | </ | ||
| + | |||
| + | Ese código no es (solo) la declaración de una función anónima: es la declaración de una función anónima y su invocación (observa los paréntesis finales). A esta construcción (declarar y ejecutar ipso-facto una función anónima) la llamamos " | ||
| + | </ | ||
| + | |||
| + | |||
| + | El //revealing module pattern// es pues una de las opciones que podemos usar para tener realmente funciones y métodos privados. El hándicap es que realmente es un // | ||
| + | |||
| + | ==== Visibilidad privada en función constructora ==== | ||
| + | |||
| + | Usando una función constructora, | ||
| + | |||
| + | <code javascript> | ||
| + | var Foo = function() { | ||
| + | var data = 42; | ||
| + | this.value = function() { | ||
| + | if (arguments.length == 0) { | ||
| + | return data; | ||
| + | } else { | ||
| + | data = arguments[0]; | ||
| + | } | ||
| + | } | ||
| + | this.inc = function() {data++} | ||
| + | } | ||
| + | var f1 = new Foo(); | ||
| + | f1.value(); | ||
| + | var f2 = new Foo(); | ||
| + | f1.value(100); | ||
| + | f1.value(); | ||
| + | f2.value(); | ||
| + | f1.data; | ||
| + | f2.data; | ||
| + | </ | ||
| + | |||
| + | En este caso la variable '' | ||
| + | |||
| + | Igual te estás preguntando cómo es posible que la variable '' | ||
| ===== DEMO: Revealing Module Pattern ===== | ===== DEMO: Revealing Module Pattern ===== | ||
| + | El //Revealing Module Pattern// es uno de los patrones que más se utilizan en JavaScript para tener variables y funciones de visibilidad privada. Básicamente, | ||
| + | |||
| + | <code javascript> | ||
| + | (function() { | ||
| + | })(); | ||
| + | </ | ||
| + | |||
| + | Todo lo que definamos dentro de la anterior función, serán variables y funciones privadas. | ||
| + | |||
| + | <code javascript> | ||
| + | var miModulo = (function() { | ||
| + | var value = 42; | ||
| + | var foo = function(i) { | ||
| + | return i + value * bar(); | ||
| + | } | ||
| + | | ||
| + | var bar = function() { return 10;} | ||
| + | | ||
| + | return { | ||
| + | // El primer '' | ||
| + | foo: foo | ||
| + | } | ||
| + | })(); | ||
| + | </ | ||
| + | |||
| + | La función '' | ||
| + | |||
| + | Al ejecutar el código anterior: | ||
| + | |||
| + | <code javascript> | ||
| + | miModulo; // -> Object { foo: foo() } | ||
| + | |||
| + | miModulo.pFoo(10); | ||
| + | |||
| + | // Comprobamos que no podemos acceder a variables y funciones privadas: | ||
| + | miModulo.value; | ||
| + | miModulo.bar(); | ||
| + | miModulo.foo(); | ||
| + | </ | ||
| ===== Variables privadas usando símbolos ===== | ===== Variables privadas usando símbolos ===== | ||
| + | |||
| + | Los símbolos ofrecen un nuevo mecanismo para declarar variables privadas en un objeto. Para ello **podemos usar un símbolo como nombre de propiedad de un objeto**. Eso garantiza que solo quien tenga acceso al símbolo original podrá acceder a dicha propiedad, ya que no hay otro modo de obtener el nombre de la propiedad. | ||
| + | |||
| + | Las propiedades cuyo nombre es un símbolo no aparecen ni al usar '' | ||
| + | |||
| + | <code javascript> | ||
| + | var obj={ | ||
| + | v1: 42, | ||
| + | | ||
| + | }; | ||
| + | </ | ||
| + | |||
| + | Dado el siguiente código, si miramos las propiedades que tiene obj tan solo obtendremos la propiedad v1. La otra propiedad (cuyo nombre es un símbolo) no la vemos de ninguna manera: | ||
| + | |||
| + | {{ : | ||
| + | |||
| + | Se puede ver que la propiedad '' | ||
| + | |||
| + | <code javascript> | ||
| + | var obj={ | ||
| + | v1: 42, | ||
| + | [Symbol(" | ||
| + | }; | ||
| + | var result = obj[Symbol(" | ||
| + | </ | ||
| + | |||
| + | Al finalizar este código el valor de '' | ||
| + | |||
| + | Por la misma razón el siguiente código no modifica el valor de la propiedad de 100 a 200, sino que crea otra propiedad con el valor 200 (distinta de la propiedad con el valor de 100). | ||
| + | |||
| + | <code javascript> | ||
| + | var obj={ | ||
| + | v1: 42, | ||
| + | [Symbol(" | ||
| + | }; | ||
| + | obj[Symbol(" | ||
| + | </ | ||
| + | |||
| + | Para acceder a la propiedad cuyo nombre es un símbolo necesitamos el símbolo original: | ||
| + | |||
| + | <code javascript> | ||
| + | var s = Symbol(" | ||
| + | var obj={ | ||
| + | v1: 42, | ||
| + | [s]: 100 | ||
| + | }; | ||
| + | var result = obj[s]; | ||
| + | </ | ||
| + | |||
| + | Observa que colocamos la variable símbolo entre corchetes para acceder a la propiedad. Si no usásemos los corchetes simplemente estaríamos creando (o accediendo a) una propiedad llamada " | ||
| ===== DEMO: Símbolos como nombres de propiedades ===== | ===== DEMO: Símbolos como nombres de propiedades ===== | ||
| + | Un símbolo nos permite obtener un nombre único. | ||
| + | |||
| + | <code javascript> | ||
| + | var obj = { | ||
| + | [Symbol(" | ||
| + | v: 60 | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | <code javascript> | ||
| + | obj; // -> Object { v: 60} | ||
| + | </ | ||
| + | |||
| + | No nos muestra la otra propiedad, la creada con el símbolo. Solo podemos acceder a ella utilizando el símbolo: | ||
| + | |||
| + | <code javascript> | ||
| + | obj[Symbol(" | ||
| + | |||
| + | Symbol(" | ||
| + | </ | ||
| + | |||
| + | Así que tendríamos que haber guardado el símbolo: | ||
| + | |||
| + | <code javascript> | ||
| + | var s = Symbol(" | ||
| + | |||
| + | var obj = { | ||
| + | [s]: 42, | ||
| + | v: 60 | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | Tras ejecutarlo: | ||
| + | |||
| + | <code javascript> | ||
| + | obj[s]; // -> 42 | ||
| + | </ | ||
| + | |||
| + | Podríamos ver todos los símbolos usados como propiedad en un objeto con '' | ||
| + | |||
| + | <code javascript> | ||
| + | var sy = Object.getOwnPropertySymbols(obj); | ||
| + | |||
| + | sy; // -> Array[ Symbol(v) ] | ||
| + | |||
| + | obj[sy[0]]; // -> 42 | ||
| + | </ | ||
| + | |||
| + | Los símbolos nos dan otro mecanismo para definir propiedades privadas, pero esto no impide que alguien utlizando '' | ||
| + | |||
| + | Si quisiéramos ocultar realmente una propiedad, no podemos usar símbolos. Deberíamos usar el revealing module pattern o utilizar el concepto de función constructora. | ||
| ===== DEMO: Símbolos en valores ===== | ===== DEMO: Símbolos en valores ===== | ||
| + | <code javascript> | ||
| + | var handle = function(name) { | ||
| + | |||
| + | var _name = name; | ||
| + | var _status = " | ||
| + | | ||
| + | return { | ||
| + | // getter para devolver la propiedad privada | ||
| + | get name() { return _name; }, | ||
| + | get status() { return _status; }, | ||
| + | open() { | ||
| + | if (_status == " | ||
| + | console.log(" | ||
| + | _status = " | ||
| + | } else { | ||
| + | console.log(" | ||
| + | } | ||
| + | } | ||
| + | } | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | Tras ejecutarlo: | ||
| + | |||
| + | <code javascript> | ||
| + | var h = handle(" | ||
| + | |||
| + | h.name; // -> ' | ||
| + | h.status; // -> ' | ||
| + | |||
| + | h.open(); | ||
| + | // -> Abriendo test | ||
| + | h.open(); | ||
| + | // -> Ya está abierto | ||
| + | </ | ||
| + | |||
| + | Para evitar colisiones con otros posibles valores, usaremos símbolos porque de esa manera tendremos valores únicos para nuestras propiedades y aumentamos la seguridad de nuestro código: | ||
| + | |||
| + | <code javascript> | ||
| + | |||
| + | var handle_status = { | ||
| + | Abierto: Symbol(" | ||
| + | Cerrado: Symbol(" | ||
| + | } | ||
| + | |||
| + | var handle = function(name) { | ||
| + | |||
| + | var _name = name; | ||
| + | var _status = handle_status.Cerrado; | ||
| + | | ||
| + | return { | ||
| + | // getter para devolver la propiedad privada | ||
| + | get name() { return _name; }, | ||
| + | get status() { return _status; }, | ||
| + | open() { | ||
| + | if (_status === handle_status.Cerrado; | ||
| + | console.log(" | ||
| + | _status = handle_status.Abierto; | ||
| + | } else { | ||
| + | console.log(" | ||
| + | } | ||
| + | } | ||
| + | } | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | Tras ejecutarlo: | ||
| + | |||
| + | <code javascript> | ||
| + | var h = handle(" | ||
| + | |||
| + | h; // -> Object { name: Getter, status: Getter, open: open() } | ||
| + | |||
| + | h.name; // -> ' | ||
| + | |||
| + | h.status; // -> Symbol(C) | ||
| + | |||
| + | h.open(); | ||
| + | // -> Abriendo edu | ||
| + | h.open(); | ||
| + | // -> Ya está abierto | ||
| + | |||
| + | h.status === handle_status.Abierto; | ||
| + | </ | ||
| ===== DEMO: Símbolos en nombres de métodos ===== | ===== DEMO: Símbolos en nombres de métodos ===== | ||
| + | Queremos hacer un objeto que sirva para mostrar información de otros objetos: | ||
| + | |||
| + | <code javascript> | ||
| + | var Dumper = function() { | ||
| + | this.dumpObject = function(obj) { | ||
| + | console.log(" | ||
| + | if (typeof(obj[" | ||
| + | obj[" | ||
| + | } else { | ||
| + | for (var k in obj) { | ||
| + | console.log(k, | ||
| + | } | ||
| + | } | ||
| + | } | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | Creamos un objeto: | ||
| + | |||
| + | <code javascript> | ||
| + | var a = {v: 42, ov: 69, foo() {} }; | ||
| + | |||
| + | var b = {v: 42, ov: 69, foo() {}, dump() { console.log(" | ||
| + | |||
| + | var myDumper = new Dumper(); | ||
| + | |||
| + | myDumper.dumpObject(a); | ||
| + | |||
| + | myDumper.dumpObject(b); | ||
| + | </ | ||
| + | |||
| + | Para evitar colisiones con otros métodos '' | ||
| + | |||
| + | <code javascript> | ||
| + | var Dumper = function() { | ||
| + | this.dumpObject = function(obj) { | ||
| + | console.log(" | ||
| + | if (typeof(obj[Dumper.dump]) == " | ||
| + | obj[Dumper.dump](); | ||
| + | } else { | ||
| + | for (var k in obj) { | ||
| + | console.log(k, | ||
| + | } | ||
| + | } | ||
| + | } | ||
| + | } | ||
| + | |||
| + | Dumper.dump = Symbol(" | ||
| + | |||
| + | var a = {v: 42, ov: 69, foo() {}, [Dumper.dump]() { console.log(" | ||
| + | |||
| + | var myDumper = new Dumper(); | ||
| + | |||
| + | myDumper.dumpObject(a); | ||
| + | </ | ||
| + | |||
| + | Ahora este código no tendrá colisiones con ningún otro objeto. | ||
| ===== Símbolos globales ===== | ===== Símbolos globales ===== | ||
| + | |||
| + | A pesar de que el objetivo básico de los símbolos es proporcionar nombres únicos y evitar las colisiones, de forma que solo quien tenga acceso al símbolo original podrá reproducir este nombre, **ECMAScript 2015 da la posibilidad de crear símbolos globales**. Un símbolo global es un símbolo pero que cualquiera que conozca la cadena identificativa de este símbolo puede recrear: | ||
| + | |||
| + | <code javascript> | ||
| + | var s1 = Symbol.for(" | ||
| + | var s2 = Symbol.for(" | ||
| + | s1 === s2; // true | ||
| + | s1 == s2; // true | ||
| + | var s3 = Symbol(" | ||
| + | s1 === s3; // false | ||
| + | var s4 = Symbol.for(" | ||
| + | s1 === s4; // false | ||
| + | </ | ||
| + | |||
| + | Los símbolos globales se crean con '' | ||
| + | |||
| + | Los símbolos globales permiten seguir usando símbolos para métodos o valores concretos, pero evitan el tener que exponer este símbolo para quien necesite usarlo. | ||
| + | |||
| + | En el vídeo anterior, teníamos que exponer el símbolo a través de la variable '' | ||
| + | |||
informatica/programacion/cursos/programacion_avanzada_javascript/simbolos.1729076568.txt.gz · Última modificación: por tempwin
