Herramientas de usuario

Herramientas del sitio


informatica:programacion:cursos:programacion_avanzada_javascript:simbolos

¡Esta es una revisión vieja del documento!


Símbolos

Módulo perteneciente al curso Programación avanzada con JavaScript y ECMAScript.

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 Symbol:

var s1 = Symbol("foo");

A la función Symbol se le pasa una cadena (no se admite llamar a Symbol sin parámetros) que sirve como identificador del símbolo. Pero aunque le pasemos la misma cadena a dos símbolos, cada uno de ellos es distinto para ECMAScript. Por ejemplo, el siguiente código crea dos símbolos distintos:

var s1 = Symbol("foo");
var s2 = Symbol("foo");
s1 === s2;   // false
s1 == s2;    // false

Tampoco hay conversión alguna entre el símbolo y la cadena que se haya usado para crearlo:

var s1 = Symbol("foo");
s1 === "foo";   // false
s1 == "foo";    // false

A partir de la cadena que se haya usado como identificación del símbolo (foo en nuestros ejemplos) no hay manera de poder obtener el símbolo asociado a ella.

Fíjate en que la función Symbol no es una función constructora. Usar new Symbol genera un error.

Los símbolos son un tipo nuevo, es decir usar typeof sobre una variable de tipo símbolo no devolverá “object”, sino que devolverá “symbol”.

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:

var s1 = Symbol("foo");
var str = s1 + "bar";       // TypeError

Un símbolo básicamente solo es igual a sí mismo o bien a un objeto creado con Object(s) siendo s el propio símbolo:

var s1 = Symbol("foo");
var obj = Object(s1);
typeof(s1);     // "symbol"
typeof(obj);    // "object"
s1 == obj;      // true
s1 === obj;     // false

Para convertir un símbolo en una cadena puede usarse String(s) (siendo s el símbolo), pero esto no devuelve la cadena que se haya usado como identificador, sino que devuelve una representación en formato cadena del símbolo:

var s1 = Symbol("foo");
var str = String(s1);  //"Symbol(foo)"
str == s1;      // false
str === s1;     // false
str == "foo";   // false
str === "foo";  // false

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 singleton, es decir un objeto del que existe una sola instancia. Por supuesto nada impide que este objeto sea una función (en JavaScript las funciones son un tipo particular de objetos) que permita crear otros objetos, por lo que puedes usar este patrón para exponer una factoría.

Factoría: Un patrón de orientación a objetos. Una factoría es un objeto (o una función) cuya funcionalidad es la de crear otros objetos. Se suele usar cuando la creación del objeto no es trivial y/o bien requiere personalizaciones posteriores a la creación del objeto pero necesarias antes de poder usarlo.

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 singleton. Para “exponer” el singleton y que sea accesible se puede: o bien asignar a una variable global, o bien guardarlo dentro de algún contexto (usualmente el global). Todo lo que se declara dentro de la función anónima autoejecutable es privado y solo lo que se devuelve al final es accesible:

var Modulo = (function() {
    var p1 = function() { console.log("En p1"); p2();}
    var p2 = function() { console.log("En p2");}
    return {
        pub1: p1
    };
})();
    Modulo.p1();            // Error p1 is not a function
    Modulo.p2();            // Error p2 is not a funcion
    Modulo.pub1();          // Ejecuta pub1 (que es realmente p1)

Observa como todas las variables declaradas dentro de la función anónima no son accesibles desde fuera: las funciones p1 y p2 no se pueden llamar desde el exterior. Solo se puede acceder al objeto que se devuelve en el return. Si queremos hacer que alguna función declarada en el módulo, sea accesible debemos devolverla como una propiedad del objeto (es el caso de pub que simplemente nos permite acceder a la propia función privada p1).

El revealing module pattern se basa simplemente en el hecho de que todas las variables locales no son accesibles desde el exterior: p1 y p2 son variables locales de la función anónima autoejecutable.

Observa que la variable Modulo no contiene la función anónima, sino el resultado de invocar a la función anónima (es decir el objeto que devolvemos). Observa que la función la declaramos con:

(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 “función anónima autoejecutable”. Una vez ejecutada ya no se puede volver ejecutar, dado que la función era anónima y no la hemos guardado en ninguna variable.

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 singleton (no podemos tener dos instancias de Modulo) Si queremos tener algo más parecido a una “clase” de la cual crear objetos y que tenga métodos o propiedades privadas debemos usar una función constructora.

Visibilidad privada en función constructora

Usando una función constructora, el tener variables o funciones privadas a nivel de objeto es trivial: basta con no asignarlas a this sino declararlas como locales a la función constructora. Dado que cada creación al objeto implica una llamada a la función constructora esas variables locales existen por cada objeto:

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();             // 42
var f2 = new Foo();
f1.value(100);
f1.value();             // 100
f2.value();             // 42
f1.data;                // undefined
f2.data;                // undefined

En este caso la variable data es una variable privada de cada objeto construido mediante la función constructora Foo. Solo lo que se asigna a this forma parte real del objeto y por lo tanto es accesible.

Igual te estás preguntando cómo es posible que la variable data no sea destruida al finalizar la ejecución de la función Foo. La pregunta tiene toda la lógica del mundo, ya que la variable data es una variable local y las variables locales son destruidas al finalizar la ejecución de la función. La razón por la que no es destruida es porque es utilizada (accedida) desde funciones que tienen más ámbito de vida (en este caso desde funciones que se asignan a this y por lo tanto su ámbito de vida es el del objeto). Cuando una función declarada dentro de otra función (p. ej. la función value declarada dentro de la función Foo) accede a una variable local de la función contenedora, decimos que la función contenida “captura la variable” y que ejerce de closure.

DEMO: Revealing Module Pattern

Variables privadas usando símbolos

DEMO: Símbolos como nombres de propiedades

DEMO: Símbolos en valores

DEMO: Símbolos en nombres de métodos

Símbolos globales

informatica/programacion/cursos/programacion_avanzada_javascript/simbolos.1729076955.txt.gz · Última modificación: por tempwin