03.1. JavaScript Avanzado
03.1. JavaScript Avanzado
Javascript avanzado
---
## Prototipos
Javascript es un [lenguaje basado en
prototipos](https://developer.mozilla.org/es/docs/Web/JavaScript/Guide/
Details_of_the_Object_Model#lenguajes_basados_en_clases_vs._basados_en_prototipos)
en lugar de estar basado en clases, es decir un objeto que se utiliza como
plantilla(prototipo) a partir de la cual se obtiene el conjunto inicial de
propiedades del nuevo objeto.
* En Javascript: El conjunto _inicial_ de propiedades lo determina la función
constructor o el **prototipo**. Se pueden añadir y quitar propiedades dinámicamente
a objetos específicos o a un conjunto de objetos.
* En Java: En la definición de una **clase** se especifican _todas_ las
propiedades de todas las instancias de esa clase. No se puede añadir propiedades
dinámicamente en tiempo de ejecución.
### Ejemplo
https://www.youtube.com/watch?v=Hf3n-p3VYx4
## Creando un objeto con `new`
https://github.com/mdn/interactive-examples
---
Los objetos los usamos para organizar el código fuente de una forma más clara y
para encapsular propiedades y métodos. Con `new NombreClase()` creamos un objeto o
una instancia de la clase.
```js
/*
Object es un objeto genérico al que podemos añadir
propiedades y métodos propios
*/
var elObjeto = new Object();
JavaScript forma los objetos como arrays asociativos, cuyos índices son
claves(strings)
```js
coche["color"] = "rojo";
coche["marca"] = "seat";
coche["modelo"] = "leon";
## Definiendo Clases
https://developer.mozilla.org/es/docs/Web/JavaScript/Reference/Classes
```js
/*
Si ponemos la instancia antes de la declaración
Nos da un ReferenceError
*/
```
```js
console.log(Rectangulo.name);
// output: "Rectangulo"
// Esta es una expresión de clase nombrada, usamos nombre en la clase
let Rectangulo2 = class Rectangulo2 {
constructor(alto, ancho) {
this.alto = alto;
this.ancho = ancho;
}
};
console.log(Rectangulo2.name);
// output: "Rectangulo2
```
### Constructor
El método [constructor](https://developer.mozilla.org/es/docs/Web/JavaScript/
Reference/Classes/constructor) es un método especial para crear e inicializar un
objeto creado con una clase. Solo puede haber un método especial con el nombre
"constructor" en una clase. Si contiene más de una ocurrencia del método
constructor, se arroja un error
[SyntaxError](https://developer.mozilla.org/es/docs/Web/JavaScript/Reference/
Global_Objects/SyntaxError)
```js
constructor() {}
```
```js
constructor(...args) {
super(...args);
}
```
```js
class Rectangulo {
constructor (alto, ancho) {
this.alto = alto;
this.ancho = ancho;
// .name no está definido por defecto
this.name = "Clase Rectángulo";
}
// Getter
get area() {
return this.calcArea();
}
// Método
calcArea () {
return this.alto * this.ancho;
}
}
/*
cuadrado.area:100
cuadrado.name:Clase Rectángulo
Ejecutado: 04.js
*/
```
```js
class Punto {
constructor ( x , y ){
this.x = x;
this.y = y;
}
static distancia ( a , b) {
const dx = a.x - b.x;
const dy = a.y - b.y;
return Math.sqrt ( dx * dx + dy * dy );
}
}
const p1 = new Punto(5, 5);
const p2 = new Punto(10, 10);
```js
// 06.js
// Ejemplo tomado de https://es.javascript.info/static-properties-methods
class Animal {
static planeta = "Tierra"; // Es un atributo que van a tener todos los objetos
constructor(nombre, velocidad)
{
this.velocidad = velocidad;
this.nombre = nombre;
}
corre(velocidad = 0)
{
this.velocidad += velocidad; // ojo velocidad local sirve para aumentar la
// velocidad inicial del constructor
document.write(this.nombre + " corre a una velocidad de " + this.velocidad);
document.write("<br>");
}
// #############################################
// Instanciamos Conejo, creando 2 ejemplares
// #############################################
let Conejos = [
new Conejo("Conejo Blanco", 10),
new Conejo("Conejo Negro", 5)
];
// #####################################################################
/* Conejo.compara es un método estático heredado de la superclase Animal
se lo aplicamos a los 2 ejemplares
Conejo.compara(Conejos[0],Conejos[1]);
/* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/
Global_Objects/Array/sort
// ##########################################################################
Dentro del método sort() aplicado a un array podemos poner como argumento
la función comparación objeto.sort(clase.compareFN)
// ##########################################################################
*/
Conejos.sort(Conejo.compara);
/*
// #####################################################################
// Esta propiedad también se hereda de Animal
// #####################################################################
document.write(Conejo.planeta); // Tierra
document.write("<br>");
document.write("Ejecutado: 06.js");
```
Como los objetos son en realidad arrays asociativos que almacenan sus propiedades y
métodos, la forma más directa para definir esas propiedades y métodos es la
notación de puntos:
elObjeto.id = "10";
elObjeto.nombre = "Objeto de prueba";
elObjeto\['id'\] = "10";
elObjeto\['nombre'\] = "Objeto de prueba";
Además de las propiedades, los métodos de los objetos también se pueden definir
mediante la notación de puntos:
elObjeto.muestraId = function() {
alert("El ID del objeto es " + this.id);
}
Uno de los aspectos más importantes del ejemplo anterior es el uso de la palabra
reservada `this`. La palabra `this` se suele utilizar habitualmente dentro de los
métodos de un objeto y siempre hace referencia al objeto que está llamado a ese
método.
Dentro del método, `this` apunta al objeto que llama a ese método. En este caso,
`this` hace referencia a `elObjeto`. Por tanto, la instrucción del método
`muestraId` es equivalente a indicar:
Además, la palabra `this` se debe utilizar siempre que se quiera acceder a una
propiedad de un objeto, ya que en otro caso, no se está accediendo correctamente a
la propiedad:
function obtieneId() {
return this.id;
}
elObjeto.obtieneId = obtieneId;
Para asignar una función externa al método de un objeto, sólo se debe indicar el
nombre de la función externa sin paréntesis. Si se utilizaran los paréntesis:
function obtieneId() {
return this.id;
}
elObjeto.obtieneId = obtieneId();
Por otra parte, no es obligatorio que el método del objeto se llame igual que la
función externa, aunque es posible que así sea.
Aplicacion.Modulos\[0\].objetoInicial = inicial;
JavaScript define un par de métodos denominados `apply()` y `call()` que son muy
útiles para las funciones. Ambos métodos permiten ejecutar una función como si
fuera un método de otro objeto. La única diferencia entre los dos métodos es la
forma en la que se pasan los argumentos a la función.
El siguiente ejemplo muestra cómo utilizar el método `call()` para ejecutar una
función como si fuera un método del objeto `elObjeto`:
function miFuncion(x) {
return this.numero + x;
}
El resto de parámetros del método `call()` son los parámetros que se pasan a la
función. En este caso, solamente es necesario un parámetro, que es el número que se
sumará a la propiedad `numero` del objeto.
El método `apply()` es idéntico al método `call()`, salvo que en este caso los
parámetros se pasan como un array:
function miFuncion(x) {
return this.numero + x;
}
var elObjeto = new Object();
elObjeto.numero = 5;
Para crear un array normal mediante JSON, se indican sus valores separados por
comas y encerrados entre corchetes. Por lo tanto, el ejemplo anterior se puede
reescribir de la siguiente manera utilizando la notación JSON:
En cualquier caso, la notación JSON permite definir los arrays asociativos de una
forma mucho más concisa. De hecho, el ejemplo anterior se puede reescribir de la
siguiente manera utilizando la notación JSON:
1. Los contenidos del array asociativo se encierran entre llaves (`{` y `}`)
2. Los elementos del array se separan mediante una coma (`,`)
3. La clave y el valor de cada elemento se separan mediante dos puntos (`:`)
Como JavaScript ignora los espacios en blanco sobrantes, es posible reordenar las
claves y valores para que se muestren más claramente en el código fuente de la
aplicación. El ejemplo anterior se puede rehacer de la siguiente manera añadiendo
nuevas líneas para separar los elementos y añadiendo espacios en blanco para
tabular las claves y para alinear los valores:
var titulos = {
rss: "Lector RSS",
email: "Gestor de email",
agenda: "Agenda"
};
var modulo = {
titulo : "Lector RSS",
objetoInicial : { estado : 1, publico : 0, nombre : "Modulo RSS", datos : {} }
};
Los objetos se pueden definir en forma de pares clave/valor separados por comas y
encerrados entre llaves. Para crear objetos vacíos, se utilizan un par de llaves
sin contenido en su interior `{}`.
**Arrays**
**Objetos**
var objeto = { clave1: valor1, clave2: valor2, clave3: valor3, ..., claveN:
valorN };
La notación abreviada se puede combinar para crear arrays de objetos, objetos con
arrays, objetos con objetos y arrays, etc. A continuación se muestran algunos
ejemplos de aplicaciones web reales que crean objetos mediante esta notación.
kinds: \['Service','Hours','Days','Product'\],
change\_kind: function(i) {
if($F('lines\_'+i+'\_kind')=='Hours') {
$('lines\_'+i+'\_unit\_price').value = $F('default\_hourly\_rate');
this.update();
}
},
focus\_num: function() {
$('invoice\_number').focus();
},
use\_freight: function() {
return $('invoice\_use\_freight').checked
},
freight: function() {
return this.use\_freight() ? Number(noCommas($('invoice\_freight').value)) :
0 ;
},
...
}
pf.prototype = {
Ed:function(a){this.hm=a},
dh:function(){if(this.eb.Th>0) {var a=Math.random()\*100; return a<this.eb.Th}
return false },
Sg:function(a,b,c){ this.Vd=2; this.kb=Ic(a,true); this.Pc=b; this.Vl=c;
this.Be() },
ne:function(a,b,c){ this.Vd=2; this.kb=Ic(a,true); this.Pc=null; this.Vl=b; if(c)
{this.vm=false} this.Be()},
...
}
var Prototype = {
Version: '1.6.0.2',
ScriptFragment: '<script\[^>\]\*>(\[\\\\S\\\\s\]\*?)<\\/script>',
JSONFilter: /^\\/\\\*-secure-(\[\\s\\S\]\*)\\\*\\/\\s\*$/,
emptyFunction: function() { },
K: function(x) { return x }
};
App.Modules.RssReaderInfos = {
infos: App.Loc.defaultRssReader\_infos,
defaultObj: {status:1, share:0, title:"", moduleName:"RssReader", data:{}}
}
App.Modules.GmailInfos = {
title: App.Loc.defaultGmail\_title,
infos: App.Loc.defaultGmail\_infos,
defaultObj:{status:1, share:0, title:App.Loc.defaultGmail\_title,
moduleName:"Gmail", data:{}},
path: NV\_PATH+"modules/gmail/gmail.js?v=5",
ico: NV\_PATH+"img/gmail.gif"
}
App.Modules.WeatherInfos = {
title: App.Loc.defaultWeather\_title,
infos: App.Loc.defaultWeather\_infos,
defaultObj:{status:1, share:0, title:App.Loc.defaultWeather\_title,
moduleName:"Weather", data:{town:"FRXX0076"}},
path: NV\_PATH+"modules/weather/weather.js?v=2",
ico: NV\_PATH+"img/weather.gif"
}
var exportManager = {
show: function() {
$('exportButton').src = "/images/b-export-on.gif"
showElement('download\_export')
},
hide: function() {
$('exportButton').src = "/images/b-export.gif"
hideElement('download\_export')
},
toggle: function() {
Element.visible('download\_export') ? this.hide() : this.show()
}
}
var dojo;
if(dj\_undef("dojo")){ dojo = {}; }
dojo.version = {
major: 0, minor: 2, patch: 2, flag: "",
revision: Number("$Rev: 2836 $".match(/\[0-9\]+/)\[0\]),
toString: function() {
with (dojo.version) {
return major + "." + minor + "." + patch + flag + " (" + revision + ")";
}
}
};
A partir de los ejemplos anteriores, se deduce que la forma habitual para definir
los objetos en JavaScript se basa en el siguiente modelo creado con la notación
JSON:
var objeto = {
"propiedad1": valor\_simple\_1,
"propiedad2": valor\_simple\_2,
"propiedad3": \[array1\_valor1, array1\_valor2\],
"propiedad4": { "propiedad anidada": valor },
"metodo1": nombre\_funcion\_externa,
"metodo2": function() { ... },
"metodo3": function() { ... },
"metodo4": function() { ... }
};
En un mismo objeto se puede utilizar de forma simultánea la notación tradicional de
JavaScript y la notación JSON:
Ejercicio 1
Definir la estructura de un objeto que almacena una factura. Las facturas están
formadas por la información de la propia empresa (nombre de la empresa, dirección,
teléfono, NIF), la información del cliente (similar a la de la empresa), una lista
de elementos (cada uno de los cuales dispone de descripción, precio, cantidad) y
otra información básica de la factura (importe total, tipo de iva, forma de pago).
Una vez definidas las propiedades del objeto, añadir un método que calcule el
importe total de la factura y actualice el valor de la propiedad correspondiente.
Por último, añadir otro método que muestre por pantalla el importe total de la
factura.
[Ver solución](http://librosweb.es/ajax/capitulo_14.html)
___
___