Herramientas de usuario

Herramientas del sitio


informatica:programacion:cursos:programacion_avanzada_javascript:notacion_objetos

Diferencias

Muestra las diferencias entre dos versiones de la página.

Enlace a la vista de comparación

Ambos lados, revisión anteriorRevisión previa
Próxima revisión
Revisión previa
informatica:programacion:cursos:programacion_avanzada_javascript:notacion_objetos [2024/10/16 12:36] – [DEMO: Prototipos en funciones constructoras - herencia] tempwininformatica:programacion:cursos:programacion_avanzada_javascript:notacion_objetos [2024/10/30 13:01] (actual) – [Propiedades con nombre dinámico] tempwin
Línea 124: Línea 124:
 En el caso del uso de funciones constructoras, el tema es un poco confuso **porque si ''Foo'' es una función constructora entonces ''Foo.prototype'' puede asignarse a cualquier objeto en cualquier momento**. Y recuerda que ''Foo.prototype'' es el prototipo de todos los objetos creados mediante la función constructora ''Foo''. Pero si sustituyes ''Foo.prototype'' por otro objeto, este cambio solo afecta a los nuevos objetos que crees con ''new Foo'', no con los ya creados: En el caso del uso de funciones constructoras, el tema es un poco confuso **porque si ''Foo'' es una función constructora entonces ''Foo.prototype'' puede asignarse a cualquier objeto en cualquier momento**. Y recuerda que ''Foo.prototype'' es el prototipo de todos los objetos creados mediante la función constructora ''Foo''. Pero si sustituyes ''Foo.prototype'' por otro objeto, este cambio solo afecta a los nuevos objetos que crees con ''new Foo'', no con los ya creados:
  
-<WRAP center round todo 60%> +{{ :informatica:programacion:cursos:programacion_avanzada_javascript:14-cambio-foo-prototype.png |}}
-Cambio de Foo.prototype +
-</WRAP>+
  
 En la imagen anterior el valor de ''f1.name'' es ''undefined'' porque ''f1'' tiene el valor inicial de ''Foo.prototype'' que no define la propiedad ''name''. Por su parte ''f2.name'' tiene valor porque ''f2'' está creado después de que hayamos cambiado ''Foo.prototype'' por otro objeto que sí define la propiedad ''name''. En la imagen anterior el valor de ''f1.name'' es ''undefined'' porque ''f1'' tiene el valor inicial de ''Foo.prototype'' que no define la propiedad ''name''. Por su parte ''f2.name'' tiene valor porque ''f2'' está creado después de que hayamos cambiado ''Foo.prototype'' por otro objeto que sí define la propiedad ''name''.
Línea 220: Línea 218:
  
 <code javascript> <code javascript>
 +Establecemos una relación de jerarquía / herencia entre Perro y Animal
 Perro.prototype = new Animal(); Perro.prototype = new Animal();
  
Línea 231: Línea 230:
 </code> </code>
  
-Establecemos una relación de jerarquía / herencia gracias a que JavaScript utiliza la cadena de prototipos, es decir, si hacemos:+Al haber establecido una relación de jerarquía / herencia gracias a que JavaScript utiliza la cadena de prototipos, podemos hacer lo siguiente:
  
 <code javascript> <code javascript>
Línea 240: Línea 239:
 ===== Prototipos en ECMAScript 2015 ===== ===== Prototipos en ECMAScript 2015 =====
  
 +Una de las novedades de ECMAScript 6 es que **soporta oficialmente la propiedad ''%%__proto__%%''**. Curiosamente dicha propiedad se incluye en el estándar **pero se declara obsoleta**.
 +
 +Puede parecer raro que algo que no existía en la versión 5 pase a existir directamente como obsoleto en la versión 6 del estándar. La razón es que el estándar incorpora ''%%__proto__%%'' para asegurar la compatibilidad con mucho código existente: a pesar de no ser oficial hay tanto código que lo usa que al incorporarlo se asegura que este código pueda ejecutarse en cualquier implementación de ECMAScript 6.
 +
 +La razón de que se considere obsoleta es que **ECMAScript 6 incorpora sus propios mecanismos para obtener y establecer el prototipo de un objeto una vez creado éste**.
 +
 +<WRAP center round important 60%>
 +Importante: A pesar de que en ECMAScript 6 cambiar el prototipo de un objeto sea una operación soportada, debemos tener presente que el impacto en rendimiento es importante. Mejor evitarlo siempre que sea posible.
 +</WRAP>
 +
 +Para obtener el prototipo de un objeto podemos usar ''Object.getPrototypeOf'' o bien ''Reflect.getPrototypeOf''. Ambas funciones son equivalentes:
 +
 +<code javascript>
 +var Foo = function() {};
 +var f1 = new Foo();
 +console.log(Object.getPrototypeOf(f1) === Foo.prototype)     // true
 +console.log(Reflect.getPrototypeOf(f1) === Foo.prototype)    // true
 +</code>
 +
 +Y para establecer el prototipo de un objeto se puede usar ''Object.setPrototypeOf'' o bien ''Reflect.setPrototypeOf''. Como antes, da igual usar la versión de [[https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create1Object]] o de [[https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect|Reflect]] ya que son equivalentes:
 +
 +<code javascript>
 +var obj = Object.create({name: 'campusMVP'});
 +console.log(obj.name);   // campusMVP
 +Object.setPrototypeOf(obj, {age: 42});
 +console.log(obj.name);   // Undefined
 +console.log(obj.age);    // 42
 +</code>
 +
 +==== Establecer el prototipo en notación de objeto ====
 +
 +Una de las novedades de ECMAScript 6 es que **permite establecer el prototipo de un objeto creado con notación de objeto**. Para ello basta con declarar una propiedad ''%%__proto__%%'' en el momento en que declaramos el objeto:
 +
 +<code javascript>
 +var a={answer: 42};
 +var obj={
 +  question: 'what is the sense of life, the universe and everything?',
 +  __proto__: a
 +}
 +console.log(obj.question, obj.answer);
 +</code>
 +
 +Se puede ver que ''obj.answer'' es ''42'' porque el objeto ''a'' es el prototipo del objeto ''obj''. Para conseguir el mismo efecto en ECMAScript 5 debíamos crear el objeto ''obj'' usando ''Object.create''.
 +
 +{{ :informatica:programacion:cursos:programacion_avanzada_javascript:14-usando-proto-notacion-objeto.png |}}
 ===== Propiedades con nombre dinámico ===== ===== Propiedades con nombre dinámico =====
  
 +Desde siempre hemos podido construir objetos con una propiedad cuyo nombre fuese dinámico, es decir dependiese de cualquier operación en tiempo de ejecución. El siguiente código crea un objeto con un método que se llama ''t_'' seguido del número del mes actual:
 +
 +<code javascript>
 +var a = {};
 +a["t_" + new Date().getMonth() + 1] = 10;
 +</code>
 +
 +Una vez se ha creado esa propiedad podemos acceder a ella con la notación de punto o de array, siendo ambas equivalentes:
 +
 +{{ :informatica:programacion:cursos:programacion_avanzada_javascript:14-propiedades-dinamicas.png |}}
 +
 +
 +Ahora bien, en **ECMAScript 5 solo podemos crear esas propiedades, añadiéndolas al objeto una vez creado**. Observa que en el código anterior declaramos el objeto vacío, y luego le añadimos la propiedad. Eso es porque la notación de objeto no nos permite declarar esas propiedades con nombre dinámico. Por supuesto en una función constructora no hay problema (dado que de hecho en la función constructora lo que realmente hacemos es añadir propiedades a ''this'').
 +
 +Una de las novedades en ECMAScript 2015 consiste en que también podemos declarar esas propiedades utilizando notación de objeto:
 +
 +<code javascript>
 +var a={
 +    ["t_" + new Date().getMonth() + 1]: 10
 +};
 +</code>
 +
 +Observa como el nombre de la propiedad es realmente una cadena y se coloca entre corchetes. Debes usar los corchetes porque sino obtendrás errores de sintaxis. Los corchetes sirven para indicarle al //parser// dónde empieza y termina la expresión que genera el nombre de la propiedad.
 +
 +==== Nombres de propiedades duplicadas ====
 +
 +En ECMAScript 5, en modo estricto, declarar un objeto con nombres de propiedades duplicadas generaba un error de sintaxis. Es lógico puesto que no tiene mucho sentido declarar un objeto con una propiedad definida más de una vez:
 +
 +<code javascript>
 +var a= {p: 20, p:30};
 +</code>
 +
 +Pero si pruebas este código en un entorno ECMAScript 2015 incluso con el modo estricto habilitado, verás que no produce error alguno.
 +
 +A partir de ECMAScript 2015 esta restricción se ha eliminado. La razón es que ahora, al poder usar nombres de propiedades dinámicos, puede ocurrir que una propiedad con nombre dinámico genere el mismo nombre que otra propiedad, digamos "normal". Entonces en lugar de prohibir nombres de propiedad duplicados, se ha optado por definir qué ocurre en caso de que existan nombres duplicados: la **propiedad declarada en segundo lugar sobrescribe a la primera**:
 +
 +<code javascript>
 +var a={p:20, p:30};
 +console.log(a.p);   // 30
 +</code>
 ===== Métodos y propiedades simplificadas ===== ===== Métodos y propiedades simplificadas =====
  
 +Acostúmbrate a la "nueva" sintaxis de ECMAScript para declarar métodos y/o propiedades en objetos, porque es tan sencilla y cómoda que te preguntarás por qué no existía antes.
 +
 +Imagina un objeto ''hello'' que tiene un método ''greetings''. Hasta ahora este objeto lo podíamos declarar de la siguiente manera:
 +
 +<code javascript>
 +var hello = {
 +    greetings: function(name) {
 +        return "hello " + name;
 +    }
 +}
 +</code>
 +
 +Pues bien, ahora tenemos disponible una **sintaxis simplificada** para métodos, que te permite declarar exactamente este mismo objeto sin necesidad de la palabra clave ''function'':
 +
 +<code javascript>
 +var hello = {
 +    greetings(name) {
 +        return "hello " + name;
 +    }
 +}
 +</code>
 +
 +Esa sintaxis simplificada puedes usarla también para //getters// y //setters// de propiedades:
 +
 +<code javascript>
 +var hello = {
 +    get salute() {return "hello"}
 +}
 +console.log(hello.salute);      // hello
 +</code>
 +
 +Un ejemplo un poco más completo, donde muestra un //getter// y un //setter// simulando una variable privada cuyo valor debe ser siempre positivo:
 +
 +<code javascript>
 +var hello = (() => {
 +    var _private = 42;
 +    return {
 +        get value() {return _private},
 +        set value(v) { 
 +            if (v > 0) {
 +                _private = v;
 +            }
 +            return _private;
 +        }
 +    }
 +})();
 +console.log(hello.value);           // 42;
 +hello.value = -10;
 +console.log(hello.value);           // 42;
 +hello.value = 20;
 +console.log(hello.value);           // 20;
 +</code>
 ===== DEMO: Novedades en notación de objetos ===== ===== DEMO: Novedades en notación de objetos =====
 +
 +Repaso de las 4 novedades importantes de **ECMAScript 6** en notación de objetos.
 +
 +Posibilidad de **especificar el prototipo** del objeto en notación del objeto a la vez que declaramos el objeto:
 +
 +<code javascript>
 +var a = {name: "campusmvp"}
 +
 +var b = {
 +    __proto__: a,
 +    cursos: ["javascript"]
 +}
 +
 +b.name; // -> 'campusmvp'
 +</code>
 +
 +Antes no se podía hacer, era necesario el uso de ''Object.create'' para obtener el mismo efecto.
 +
 +Otra de las novedades son las **propiedades con nombre dinámico**:
 +
 +<code javascript>
 +var b = {
 +    __proto__: a,
 +    cursos: ["javascript"],
 +    ["i_" + Math.random()]: 42
 +}
 +</code>
 +
 +Lo ejecutamos:
 +
 +<code javascript>
 +b; // -> Object { cursos: Array[1], i_0.123412341234: 42}
 +</code>
 +
 +En ECMAScript 5 podíamos hacer:
 +
 +<code javascript>
 +b["i_" + Math.random()]: 42
 +</code>
 +
 +Pero no podíamos hacerlo en la declaración de un objeto.
 +
 +Otra novedad más es una **sintaxis simplificada para declarar funciones**. 
 +
 +<code javascript>
 +var b = {
 +    __proto__: a,
 +    cursos: ["javascript"],
 +    ["i_" + Math.random()]: 42,
 +    foo(p) {return p + 1} // No es necesario usar 'function' ni los dos puntos (:)
 +}
 +</code>
 +
 +Finalmente, otra característica nueva son los //shorthand properties names//:
 +
 +<code javascript>
 +var url = "https://campusmvp.es";
 +
 +var options = {
 +    url, // en lugar de url: url
 +    method: "GET"
 +});
 +</code>
informatica/programacion/cursos/programacion_avanzada_javascript/notacion_objetos.1729074995.txt.gz · Última modificación: por tempwin