Curso Javascript profesionales

601

description

Documentación curso Javascript para profesionales por Redradix School. Curso avanzado de Javascript.

Transcript of Curso Javascript profesionales

Page 1: Curso Javascript profesionales
Page 2: Curso Javascript profesionales

Javascript para Profesionales

made with love by Redradix (www.redradix.com)

Page 3: Curso Javascript profesionales

Fundamentos

Page 4: Curso Javascript profesionales

Objetos

! Conjunto de propiedades propias + heredadas de otro objeto (prototipos)

! ¡Qué no cunda el pánico!

Page 5: Curso Javascript profesionales

Objetos

! Dinámicos

! Set de strings

var obj = {};obj.nuevaPropiedad = 1;delete obj.nuevaPropiedad;

var strset = { hola: true, adios: true};"hola" in strset;

Page 6: Curso Javascript profesionales

Objetos

! Referencias

! Todo son objetos excepto: strings, números, booleans, null o undefined

! Strings, números y booleans se comportan como objetos inmutables

var p1 = {x: 1}, p2 = p1;

p1 === p2; // truep1.x = 5;p2.x; // 5

Page 7: Curso Javascript profesionales

Objetos

! Ciudadanos de primer orden

! Contener valor primitivo u otros objetos. Incluyendo funciones.

(function (obj) { return {b: 2};})({a: 1});

var obj = { f: function() { console.log("hola"); }};obj.f();

Page 8: Curso Javascript profesionales

Objetos

! Literales

- clase: Object

- sencillos y ligeros

! Construidos

- clase: prototype

- constructor

{ un: "objeto", literal: true};

new NombreDeUnaFuncion();

Page 9: Curso Javascript profesionales

Clases

! Pueden entenderse como:− Tipo (jerárquico) de datos

− Aquí no

− Categoría de objetos con la misma estructura

− Al grano: objetos con el mismo prototipo.

Page 10: Curso Javascript profesionales

Clases

Si esto es un “punto”

Y esto es otro “punto”

¿Qué es esto?

¿Y esto?

var point = {x: 0, y: 0};

var point2 = {x: 5, y: 5};

var what = {x: 10, y: 10};

var isit = {x: -10, y: -20};

Page 11: Curso Javascript profesionales

Mensajes

• Teniendo:

• ¿Que significa esto?

var obj = { nombre: "Pepito", saludo: function () { return "Hola, Mundo!"; }};

obj.nombre;

Page 12: Curso Javascript profesionales

Mensajes

• Teniendo:

• ¿Y esto?

var obj = { nombre: "Pepito", saludo: function () { return "Hola, Mundo!"; }};

obj.saludo;

Page 13: Curso Javascript profesionales

Mensajes

• Teniendo:

• ¿Y esto otro? (¡cuidado!)

var obj = { nombre: "Pepito", saludo: function () { return "Hola, Mundo!"; }};

obj[“saludo”]();

Page 14: Curso Javascript profesionales

Mensajes

• Teniendo:

• ¿Es lo mismo?

var obj = { nombre: "Pepito", saludo: function () { return "Hola, Mundo!"; }};

var fn = obj["saludo"];fn();

Page 15: Curso Javascript profesionales

Mensajes

• Teniendo:

• ¡NO es no mismo!

var obj = { nombre: "Pepito", saludo: function () { return "Hola, Mundo!"; }};

var fn = obj["saludo"];fn();

Page 16: Curso Javascript profesionales

Mensajes

• Una función se puede ejecutar de 4 maneras:

- Invocando directamente la función

- Enviando un mensaje a un objeto (método)

- Como constructor

- Indirectamente, a través de call(...) y apply(...)

(function() { alert("Hey!"); })();

objeto.metodo();

new MiConstructor();

fn.call({}, "param");

Page 17: Curso Javascript profesionales

Mensajes

- Invocando directamente la función

- Enviando un mensaje a un objeto (método)

(function() { alert("Hey!"); })();

objeto.metodo();

Page 18: Curso Javascript profesionales

Mensajes

Un mensaje se envía a un receptor

var obj = {};obj.toString(); // [object Object]

"hola don Pepito".toUpperCase();

Page 19: Curso Javascript profesionales

Mensajes

Un mensaje se envía a un receptor

var obj = {};obj.toString(); // [object Object]

"hola don Pepito".toUpperCase();

Page 20: Curso Javascript profesionales

Mensajes

La sintaxis es engañosa

var obj = { coleccion: ["uno", "dos", "tres"], metodo: function() { return "Hola, Mundo!"; }};

obj.coleccion[1]; obj.metodo();vs.

Page 21: Curso Javascript profesionales

Mensajes

obj.metodo();var fn = obj.metodo;fn();

Page 22: Curso Javascript profesionales

Mensajes

obj.metodo();var fn = obj.metodo;fn();

- Accede a la propiedad “metodo” de obj

- Supongo que es una función y la invoco

Page 23: Curso Javascript profesionales

Mensajes

obj.metodo();var fn = obj.metodo;fn();

- Accede a la propiedad “metodo” de obj

- Supongo que es una función y la invoco

- Envía el mensaje “metodo” a obj

- Si existe, obj se encarga de ejecutar la función

Page 24: Curso Javascript profesionales

Mensajes

obj.metodo();var fn = obj.metodo;fn();

- Accede a la propiedad “metodo” de obj

- Supongo que es una función y la invoco

- NO HAY RECEPTOR

- Envía el mensaje “metodo” a obj

- Si existe, obj se encarga de ejecutar la función

- obj ES EL RECEPTOR

Page 25: Curso Javascript profesionales

Mensajes

Un error típico:

$("#elemento").click(objeto.clickHandler);

Page 26: Curso Javascript profesionales

Mensajes

Un error típico:

• Lo que se intenta decir:

- “Al hacer click sobre #elemento, envía el mensaje clickHandler a objeto”

$("#elemento").click(objeto.clickHandler);

Page 27: Curso Javascript profesionales

Mensajes

Un error típico:

• Lo que se intenta decir:

- “Al hacer click sobre #elemento, envía el mensaje clickHandler a objeto”

• Lo que se dice en realidad:

- “Accede al valor de la propiedad clickHandler de objeto y ejecútalo al hacer click sobre #elemento”

$("#elemento").click(objeto.clickHandler);

Page 28: Curso Javascript profesionales

El receptor: ...

¿Por qué tanto lío con el receptor del mensaje?

Page 29: Curso Javascript profesionales

El receptor: this

¿Por qué tanto lío con el receptor del mensaje?

- ¡El receptor es this!

- La metáfora mensaje/receptor aclara su (escurridizo) significado

Page 30: Curso Javascript profesionales

El receptor: this

this = “el receptor de este mensaje”

var nombre = "Sonia";

var obj = { nombre: "Pepito", saludo: function() { alert("hola " + this.nombre); }}

obj.saludo();

Page 31: Curso Javascript profesionales

El receptor: this

this• Su significado es dinámico

• Se decide en el momento (y según la manera) de ejecutar la función

• Se suele llamar “el contexto de la función”

• Cuando no hay receptor, apunta al objeto global

Page 32: Curso Javascript profesionales

El receptor: this

Cuando no hay receptor, es el objeto global

var nombre = "Sonia"

var obj = { nombre: "Pepito", saludo: function() { alert("hola " + this.nombre) }}

var fn = obj["saludo"];fn();

Page 33: Curso Javascript profesionales

El receptor: this

Su valor es dinámico

var obj = { nombre: "Pepito", saludo: function() { alert("hola " + this.nombre); }};

var maria = { nombre: "María"};

maria.saludo = obj.saludo;maria.saludo();

Page 34: Curso Javascript profesionales

El receptor: this

Semánticamente, es como un parámetro oculto

que el receptor se encargara de proveer

function ([this]) { alert("hola " + this.nombre);}

obj.saludo(); => saludo([obj]);

Page 35: Curso Javascript profesionales

El receptor: this

Semánticamente, es como un parámetro oculto

var nombre = "Sonia";

var obj = { nombre: "Pepito", saludo: function() { var saludo_fn = function() { alert("hola " + this.nombre); }; saludo_fn(); }};

obj.saludo();

Page 36: Curso Javascript profesionales

El receptor: this

Semánticamente, es como un parámetro oculto

var nombre = "Sonia";

var obj = { nombre: "Pepito", saludo: function([this]) { var saludo_fn = function([this]) { alert("hola " + this.nombre); }; saludo_fn([objeto global]); }};

obj.saludo([obj]);

Page 37: Curso Javascript profesionales

El receptor: this

Semánticamente, es como un parámetro oculto

var nombre = "Sonia";

var obj = { nombre: "Pepito", saludo: function([this]) { var saludo_fn = function([this]) { alert("hola " + this.nombre); }; saludo_fn([objeto global]); }};

obj.saludo([obj]);

Page 38: Curso Javascript profesionales

El receptor: this

Es decir:

• Cada función tiene su propio this

• Una función anidada en otra NO comparte el receptor

• El valor de this depende de la invocación, NO de la definición (no se clausura)

Page 39: Curso Javascript profesionales

El receptor: this

Otro error común:

var obj = { clicks: 0, init: function() { $("#element").click(function() { this.clicks += 1; }); }};

obj.init();

Page 40: Curso Javascript profesionales

El receptor: this

Otro error común:

var obj = { clicks: 0, init: function([this]) { $("#element").click(function([this]) { this.clicks += 1; }); }};

obj.init([obj]);

Page 41: Curso Javascript profesionales

El receptor: this

Una posible solución (aunque no la mejor):

var obj = { clicks: 0, init: function() { var that = this; $("#element").click(function() { that.clicks += 1; }); }};

obj.init();

Page 42: Curso Javascript profesionales

Repaso: Mensajes

• Una función se puede ejecutar de 4 maneras:

- Invocando directamente la función

- Enviando un mensaje a un objeto (método)

- Como constructor

- Indirectamente, a través de call(...) y apply(...)

(function() { alert("Hey!"); })();

objeto.metodo();

new MiConstructor();

fn.call({}, "param");

Page 43: Curso Javascript profesionales

Repaso: Mensajes

Una función se puede ejecutar de 4 maneras:

Invocando directamente la función

Enviando un mensaje a un objeto (método)

Como constructor

- Indirectamente, a través de call(...) y apply(...)

fn.call({}, "param");

Page 44: Curso Javascript profesionales

El receptor: this

• Las funciones son objetos

• Se pueden manipular como cualquier otro objeto

- Asignar valores a propiedades

- Pasar como parámetros a otras funciones

- Ser el valor de retorno

- Guardarse en variables u otros objetos

• Tienen métodosvar fn = function() { alert("Hey!"); };

fn.toString();

Page 45: Curso Javascript profesionales

El receptor: this

• Dos métodos permiten manipular el receptor (contexto):

- fn.call(context [, arg1 [, arg2 [...]]])

- fn.apply(context, arglist)

var a = [1,2,3];Array.prototype.slice.call(a, 1, 2); // [2]

var a = [1,2,3];Array.prototype.slice.apply(a, [1, 2]); // [2]

Page 46: Curso Javascript profesionales

El receptor: this

var nombre = "Objeto Global";

function saluda() { alert("Hola! Soy " + this.nombre);}

var alicia = { nombre: "Alicia"};

saluda();

saluda.call(alicia);

Page 47: Curso Javascript profesionales

arguments

• El otro parámetro oculto

• Contiene una lista de todos los argumentos

• NO es un Array

function echoArgs() { alert(arguments); // [object Arguments]}

echoArgs(1, 2, 3, 4);

Page 48: Curso Javascript profesionales

arguments

• Se comporta (más o menos) como Array...

• ...pero NO del todo

function echoArgs() { alert(arguments[0]); // 1}

echoArgs(1, 2, 3, 4);

function echoArgs() { return arguments.slice(0, 1); // Error!}

echoArgs(1, 2, 3, 4);

Page 49: Curso Javascript profesionales

arguments

• Un truco:

function echoArgs() { var slice = Array.prototype.slice; return slice.call(arguments, 0, 1);}

echoArgs(1, 2, 3, 4); // [1]

Page 50: Curso Javascript profesionales

arguments

¡Cuidado, se comporta como parámetro oculto!

function exterior() { var interior = function() { alert(arguments.length); }; interior();}

exterior("a", "b", "c");

Page 51: Curso Javascript profesionales

intermedio: this y arguments

¿Qué hace esta función?

function misterio(ctx, fn) { return function() { return fn.apply(ctx, arguments); }}

Page 52: Curso Javascript profesionales

Intermedio: this y arguments

function misterio(ctx, fn) { return function() { return fn.apply(ctx, arguments); }}

var algo = misterio();

typeof algo; // ???

Page 53: Curso Javascript profesionales

Intermedio: this y arguments

function misterio(ctx, fn) { return function() { return fn.apply(ctx, arguments); }}

var algo = misterio();

algo(); // ???

Page 54: Curso Javascript profesionales

Intermedio: this y arguments

function misterio(ctx, fn) { return function() { return fn.apply(ctx, arguments); }}

var algo = misterio({}, function() { return this;});

typeof algo(); // ???

Page 55: Curso Javascript profesionales

Intermedio: this y arguments

function misterio(ctx, fn) { return function() { return fn.apply(ctx, arguments); }}

var obj = {};

var algo = misterio(obj, function() { return this;});

obj === algo(); // ???

Page 56: Curso Javascript profesionales

Intermedio: this y arguments

function misterio(ctx, fn) { return function() { return fn.apply(ctx, arguments); }}

var obj = {};

var algo = misterio({}, function() { return this;});

obj === algo(); // ???

Page 57: Curso Javascript profesionales

Intermedio: this y arguments

function misterio(ctx, fn) { return function() { return fn.apply(ctx, arguments); }}

var obj = { nombre: "Bárbara"};

var algo = misterio(obj, function() { return this.nombre;});

algo(); /// ???

Page 58: Curso Javascript profesionales

Intermedio: this y arguments

function misterio(ctx, fn) { return function() { return fn.apply(ctx, arguments); }}

var obj = { nombre: "Bárbara"};

var algo = misterio(obj, function (saludo) { return saludo + " " + this.nombre;});

algo("Hola, "); /// ???

Page 59: Curso Javascript profesionales

Intermedio: this y arguments

function misterio(ctx, fn) { return function() { return fn.apply(ctx, arguments); }}

var barbara = { nombre: "Bárbara" };var carlos = { nombre: "Carlos" };

var algo = misterio(barbara, function (saludo) { return saludo + " " + this.nombre;});

algo.call(carlos, "Hola, "); /// ???

Page 60: Curso Javascript profesionales

Intermedio: this y arguments

• bind: fija una función a un contexto

function bind(ctx, fn) { return function() { return fn.apply(ctx, arguments); }}

Page 61: Curso Javascript profesionales

Intermedio: this y arguments

Volviendo al problema:

var obj = { clicks: 0, init: function() { $("#element").click(function() {

// MAL this.clicks += 1; }); }};

obj.init();

Page 62: Curso Javascript profesionales

Intermedio: this y arguments

Apaño:

var obj = { clicks: 0, init: function() { var that = this; $("#element").click(function() { that.clicks += 1; }); }};

obj.init();

Page 63: Curso Javascript profesionales

Intermedio: this y arguments

¿Qué pasa si el callback es un método?

var obj = { clicks: 0, incClicks: function() { this.clicks += 1; }, init: function() { $("#element").click( // ??? ); }};

obj.init();

Page 64: Curso Javascript profesionales

Intermedio: this y arguments

Apaño cada vez más feo:

var obj = { clicks: 0, incClicks: function() { this.clicks += 1; }, init: function() { var that = this; $("#element").click(function() { that.incClicks(); }); }};

Page 65: Curso Javascript profesionales

Intermedio: this y arguments

• bind al rescate

var obj = { clicks: 0, incClicks: function() { this.clicks += 1; }, init: function() { $("#element").click( bind(this, this.incClicks) ); }};

Page 66: Curso Javascript profesionales

intermedio: this y arguments

¿Qué hace esta otra función?

function enigma(fn) { var slice = Array.prototype.slice, args = slice.call(arguments, 1); return function() { var newargs = slice.call(arguments); return fn.apply(this, args.concat(newargs)); };}

Page 67: Curso Javascript profesionales

intermedio: this y arguments

function enigma(fn) { var slice = Array.prototype.slice, args = slice.call(arguments, 1); return function() { var newargs = slice.call(arguments); return fn.apply(this, args.concat(newargs)); };}

typeof enigma(); // ???

Page 68: Curso Javascript profesionales

intermedio: this y arguments

function enigma(fn) { var slice = Array.prototype.slice, args = slice.call(arguments, 1); return function() { var newargs = slice.call(arguments); return fn.apply(this, args.concat(newargs)); };}

var cosa = enigma();typeof cosa(); // ???

Page 69: Curso Javascript profesionales

intermedio: this y arguments

function enigma(fn) { var slice = Array.prototype.slice, args = slice.call(arguments, 1); return function() { var newargs = slice.call(arguments); return fn.apply(this, args.concat(newargs)); };}

var cosa = enigma(function() { return "Hola!";});

cosa(); // ???

Page 70: Curso Javascript profesionales

intermedio: this y arguments

function enigma(fn) { var slice = Array.prototype.slice, args = slice.call(arguments, 1); return function() { var newargs = slice.call(arguments); return fn.apply(this, args.concat(newargs)); };}

function saluda(nombre) { return "Hola, " + nombre + "!";}

var cosa = enigma(saluda);

cosa("Mundo"); // ???

Page 71: Curso Javascript profesionales

intermedio: this y arguments

function enigma(fn) { var slice = Array.prototype.slice, args = slice.call(arguments, 1); return function() { var newargs = slice.call(arguments); return fn.apply(this, args.concat(newargs)); };}

function saluda(nombre) { return "Hola, " + nombre + "!";}

var cosa = enigma(saluda, "Mundo");

cosa(); // ???

Page 72: Curso Javascript profesionales

intermedio: this y arguments

function enigma(fn) { var slice = Array.prototype.slice, args = slice.call(arguments, 1); return function() { var newargs = slice.call(arguments); return fn.apply(this, args.concat(newargs)); };}

function saluda(saludo, nombre) { return saludo + ", " + nombre + "!";}

var cosa = enigma(saluda, "Hola", "Mundo");

cosa(); // ???

Page 73: Curso Javascript profesionales

intermedio: this y arguments

function enigma(fn) { var slice = Array.prototype.slice, args = slice.call(arguments, 1); return function() { var newargs = slice.call(arguments); return fn.apply(this, args.concat(newargs)); };}

function saluda(saludo, nombre) { return saludo + ", " + nombre + "!";}

var cosa = enigma(saluda, "Hola");

cosa("Mundo"); // ???cosa("Don Pepito"); // ???

Page 74: Curso Javascript profesionales

intermedio: this y arguments

function enigma(fn) { var slice = Array.prototype.slice, args = slice.call(arguments, 1); return function() { var newargs = slice.call(arguments); return fn.apply(this, args.concat(newargs)); };}

var dario = {nombre: "Darío"};var elena = {nombre: "Elena"};

function saluda(saludo) { return saludo + ", " + this.nombre + "!";}

var cosa = enigma(saluda, "Qué pasa");

cosa.call(dario); // ???cosa.call(elena); // ???

Page 75: Curso Javascript profesionales

Intermedio: this y arguments

• curry: aplicación parcial de una función

function curry(fn) { var slice = Array.prototype.slice, args = slice.call(arguments, 1); return function() { var newargs = slice.call(arguments); return fn.apply(this, args.concat(newargs)); };}

Page 76: Curso Javascript profesionales

Intermedio: rizar el rizo

var unObj = { nombre: "Manuel", edad: 32};

function getNombre() { return this.nombre; }function setNombre(nombre) { this.nombre = nombre; }function getEdad() { return this.edad; }function setEdad(edad) { this.edad = edad; }

var bindToUnObj = curry(bind, unObj), getUnObjNombre = bindToUnObj(getNombre), setUnObjNombre = bindToUnObj(setNombre);

setUnObjNombre("Pepito");getUnObjNombre(); // ???

Page 77: Curso Javascript profesionales

Intermedio: rizar el rizo

function getter(prop) { return this[prop]; }function setter(prop, value) { this[prop] = value; }

var manuel = { nombre: "Manuel", edad: 32};

var edadDeManuel = bind(manuel, curry(getter, "edad"));edadDeManuel(); // ???

Page 78: Curso Javascript profesionales

Prototipos

Page 79: Curso Javascript profesionales

Prototipos

• ¿Por qué tan mala fama?

• ¡Es un mecanismo muy sencillo!

• Distinto a otros lenguajes

Page 80: Curso Javascript profesionales

Prototipos

Un objeto obj:

qué pasa si hacemos:

var obj = {uno: 1, dos: 2};

obj.uno; // 1

Page 81: Curso Javascript profesionales

Prototipos

var obj = {uno: 1, dos: 2};

obj.uno; // 1

uno 1

dos 2

uno 1

dos 2

obj

obj

Page 82: Curso Javascript profesionales

Prototipos

Si hacemos:

var obj = {uno: 1, dos: 2};

obj.tres; // undefined

uno 1

dos 2

obj

objuno 1dos 2

Not found! undefined

Page 83: Curso Javascript profesionales

Prototipos

¿De dónde sale?

var obj = {uno: 1, dos: 2};

obj.toString(); // '[object Object]'

uno 1

dos 2

obj

objuno 1dos 2

Not found! undefined

¿?

Page 84: Curso Javascript profesionales

Prototipos

obj.toString(); // '[object Object]'

objuno 1

dos 2

prototype Object

toString functionvalueOf function

...Not found! undefined

Object

Page 85: Curso Javascript profesionales

Prototipos

Teniendo:

auno 1dos 2

prototype b

toString functionvalueOf function

...Not found! undefine

d

Objectbtres 3

cuatro 4prototype Object

Page 86: Curso Javascript profesionales

Prototipos

auno 1dos 2

prototype b

toString functionvalueOf function

...Not found! undefine

d

Objectbtres 3

cuatro 4prototype Object

a.uno; // 1

Page 87: Curso Javascript profesionales

Prototipos

auno 1dos 2

prototype b

toString functionvalueOf function

...Not found! undefine

d

Objectbtres 3

cuatro 4prototype Object

a.cuatro; // 4

Page 88: Curso Javascript profesionales

Prototipos

auno 1dos 2

prototype b

toString functionvalueOf function

...Not found! undefine

d

Objectbtres 3

cuatro 4prototype Object

a.toString; // [object Object]

Page 89: Curso Javascript profesionales

Prototipos

auno 1dos 2

prototype b

toString functionvalueOf function

...Not found! undefine

d

Objectbtres 3

cuatro 4prototype Object

a.noExiste; // undefined

Page 90: Curso Javascript profesionales

Prototipos

Pero... ¿Cómo establezco el prototipo de un objeto?

- No se puede hacer directamente

- No se puede modificar el prototipo de objetos literales

- Solo objetos generados (con new)

- Constructores!

Page 91: Curso Javascript profesionales

Repaso: Mensajes

• Una función se puede ejecutar de 4 maneras:

- Invocando directamente la función

- Enviando un mensaje a un objeto (método)

- Como constructor

- Indirectamente, a través de call(...) y apply(...)

(function() { alert("Hey!"); })();

objeto.metodo();

new MiConstructor();

fn.call({}, "param");

Page 92: Curso Javascript profesionales

Repaso: Mensajes

Una función se puede ejecutar de 4 maneras:

Invocando directamente la función

Enviando un mensaje a un objeto (método)

- Como constructor

new MiConstructor();

Page 93: Curso Javascript profesionales

Constructores

• Funciones

• Invocación precedida por new

• Su contexto es un objeto recién generado

• return implícito

• La única manera de manipular prototipos

Page 94: Curso Javascript profesionales

Constructores

function Constructor(param) { // this tiene otro significado! this.propiedad = "una propiedad!"; this.cena = param;}

var instancia = new Constructor("Pollo asado");instancia.propiedad; // una propiedad!instancia.cena; // "Pollo asado"

Page 95: Curso Javascript profesionales

Constructores

function Constructor(param) { // this tiene otro significado! this.propiedad = "una propiedad!"; this.cena = param;}

var instancia = new Constructor("Pollo asado");instancia.propiedad; // una propiedad!instancia.cena; // "Pollo asado"

Page 96: Curso Javascript profesionales

Constructores

3 pasos:

1. Crear un nuevo objeto

2. Prototipo del objeto = propiedadad prototype del constructor

3. El nuevo objeto es el contexto del constructor

Page 97: Curso Javascript profesionales

Constructores

var b = { uno: 1, dos: 2};

function A() { this.tres = 3; this.cuatro = 4;}

A.prototype = b;

var instancia = new A();instancia.tres; // 3instancia.uno; // 1

Page 98: Curso Javascript profesionales

Constructores

var b = { uno: 1, dos: 2};

function A() { this.tres = 3; this.cuatro = 4;}

A.prototype = b;

var instancia = new A();instancia.tres; // 3instancia.uno; // 1

instanciauno 1dos 2

proto b

btres 3

cuatro 4proto Object

Page 99: Curso Javascript profesionales

Constructores

.hasOwnProperty(name)• Distinguir las propiedades heredadas de las propias

• true solo si la propiedad es del objeto

instancia.hasOwnProperty("tres"); // trueinstancia.hasOwnProperty("uno"); // false

Page 100: Curso Javascript profesionales

Constructores

¿Qué pasa aquí?

var comun = { empresa: "ACME" };

function Empleado(nombre) { this.nombre = nombre;}Empleado.prototype = comun;

var pepe = new Empleado("Pepe");

pepe.nombre; // "Pepe"pepe.empresa; // ???

Page 101: Curso Javascript profesionales

Constructores

¿Qué pasa aquí?

var comun = { empresa: "ACME" };

function Empleado(nombre) { this.nombre = nombre;}Empleado.prototype = comun;

var pepe = new Empleado("Pepe");

comun.empresa = "Googlezon";var antonio = new Empleado("Antonio");

antonio.empresa; // ???

Page 102: Curso Javascript profesionales

Constructores

¿Qué pasa aquí?

var comun = { empresa: "ACME" };

function Empleado(nombre) { this.nombre = nombre;}Empleado.prototype = comun;

var pepe = new Empleado("Pepe");

comun.empresa = "Googlezon";var antonio = new Empleado("Antonio");

pepe.empresa; // ???

Page 103: Curso Javascript profesionales

Constructores

pepenombre “Pepe”

proto comun

comunempresa “ACME”

proto Object

var pepe = new Empleado("Pepe");

Page 104: Curso Javascript profesionales

Constructores

pepenombre “Pepe”

proto comun

comunempresa “Googlezone”

proto Object

comun.empresa = "Googlezon";

Page 105: Curso Javascript profesionales

Constructores

pepenombre “Pepe”

proto comun

comunempresa “Googlezone”

proto Object

antonionombre “Antonio”

proto comun

var antonio = new Empleado("Antonio");

Page 106: Curso Javascript profesionales

Constructores

pepenombre “Pepe”

proto comun

comunempresa “Googlezone”

proto Object

antonionombre “Antonio”

proto comun

pepe.empresa;

Page 107: Curso Javascript profesionales

Prototipos

Es decir:

• Las propiedades de los prototipos se comparten!

• Se resuelven dinámicamente

• Modificar un prototipo afecta a todas las instancias anteriores (y futuras)!

Page 108: Curso Javascript profesionales

Intermedio: constructores

¿Cómo hacer que C herede de B que hereda de A?

Page 109: Curso Javascript profesionales

Intermedio: constructores

¿Cómo hacer que C herede de B que hereda de A?

Cuno 1

proto B

tres 3

proto Object

ABdos 2

proto A

var instancia = new C();instancia.tres; // 3

Page 110: Curso Javascript profesionales

Intermedio: constructores

function C() { this.uno = 1;}

var instancia = new C();instancia.tres;

Page 111: Curso Javascript profesionales

Intermedio: constructores

var B = {dos: 2};

function C() { this.uno = 1;}

C.prototype = B;

var instancia = new C();instancia.tres;

Page 112: Curso Javascript profesionales

Intermedio: constructores

var A = {tres: 3};

function B() { this.dos = 2;}B.prototype = A;

function C() { this.uno = 1;}C.prototype = B;

var instancia = new C();instancia.tres;

Page 113: Curso Javascript profesionales

Intermedio: constructores

var A = {tres: 3};

function B() { this.dos = 2;}B.prototype = A;

function C() { this.uno = 1;}C.prototype = B;

var instancia = new C();instancia.dos; // !!!

Page 114: Curso Javascript profesionales

Intermedio: constructores

var A = {tres: 3};

function B() { this.dos = 2;}B.prototype = A;

function C() { this.uno = 1;}C.prototype = B;

typeof C.prototype; // ???C.prototype.dos; // ???

Page 115: Curso Javascript profesionales

Intermedio: constructores

var A = {tres: 3};

function B() { this.dos = 2;}B.prototype = A;

function C() { this.uno = 1;}C.prototype = new B();

var instancia = new C();instancia.tres;

Page 116: Curso Javascript profesionales

Intermedio: constructores

function A() { this.tres = 3;}

function B() { this.dos = 2;}B.prototype = new A();

function C() { this.uno = 1;}C.prototype = new B();

var instancia = new C();instancia.tres;

Page 117: Curso Javascript profesionales

Cadena de prototipos

La herencia en varios niveles necesita:

• Encadenar prototipos

• El prototipo del “sub constructor” ha de ser siempre new Padre()

• Es la única manera de mantener el “padre del padre” en la cadena!

Page 118: Curso Javascript profesionales

Mecanismos de herencia

Page 119: Curso Javascript profesionales

Mecanismos de herencia

• Herencia clásica

• Herencia de prototipos

• Mixins (módulos)

• Extra: herencia funcional

Page 120: Curso Javascript profesionales

Herencia clásica

¿Qué significa?

• Clases!

• El tipo de herencia más común en otros lenguajes

• Encapsulado y visibilidad

Page 121: Curso Javascript profesionales

Herencia clásica

Vamos a empezar por un constructor:

function MiClase() { // ???}

var instancia = new MiClase();

Page 122: Curso Javascript profesionales

Herencia clásica

Las propiedades (públicas)

function MiClase() { this.unaPropiedad = "valor"; this.otraPropiedad = "otro valor";}

var instancia = new MiClase();

Page 123: Curso Javascript profesionales

Herencia clásica

¿Métodos?

Page 124: Curso Javascript profesionales

Herencia clásica

¿Métodos?function MiClase() { this.unaPropiedad = "valor"; this.otraPropiedad = "otro valor"; this.unMetodo = function() { alert(this.unaPropiedad); }; this.otroMetodo = function() { alert(this.otraPropiedad); };}

var instancia = new MiClase();instancia.otroMetodo();

Page 125: Curso Javascript profesionales

Herencia clásica

Mejor así:function MiClase() { this.unaPropiedad = "valor"; this.otraPropiedad = "otro valor";}

MiClase.prototype.unMetodo = function() { alert(this.unaPropiedad);};

MiClase.prototype.otroMetodo = function() { alert(this.otraPropiedad);};

var instancia = new MiClase();instancia.otroMetodo();

Page 126: Curso Javascript profesionales

Herencia clásica

Solo falta... function Superclase() { /* ... */ }

function MiClase() { this.unaPropiedad = "valor"; this.otraPropiedad = "otro valor";}MiClase.prototype = new Superclase();

MiClase.prototype.unMetodo = function() { alert(this.unaPropiedad);};

MiClase.prototype.otroMetodo = function() { alert(this.otraPropiedad);};

Page 127: Curso Javascript profesionales

Herencia clásica

¿Cómo se pueden crear métodos “de clase”?

MiClase.metodoEstatico("hola!");

Page 128: Curso Javascript profesionales

Herencia clásica

¿Cómo se pueden crear métodos “de clase”?

¡Los constructores son objetos!

MiClase.metodoEstatico("hola!");

MiClase.metodoEstatico = function(cadena) { console.log("metodoEstatico:", cadena);}

Page 129: Curso Javascript profesionales

Herencia clásica: Cuidado!

Este esquema tiene varios problemas:

Page 130: Curso Javascript profesionales

Herencia clásica: Cuidado!

Este esquema tiene varios problemas:

• ¡No es fácil invocar al super constructor!

Page 131: Curso Javascript profesionales

Herencia clásica: Cuidado!

function Animal(especie) { this.especie = especie;}

Animal.prototype.getEspecie = function() { return this.especie;}

function Perro(raza) { this.raza = raza;}Perro.prototype = new Animal();

Perro.prototype.describir = function() { return this.getEspecie() + ", de raza " + this.raza;}

Page 132: Curso Javascript profesionales

function Animal(especie) { this.especie = especie;}

Animal.prototype.getEspecie = function() { return this.especie;}

function Perro(raza) { // ??? this.raza = raza;}Perro.prototype = new Animal();

Perro.prototype.describir = function() { return this.getEspecie() + ", de raza " + this.raza;}

Herencia clásica: Cuidado!

Page 133: Curso Javascript profesionales

function Animal(especie) { this.especie = especie;}

Animal.prototype.getEspecie = function() { return this.especie;}

function Perro(raza) { Animal("perro"); this.raza = raza;}Perro.prototype = new Animal();

Perro.prototype.describir = function() { return this.getEspecie() + ", de raza " + this.raza;}

Herencia clásica: Cuidado!

Page 134: Curso Javascript profesionales

function Animal(especie) { this.especie = especie;}

Animal.prototype.getEspecie = function() { return this.especie;}

function Perro(raza) { Animal("perro"); this.raza = raza;}Perro.prototype = new Animal();

Perro.prototype.describir = function() { return this.getEspecie() + ", de raza " + this.raza;}

Herencia clásica: Cuidado!

crea glob. especie!

Page 135: Curso Javascript profesionales

function Animal(especie) { this.especie = especie;}

Animal.prototype.getEspecie = function() { return this.especie;}

function Perro(raza) { this = new Animal("perro"); this.raza = raza;}Perro.prototype = new Animal();

Perro.prototype.describir = function() { return this.getEspecie() + ", de raza " + this.raza;}

Herencia clásica: Cuidado!

Page 136: Curso Javascript profesionales

function Animal(especie) { this.especie = especie;}

Animal.prototype.getEspecie = function() { return this.especie;}

function Perro(raza) { this = new Animal("perro"); this.raza = raza;}Perro.prototype = new Animal();

Perro.prototype.describir = function() { return this.getEspecie() + ", de raza " + this.raza;}

Herencia clásica: Cuidado!

ERROR!

Page 137: Curso Javascript profesionales

function Animal(especie) { this.especie = especie;}

Animal.prototype.getEspecie = function() { return this.especie;}

function Perro(raza) { Animal.call(this, "perro"); this.raza = raza;}Perro.prototype = new Animal();

Perro.prototype.describir = function() { return this.getEspecie() + ", de raza " + this.raza;}

Herencia clásica: Cuidado!

Page 138: Curso Javascript profesionales

Herencia clásica: Cuidado!

Este esquema tiene varios problemas:

• ¡No es fácil invocar al super constructor!

• No es fácil encapsular

Page 139: Curso Javascript profesionales

Herencia clásica: Cuidado!

function MiClase() { this.propPublica = "pública!";}

MiClase.prototype.metPublico = function() { return "público!";}

var instancia = new MiClase();instancia.propPublica; // "pública!"instancia.metPublico();

Page 140: Curso Javascript profesionales

Herencia clásica: Cuidado!

Este esquema tiene varios problemas:

• ¡No es fácil invocar al super constructor!

• No es fácil encapsular...

• ¡Se crea una instancia solo para mantener la cadena de prototipos!

Page 141: Curso Javascript profesionales

Herencia clásica: Cuidado!

function Superclase() { operacionMuyCostosa(); alert(“Oh, no!”);}

function MiClase() { // ...}MiClase.prototype = new Superclase();

Page 142: Curso Javascript profesionales

Herencia clásica?

¿Qué se puede hacer?

Page 143: Curso Javascript profesionales

Herencia clásica?

Hay dos enfoques:

Page 144: Curso Javascript profesionales

Herencia clásica?

Hay dos enfoques:

• El simple

Page 145: Curso Javascript profesionales

El “simple”

“Hagamos una funcioncita!”

function inherits(subClass, superClass) { var F = function() {}; F.prototype = superClass.prototype; subClass.prototype = new F(); subClass.prototype.constructor = subClass; // extra subClass.prototype.superclass = superClass;}

Page 146: Curso Javascript profesionales

El “simple”

function Animal() { }Animal.prototype.mover = function() { console.log("El animal se mueve...");}; Animal.prototype.comer = function() { console.log("¡Ñam!");};

function Perro(raza) { this.superclass.call(this);}inherits(Perro, Animal);

Perro.prototype.comer = function() { console.log("El perro va a por su plato..."); this.superclass.prototype.comer.call(this);};

var p = new Perro("terrier");p.mover();p.comer();p instanceof Perro;

Page 147: Curso Javascript profesionales

El “simple”

function Animal() { }Animal.prototype.mover = function() { console.log("El animal se mueve...");}; Animal.prototype.comer = function() { console.log("¡Ñam!");};

function Perro(raza) { this.superclass.call(this);}inherits(Perro, Animal);

Perro.prototype.comer = function() { console.log("El perro va a por su plato..."); this.superclass.prototype.comer.call(this);};

var p = new Perro("terrier");p.mover();p.comer();p instanceof Perro;

OMG!

Page 148: Curso Javascript profesionales

El “simple”

• Ventajas

- Muy simple de implementar

- Muy ligero

- No añade demasiado ruido

• Inconvenientes

- No soluciona mucho...

- No se “heredan” los métodos/propiedades de clase

- Sigue sin ser cómodo de usar

Page 149: Curso Javascript profesionales

El “simple”

Caso práctico: CoffeeScript

var __hasProp = {}.hasOwnProperty, __extends = function (child, parent) { for (var key in parent) {

if (__hasProp.call(parent, key)) child[key] = parent[key];

} function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };

Page 150: Curso Javascript profesionales

El “simple”

Caso práctico: CoffeeScript

var __hasProp = {}.hasOwnProperty, __extends = function (child, parent) { for (var key in parent) {

if (__hasProp.call(parent, key)) child[key] = parent[key];

} function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };

Page 151: Curso Javascript profesionales

El “simple”

var MiClase = (function(_super) {

__extends(MiClase, _super);

function MiClase() { MiClase.__super__.constructor.apply(this, arguments); this.miPropiedad = 1; }

MiClase.prototype.miMetodo = function() { return MiClase.__super__.miMetodo.call(this, "hola"); };

return MiClase;

})(Superclase);

Page 152: Curso Javascript profesionales

Herencia clásica?

Hay dos enfoques:

• El simple

• El cómodo

Page 153: Curso Javascript profesionales

El cómodo

Más complejo, pero merece la pena

var Persona = Class.extend({ init: function(nombre) { console.log("Bienvenido,", nombre); }});

var Ninja = Persona.extend({ init: function(){ this._super("ninja"); } esgrimirEspada: function(){ console.log("En guardia!"); }});

Page 154: Curso Javascript profesionales

El cómodo

Más complejo, pero merece la pena

var Persona = Class.extend({ init: function(nombre) { console.log("Bienvenido,", nombre); }});

var Ninja = Persona.extend({ init: function(){ this._super("ninja"); } esgrimirEspada: function(){ console.log("En guardia!"); }});

Page 155: Curso Javascript profesionales

Intermedio: klass.js

¡Rellena los huecos!

var Class = function(){};

Class.extend = function(prop) { var _super = this.prototype;

// ...

return Klass;};

Page 156: Curso Javascript profesionales

Intermedio: klass.js

Está muy bien pero...

• No hay métodos de clase (y no se heredan!)

• Todo sigue siendo público

• ¡Es solo una primera versión!

Page 157: Curso Javascript profesionales

El cómodo

¿Cuándo usar este método?

• ¡Siempre que sea posible!

Contras:

• La implementación es más compleja...

• Hay que incluir la librería externa

• No es el enfoque “tradicional”

Page 158: Curso Javascript profesionales

Herencia de prototipos

Vamos a cambiar de marcha...

Page 159: Curso Javascript profesionales

Herencia de prototipos

Vamos a cambiar de marcha...

¡Ahora, sin clases!

Page 160: Curso Javascript profesionales

Herencia de prototipos

Vamos a cambiar de marcha...

¡Ahora, sin clases!

¿¿Cómo puede haber POO sin clases??

Page 161: Curso Javascript profesionales

Herencia de prototipos

Herencia clásica: categorías

-Definir “Persona”

-crear instancias de persona según la definición

-Para hacer más concreto, ¡redefine!

Page 162: Curso Javascript profesionales

Herencia de prototipos

Herencia clásica: categorías

-Definir “Persona”

-crear instancias de persona según la definición

-Para hacer más concreto, ¡redefine!

Herencia de prototipos: ejemplos

-“Como ese de ahí, pero más alto”

-Cualquier objeto concreto puede servir de ejemplo

-Para hacer más concreto, ¡cambia lo que quieras!

Page 163: Curso Javascript profesionales

Herencia de prototipos

var benito = { nombre: "Benito", edad: 36, profesión: "jardinero", saludar: function() { alert("Buen día!"); }, envejecer: function() { this.edad += 1; }};

Page 164: Curso Javascript profesionales

Herencia de prototipos

var benito = { nombre: "Benito", edad: 36, profesión: "jardinero", saludar: function() { alert("Buen día!"); }, envejecer: function() { this.edad += 1; }};

var gonzalo = clone(benito);gonzalo.nombre = "Gonzalo";gonzalo.profesion = "carpintero";gonzalo.saludar(); // Buen día!

Page 165: Curso Javascript profesionales

Herencia de prototipos

var benito = { nombre: "Benito", edad: 36, profesión: "jardinero", saludar: function() { alert("Buen día!"); }, envejecer: function() { this.edad += 1; }};

var gonzalo = clone(benito);gonzalo.nombre = "Gonzalo";gonzalo.profesion = "carpintero";gonzalo.saludar(); // Buen día!

Page 166: Curso Javascript profesionales

Herencia de prototipos

También se puede generalizarvar Animal = { vivo: true, comer: function() { console.log("Ñam, ñam"); }};

var Perro = clone(Animal);Perro.especie = "perro";

var Dogo = clone(Perro);Dogo.raza = "dogo";

var toby = clone(Dogo);toby.nombre = "Toby";

Page 167: Curso Javascript profesionales

Herencia de prototipos

¡Así de simple!

function clone(obj) { function F(){} F.prototype = obj; return new F();}

Page 168: Curso Javascript profesionales

Herencia de prototipos

Herencia clásica vs. de prototipos

• Clásica

✓ MUCHO más extendida y bien comprendida

✓ Mayor catálogo de mecanismos de abstracción

• Prototipos

✓ Uso mucho más eficiente de la memoria

✓ La “auténtica” herencia en JS

✓ Muy simple

Page 169: Curso Javascript profesionales

Herencia de prototipos

Peeero...

• Clásica

๏ Solo se puede emular. Necesario entender los prototipos.

๏ A contrapelo

• Prototipos

๏ Bastante limitada

๏ Lenta con cadenas de prototipos largas!

Page 170: Curso Javascript profesionales

Herencia de prototipos

¿Cuál uso?

Page 171: Curso Javascript profesionales

Herencia de prototipos

¿Cuál uso?

¡Las dos!

Page 172: Curso Javascript profesionales

Intermedio: prototipos

¿Qué significa?

objeto.propiedad;

Page 173: Curso Javascript profesionales

Intermedio: prototipos

¿Qué significa?

“Accede a la propiedad propiedad del objeto objeto”

objeto.propiedad;

Page 174: Curso Javascript profesionales

Intermedio: prototipos

¿Qué significa?

“Accede a la propiedad propiedad del objeto objeto. Si no la encuentras, sigue buscando por la cadena de prototipos.”

objeto.propiedad;

Page 175: Curso Javascript profesionales

Intermedio: prototipos

¿Qué significa?

objeto.propiedad = 1;

Page 176: Curso Javascript profesionales

Intermedio: prototipos

¿Qué significa?

“Guarda el valor 1 en la propiedad propiedad del objeto objeto.”

objeto.propiedad = 1;

Page 177: Curso Javascript profesionales

Intermedio: prototipos

¿Qué significa?

“Guarda el valor 1 en la propiedad propiedad del objeto objeto. Si no la encuentras, créala!”

objeto.propiedad = 1;

Page 178: Curso Javascript profesionales

Intermedio: prototipos

peso 1

proto Object

plumaplomoproto pluma

var pluma = { peso: 1};

var plomo = clone(pluma);

Page 179: Curso Javascript profesionales

Intermedio: prototipos

peso 1

proto Object

plumaplomoproto pluma

plomo.peso; // 1

Page 180: Curso Javascript profesionales

Intermedio: prototipos

peso 1

proto Object

plumaplomoproto pluma

peso 100

plomo.peso = 100;

Page 181: Curso Javascript profesionales

Intermedio: prototipos

Es decir:

• Hay asimetría entre escritura y lectura!

• Lectura: busca en la cadena

• Escritura: crea una nueva propiedad

• Es el comportamiento natural

• Uso eficiente de la memoria: solo se crean los valores diferentes.

Page 182: Curso Javascript profesionales

Intermedio: prototipos

¿Qué sucede?var Lista = { elementos: []};

var laCompra = clone(Lista);

laCompra.elementos.push("Leche");laCompra.elementos.push("Huevos");

var toDo = clone(Lista);toDo.elementos.push("Contestar emails");toDo.elementos.push("Subir a producción");

toDo.elementos;

Page 183: Curso Javascript profesionales

prototipos

Object.create(proto)• Igual que clone

• Nativo en algunos navegadores

var pluma = { peso: 1, enStock: true,};

var plomo = Object.create(pluma);plomo.peso = 100;

Page 184: Curso Javascript profesionales

¡Se acabaron los prototipos!

Page 185: Curso Javascript profesionales

Clausuras

Sólo una idea importante más: ámbitos

Page 186: Curso Javascript profesionales

Clausuras

• Una idea sencilla, pero difícil de explicar

• Están por todas partes

• Consecuencia natural del lenguaje

• ¡Muy útiles!

Page 187: Curso Javascript profesionales

Clausuras

function clausurator() { var a = 1; return function() { return a; };}

Page 188: Curso Javascript profesionales

Clausuras

function clausurator() { var a = 1; return function() { return a; };}

var fn = clausurator();typeof fn;

Page 189: Curso Javascript profesionales

Clausuras

function clausurator() { var a = 1; return function() { return a; };}

var fn = clausurator();fn(); // ???

Page 190: Curso Javascript profesionales

Clausuras

function clausurator() { var a = 1; return function() { return a; };}

var fn = clausurator();fn();

fn = function() { return a;}

Page 191: Curso Javascript profesionales

Clausuras

function clausurator() { var a = 1; return function() { return a; };}

var fn = clausurator();fn();

fn = function() { return a;}

Page 192: Curso Javascript profesionales

Clausuras

function clausurator() { var a = 1; return function() { return a; };}

var fn = clausurator();fn();

fn = function() { return a;}

Page 193: Curso Javascript profesionales

Clausuras

function clausurator() { var a = 1; return function() { return a; };}

var fn = clausurator();fn();

fn = function() { return a;}

a = 1;

Page 194: Curso Javascript profesionales

Clausuras

Otro caso:function makeContador() { var i = 0; return function() { return i++; }}

var contador1 = makeContador();contador1(); // ???contador1(); // ???

var contador2 = makeContador();contador2(); // ???

Page 195: Curso Javascript profesionales

Clausuras

Otro caso:function makeContador() { var i = 0; return function() { return i++; }}

var contador1 = makeContador();contador1(); // 0contador1(); // 1

var contador2 = makeContador();contador2(); // 0

Page 196: Curso Javascript profesionales

Clausuras

function makeContador() { var i = 0; return function() { return i++; }}

var contador1 = makeContador();contador1(); // 0contador1(); // 1

var contador2 = makeContador();contador2(); // 0

contador1 = function() { return i++;}

i = 0;

Page 197: Curso Javascript profesionales

Clausuras

function makeContador() { var i = 0; return function() { return i++; }}

var contador1 = makeContador();contador1(); // 0contador1(); // 1

var contador2 = makeContador();contador2(); // 0

contador1 = function() { return i++;}

i = 1;

Page 198: Curso Javascript profesionales

Clausuras

function makeContador() { var i = 0; return function() { return i++; }}

var contador1 = makeContador();contador1(); // 0contador1(); // 1

var contador2 = makeContador();contador2(); // 0

contador1 = function() { return i++;}

i = 1;

contador2 = function() { return i++;}

i = 0;

Page 199: Curso Javascript profesionales

Clausuras

function interesante() { var algo = 0; return { get: function() { return algo; }, set: function(valor) { return algo = valor; } };}

var obj = interesante();obj.get(); // ???obj.set("hola!");obj.get(); // ???

Page 200: Curso Javascript profesionales

Clausuras

• bind: fija una función a un contexto

function bind(ctx, fn) { return function() { return fn.apply(ctx, arguments); }}

Page 201: Curso Javascript profesionales

Clausuras

• curry: aplicación parcial de una función

function curry(fn) { var slice = Array.prototype.slice, args = slice.call(arguments, 1); return function() { var newargs = slice.call(arguments); return fn.apply(this, args.concat(newargs)); };}

Page 202: Curso Javascript profesionales

Clausuras

Es decir:

• Las clausuras son función + entorno

• Asocian datos a funciones

• No se puede acceder directamente a las variables clausuradas desde el exterior de la función

• Duración indefinida

• ¡Son muy útiles!

Page 203: Curso Javascript profesionales

Intermedio: herencia funcional

function PseudoConstructor() { var self = {}; self.propiedad = "valor"; return self;}

var pseudoInstancia = PseudoConstructor();pseudoInstancia.propiedad; // "valor"

Page 204: Curso Javascript profesionales

Intermedio: herencia funcional

function PseudoConstructor() { var self = {}; self.propiedad = "valor"; self.metodo = function(nombre) { return "No te duermas, " + nombre + "!"; }; return self;}

var pseudoInstancia = PseudoConstructor();pseudoInstancia.metodo("Abraham");

Page 205: Curso Javascript profesionales

Intermedio: herencia funcional

function PseudoConstructor() { var self = {}; self.propiedad = "valor"; self.metodo = function(nombre) { return "No te duermas, " + nombre + "!"; }; return self;}

var pseudoInstancia = PseudoConstructor();pseudoInstancia.metodo("Abraham");

Page 206: Curso Javascript profesionales

Intermedio: herencia funcional

function PseudoConstructor() { var self = {}, propiedad = "privada!"; var metodoPrivado = function(s) { return s.toUpperCase(); } self.metodo = function(nombre) { var mayus = metodoPrivado(nombre); return "No te duermas, " + mayus + "!"; }; return self;}

var pseudoInstancia = PseudoConstructor();pseudoInstancia.metodo("Abraham");

Page 207: Curso Javascript profesionales

Intermedio: herencia funcional

1. Crear un pseudoconstructor

2. Definir una variable self con un objeto vacío

3. Añadir las propiedades/métodos públicos a self

4. Devolver self

Page 208: Curso Javascript profesionales

Intermedio: herencia funcional

¿Y para heredar?

Page 209: Curso Javascript profesionales

Intermedio: herencia funcional

¿Y para heredar?function A() { var self = {}; self.uno = 1; return self;}

function B() { var self = A(); self.dos = 2; return self;}

var b = B();b.uno; // 1

Page 210: Curso Javascript profesionales

Intermedio: herencia funcional

¿Cómo llamar al supermétodo?

Page 211: Curso Javascript profesionales

Intermedio: herencia funcional

function A() { var self = {}; self.metodo = function() { console.log("A"); } return self;}

function B() { var self = A(); var superMetodo = self.metodo; self.metodo = function() { superMetodo(); console.log("B"); } return self;}

Page 212: Curso Javascript profesionales

Intermedio: herencia funcional

“Herencia funcional”:

✓ Explotar clausuras y objetos en linea

✓ Extremadamente simple e intuitivo

✓ Mejor encapsulado público/privado

✓ Poco ruido sintáctico

✓ No hacen falta helpers ni librerías

Page 213: Curso Javascript profesionales

Intermedio: herencia funcional

“Herencia funcional”:

✓ Explotar clausuras y objetos en linea

✓ Extremadamente simple e intuitivo

✓ Mejor encapsulado público/privado

✓ Poco ruido sintáctico

✓ No hacen falta helpers ni librerías

๏ Un poco... ¿cutre?

๏ No es la manera más popular

๏ ¡Peor uso de la memoria!

Page 214: Curso Javascript profesionales

Programación Funcional

made with love by Redradix (www.redradix.com)

Page 215: Curso Javascript profesionales

¿Programación funcional?

La vamos a entender como

• Creación y manipulación de funciones

• Alteración de funciones

• Aplicación de funciones

• Asincronía

Page 216: Curso Javascript profesionales

Funciones de orden superior

Funciones que devuelven funciones

• curry

• bind

• ¡Muchas otras!

Page 217: Curso Javascript profesionales

Funciones de orden superior

Algunas de las más útiles:

• throttle

• debounce

• once

• after

• compose

• memoize

Page 218: Curso Javascript profesionales

throttle

Controlar la frecuencia de invocación

• La función se invocará como máximo una vez

• Durante el periodo de tiempo especificado

Page 219: Curso Javascript profesionales

throttle

var counter = 0, inc = function() { counter++; };

inc = throttle(inc, 10);

for (var i=100000; i--;) { inc();}

alert(counter); // ~6

Page 220: Curso Javascript profesionales

throttle

function throttle(fn, time) { var last = 0; return function() { var now = new Date(); if ((now - last) > time) { last = now; return fn.apply(this, arguments); } }}

Page 221: Curso Javascript profesionales

debounce

Ejecutar la función cuando se deje de llamar

• La llamada se pospone hasta que pasen x ms

• Desde la última invocación

Page 222: Curso Javascript profesionales

debounce

var counter = 0, inc = function() { counter++; alert(counter); };

inc = debounce(inc, 1000);

for (var i=100000; i--;) { inc();}

Page 223: Curso Javascript profesionales

debounce

function debounce(fn, time) { var timerId; return function() { var args = arguments; if (timerId) clearTimeout(timerId); timerId = setTimeout(bind(this, function() { fn.apply(this, args); }), time); }}

Page 224: Curso Javascript profesionales

once

La función solo se puede invocar una vez

var counter = 0, inc = function() { counter++; };

inc = once(inc);

for (var i=100000; i--;) { inc();}

alert(counter);

Page 225: Curso Javascript profesionales

once

function once(fn) { var executed = false; return function() { if (!executed) { executed = true; return fn.apply(this, arguments); } }}

Page 226: Curso Javascript profesionales

after

La función se ejecuta solo tras haber sido invocada n veces

var counter = 0, inc = function() { counter++; };

inc = after(inc, 1000);

for (var i=100000; i--;) { inc();}

alert(counter);

Page 227: Curso Javascript profesionales

after

function after(fn, n) { var times = 0; return function() { times++; if (times % n == 0) { return fn.apply(this, arguments); } }}

Page 228: Curso Javascript profesionales

compose

Composición de funciones

function multiplier(x) { return function(y) { return x*y; }}

var randCien = compose(Math.floor, multiplier(100), Math.random);

alert(randCien());

Page 229: Curso Javascript profesionales

compose

function compose() { var fns = [].slice.call(arguments); return function(x) { var currentResult = x, fn; for (var i=fns.length; i--;) { fn = fns[i]; currentResult = fn(currentResult); } return currentResult; }}

Page 230: Curso Javascript profesionales

memoize

Nunca calcules el mismo resultado 2 veces!

• La primera invocación calcula el resultado

• Las siguientes devuelven el resultado almacenado

• Solo vale para funciones puras

Page 231: Curso Javascript profesionales

memoize

function fact(x) { if (x == 1) { return 1; } else { return x * fact(x-1); }}

fact = memoize(fact);

var start = new Date();fact(100);console.log(new Date() - start);

start = new Date();fact(100);console.log(new Date() - start);

Page 232: Curso Javascript profesionales

memoize

function memoize(fn) { var cache = {}; return function(p) { var key = JSON.stringify(p); if (!(key in cache)) { cache[key] = fn.apply(this, arguments); } return cache[key]; }}

Page 233: Curso Javascript profesionales

Asincronía

JS es, por naturaleza, asíncrono

• Eventos

• AJAX

• Carga de recursos

Page 234: Curso Javascript profesionales

Asincronía

¿Qué significa asíncrono?

function asincrona() { var random = Math.floor(Math.random() * 100); setTimeout(function() { return random; }, random);}

Page 235: Curso Javascript profesionales

Asincronía

¿Cómo devuelvo el valor random desde dentro?

function asincrona() { var random = Math.floor(Math.random() * 100); setTimeout(function() { return random; }, random);}

Page 236: Curso Javascript profesionales

Asincronía

function asincrona(callback) { var random = Math.floor(Math.random() * 1000); setTimeout(function() { callback(random); }, random);}

asincrona(function(valor) { alert(valor);});

Page 237: Curso Javascript profesionales

Asincronía

function asincrona(callback) { var random = Math.floor(Math.random() * 1000); setTimeout(function() { callback(random); }, random);}

asincrona(function(valor) { alert(valor);});

Page 238: Curso Javascript profesionales

Asincronía

function asincrona(callback) { var random = Math.floor(Math.random() * 1000); setTimeout(function() { callback(random); }, random);}

asincrona(function(valor) { alert(valor);});

Page 239: Curso Javascript profesionales

Asincronía

Promesas

• Otra forma de escribir código asíncrono

• Más fácil de manipular

• Más fácil de combinar

Page 240: Curso Javascript profesionales

Asincronía

Promesas

• Una idea muy sencilla:

- Un objeto que representa un estado futuro

• El estado futuro puede ser:

- La resolución de la promesa en un valor

- El rechazo de la promesa con un error

• Mucho, mucho más fácil de manejar que los callbacks

Page 241: Curso Javascript profesionales

Promesas

function onSuccess(data) { /* ... */ }

function onFailure(e) { /* ... */}

var promesa = $.get('/mydata');promesa.then(onSuccess, onFailure);

Page 242: Curso Javascript profesionales

Promesas

promise.then(onSuccess [, onFailure])• En caso de éxito, se invoca a onSuccess con el valor

• En caso de error, se invoca a onFailure

• Devuelve, a su vez, una promesa

Page 243: Curso Javascript profesionales

Promesas

¿Para qué sirven?

• Dar un aspecto más coherente al código

• Hacer más explícito el flow

• Gestionar los errores en cascada

Page 244: Curso Javascript profesionales

Promesas

Parse.User.logIn("user", "pass", { success: function(user) { query.find({ success: function(results) { results[0].save({ key: value }, { success: function(result) { // El objeto se guardó. }, error: function(result, error) { // Error. } }); }, error: function(error) { // Error. } }); }, error: function(user, error) { // Error. }});

Page 245: Curso Javascript profesionales

Promesas

Parse.User.logIn("user", "pass").then(function(user) { return query.find();}).then(function(results) { return results[0].save({ key: value });}).then(function(result) { // El objeto se guardó.}, function(error) { // Error.});

Page 246: Curso Javascript profesionales

Promesas

Parse.User.logIn("user", "pass").then(function(user) { return query.find();}).then(function(results) { return results[0].save({ key: value });}).then(function(result) { // El objeto se guardó.}, function(error) { // Error.});

Page 247: Curso Javascript profesionales

Promesas

Casos: cuando onSuccess devuelve un valor

/* siendo promise una promesa... */

promise.then(function() { return 42;}).then(function(valor) { return "La respuesta es " + valor;}).then(function(mensaje) { console.log(mensaje);});

Page 248: Curso Javascript profesionales

Promesas

Casos: cuando onSuccess devuelve un valor

/* siendo promise una promesa... */

promise.then(function() { return 42;}).then(function(valor) { return "La respuesta es " + valor;}).then(function(mensaje) { console.log(mensaje);});

Page 249: Curso Javascript profesionales

Promesas

Casos: llamando varias a veces a .then

/* siendo promise una promesa... */

promise.then(function() { console.log("primer onSuccess!");});

promise.then(function() { console.log("segundo onSuccess!");});

Page 250: Curso Javascript profesionales

Promesas

Casos: llamando varias a veces a .then

/* siendo promise una promesa... */

promise.then(function() { console.log("primer onSuccess!");}, function(e) { console.log("primer onFailure...");});

promise.then(function() { console.log("segundo onSuccess!");}, function(e) { console.log("segundo onFailure...");});

Page 251: Curso Javascript profesionales

Promesas

Casos: capturar errores

/* siendo promise una promesa... */

promise.then(function() { throw new Error("Oops!");}).then(function() { console.log("Nunca llegamos aquí...");}, function(e) { console.log("Vaya por Dios!"); console.log(e);});

Page 252: Curso Javascript profesionales

Promesas

Casos: capturar errores

/* siendo promise una promesa... */

promise.then(function() { throw new Error("Oops!");}).then(function() { console.log("Nunca llegamos aquí...");}, function(e) { console.log("Vaya por Dios!"); console.log(e);});

Page 253: Curso Javascript profesionales

Promesas

Casos: cascada de errores

/* siendo promise una promesa... */

promise.then(function() { throw new Error("Oh no!");}).then(function() { console.log("Nunca se ejecuta.");}).then(function() { console.log("Esto tampoco.");}, function(e) { console.log("Vaya por Dios!"); console.log(e);});

Page 254: Curso Javascript profesionales

Promesas

Casos: cascada de errores

/* siendo promise una promesa... */

promise.then(function() { throw new Error("Oh no!");}).then(function() { console.log("Nunca se ejecuta.");}).then(function() { console.log("Esto tampoco.");}, function(e) { console.log("Vaya por Dios!"); console.log(e);});

Page 255: Curso Javascript profesionales

Promesas

Casos: errores localizados

/* siendo promise una promesa... */

promise.then(function() { throw new Error("Oh no!");}).then(function() { console.log("Nunca se ejecuta.");}, function(e) { console.log("Manejador del error");}).then(function() { /* ... */}, function(e) { /* este manejador no se ejecuta! */});

Page 256: Curso Javascript profesionales

Promesas

Casos: errores localizados

/* siendo promise una promesa... */

promise.then(function() { throw new Error("Oh no!");}).then(function() { console.log("Nunca se ejecuta.");}, function(e) { console.log("Manejador del error");}).then(function() { /* ... */}, function(e) { /* este manejador no se ejecuta! */});

Page 257: Curso Javascript profesionales

Promesas

¿Cómo creo una promesa?

Page 258: Curso Javascript profesionales

Promesas

Deferreds o diferidos

• Objetos que nos permiten crear y controlar promesas de valores futuros

• Dos operaciones:

- resolve: resuelve la promesa como exitosa

- reject: rechaza la promesa como fracasada

Page 259: Curso Javascript profesionales

Promesas

Promesa DiferidoRepresenta un valor futuro

Controla la generación del valor

onSuccess resolve(valor)

onFailure reject(error)

Page 260: Curso Javascript profesionales

Promesas

function enDiezSegundos() { var diferido = new R.Deferred(); setTimeout(function() { diferido.resolve(new Date()); }, 10*1000); return diferido.promise();}

var promesa = enDiezSegundos();

promesa.then(function(elFuturo) { console.log("Ya han pasado diez segundos!"); console.log(elFuturo.getTime());});

Page 261: Curso Javascript profesionales

Promesas

Deferred#resolve([arg1, arg2, ...])• Resuelve la promesa (ejecuta el callback onSuccess)

• Los parámetros con los que se llame a .resolve()

serán los que reciba el callback onSuccess

• Solo se debería llamar una vez

Page 262: Curso Javascript profesionales

Promesas

Deferred#reject([arg1, arg2, ...])• Rechaza la promesa (ejecuta el callback onFailure)

• Los parámetros con los que se llame a .reject() serán los que reciba el callback onFailure

• Solo se debería llamar una vez

Page 263: Curso Javascript profesionales

Promesas

Deferred#promise()• Devuelve la promesa asociada al diferido

Page 264: Curso Javascript profesionales

Promesas

Deferred#then(onSuccess, onFailure)• Exactamente igual que hacer: deferred.promise().then(...);

Page 265: Curso Javascript profesionales

Promesas

Vamos a crear una librería de promesas

• Una implementación sencilla

• Que satisfaga la especificación Promises/A+- http://promises-aplus.github.com/promises-spec/

• tema2/r-promise/index.html

Page 266: Curso Javascript profesionales

Promesas

Por dónde empezar:

• Poder crear instancias de diferidos

• Poder poner un callback de éxito y uno de fracaso

• .then()- Por ahora, que no devuelva nada

- Solo se puede llamar a una vez por diferido

• .resolve([arg1, ...]) y .reject([arg1, ...])

- Invocan el callback adecuado

- Pasándole los parámetros adecuados

Page 267: Curso Javascript profesionales

Promesas

Siguientes pasos:

• Poder invocar a .then() varias veces

- Es decir, tener varios callbacks para cada caso en un mismo diferido

• Que funcione el primer ejemplo del ejercicio

Lo último a abordar:

• Que las llamadas a .then() se puedan encadenar

• Es decir, que .then() devuelva a su vez una promesa

• Que funcione el segundo ejemplo

Page 268: Curso Javascript profesionales

Promesas

when(pov1 [, pov2, ...])• Dos utilidades:

- Homogeneizar promesas y valores en el código

- Combinar varias promesas/valores

• Devuelve siempre una promesa

• La promesa devuelta:

- Se resolverá si todas las promesas se resuelven.

- Los parámetros del callback son los valores devueltos por cada una de las promesas.

- Se rechazará en caso contrario

Page 269: Curso Javascript profesionales

Promesas

R.Deferred.when(1, 2, 3).then(function(a, b, c) { console.log(a, b, c); // 1 2 3});

Page 270: Curso Javascript profesionales

Promesas

var p1 = new R.Deferred(), p2 = new R.Deferred(), p3 = new R.Deferred();

R.Deferred.when(p1, p2, p3).then(function(a, b, c) { console.log(a, b, c); // 1 2 3});

p1.resolve(1);p2.resolve(2);p3.resolve(3);

Page 271: Curso Javascript profesionales

Promesas

/* Homogeneizar */

var promesaOValor = noSeQueDevuelve();

R.Deferred.when(promesaOValor).then(function(valor) { console.log(valor);});

Page 272: Curso Javascript profesionales

Promesas

/* Homogeneizar */

var valor = 4, promesa = new R.Deferred();

R.Deferred.when(valor, promesa).then(function(a, b) { console.log(a, b); // 4, 5});

promesa.resolve(5);

Page 273: Curso Javascript profesionales

Promesas

var valor = 4, promesa = new R.Deferred();

R.Deferred.when(valor, promesa).then(function(a, b) { console.log(a, b);}, function(e) { alert("Oh, no!");});

promesa.reject("No funciono");

Page 274: Curso Javascript profesionales

Promesas

Implementa R.Deferred.when()• tema2/when/index.html

Page 275: Curso Javascript profesionales

Patrones y principios de diseño

made with love by Redradix (www.redradix.com)

Page 276: Curso Javascript profesionales

Principios de diseño

• SRP: Single Responsibility Principle

- El código de una elemento ha de tener solo una razón para cambiar.

- EL principio de diseño

- También el complementario: cada responsabilidad ha de tener un único lugar en el código (D.R.Y.)

Page 277: Curso Javascript profesionales

SRP

Es común ver cosas como esta:

$.ajax({ ... }) .success(function() { cambioEnInterfaz(); mostrarModal(); if ($("#elemento").value() == "Ok") { /* ... */ } globalSeHaGuardado = true; }) .error(function() { // ... });

Page 278: Curso Javascript profesionales

SRP

O como esta:

var Widget = Class.extend({ onClick: function() { ... }, guardar: function() { ... }, render: function() { ... }, mostrarError: function() { ... }});

Page 279: Curso Javascript profesionales

SRP

var Widget = Model.extend({ guardar: function() { ... }});

var WidgetView = View.extend({ render: function() { ... }});

var WidgetController = Controller.extend({ onClick: function() { ... }});

var ErrorAlert = ModalWindow.extend({ mostrarError: function() { ... }});

Page 280: Curso Javascript profesionales

SRP

Caso práctico: masonry.js

• https://github.com/desandro/masonry/blob/master/jquery.masonry.js

• en el método _create (línea 102)...

Page 281: Curso Javascript profesionales

SRP

!!!!//!sets!up!widget!!!!_create!:!function(!options!)!{!!!!!!//![...]

!!!!!!//!get!original!styles!in!case!we!re3apply!them!in!.destroy()!!!!!!var!elemStyle!=!this.element[0].style;!!!!!!this.originalStyle!=!{!!!!!!!!//!get!height!!!!!!!!height:!elemStyle.height!||!''!!!!!!};!!!!!!//!get!other!styles!that!will!be!overwritten!!!!!!//![...]

3s.isFluid!=!this.options.columnWidth!&&!typeof!this.options.columnWidth!===!'function';

!!!!!!//!add!masonry!class!first!time!around!!!!!!var!instance!=!this;!!!!!!setTimeout(!function()!{!!!!!!!!instance.element.addClass('masonry');!!!!!!},!0!);!!!!!!!!!!!!//!bind!resize!method!!!!!!if!(!this.options.isResizable!)!{!!!!!!!!$(window).bind(!'smartresize.masonry',!function()!{!!!!!!!!!!!instance.resize();!!!!!!!!});!!!!!!}

Page 282: Curso Javascript profesionales

SRP

!!!!//!sets!up!widget!!!!_create!:!function(!options!)!{!!!!!!//![...]

!!!!!!//!get!original!styles!in!case!we!re3apply!them!in!.destroy()!!!!!!var!elemStyle!=!this.element[0].style;!!!!!!this.originalStyle!=!{!!!!!!!!//!get!height!!!!!!!!height:!elemStyle.height!||!''!!!!!!};!!!!!!//!get!other!styles!that!will!be!overwritten!!!!!!//![...]

3s.isFluid!=!this.options.columnWidth!&&!typeof!this.options.columnWidth!===!'function';

!!!!!!//!add!masonry!class!first!time!around!!!!!!var!instance!=!this;!!!!!!setTimeout(!function()!{!!!!!!!!instance.element.addClass('masonry');!!!!!!},!0!);!!!!!!!!!!!!//!bind!resize!method!!!!!!if!(!this.options.isResizable!)!{!!!!!!!!$(window).bind(!'smartresize.masonry',!function()!{!!!!!!!!!!!instance.resize();!!!!!!!!});!!!!!!}

Page 283: Curso Javascript profesionales

SRP

El resultado: caos!

• No hay un lugar claro para cada operación

• Es difícil entender qué hace cada línea

- El “qué” está enterrado en el “cómo”

• Muy complicado de testear

• Difícil de reutilizar

Page 284: Curso Javascript profesionales

SRP

Caso práctico: BrowserQuest

• https://github.com/mozilla/BrowserQuest/blob/master/client/js/chest.js

Page 285: Curso Javascript profesionales

SRP

!!!!var!Chest!=!Entity.extend({!!!!!!!!init:!function(id,!kind)!{!!!!! !!!!this._super(id,!Types.Entities.CHEST);!!!!!!!!},!!!!!!!!!!!!getSpriteName:!function()!{!!!!!!!!!!!!return!"chest";!!!!!!!!},!!!!!!!!!!!!isMoving:!function()!{!!!!!!!!!!!!return!false;!!!!!!!!},!!!!!!!!!!!!open:!function()!{!!!!!!!!!!!!if(this.open_callback)!{!!!!!!!!!!!!!!!!this.open_callback();!!!!!!!!!!!!}!!!!!!!!},!!!!!!!!!!!!onOpen:!function(callback)!{!!!!!!!!!!!!this.open_callback!=!callback;!!!!!!!!}!!!!});

Page 286: Curso Javascript profesionales

SRP

Caso práctico: BrowserQuest

• Todo el proyecto está muy bien estructurado

- entity.js

- character.js

- animation.js

- ...

• A pesar de ser muy grande, cada responsabilidad tiene su sitio

Page 287: Curso Javascript profesionales

SRP

Caso práctico: Backbone.js

• https://github.com/documentcloud/backbone/blob/master/backbone.js

• en Backbone.Model, línea 179...

Page 288: Curso Javascript profesionales

SRP

• Por un lado..

๏ gestión de estado (set, get)

๏ validación

๏ formateo (toJSON, escape)

๏ servidor (fetch, save)

• Por otro...

✓ Delega los detalles a otros módulos (Sync, Event)

✓ Bajo acoplamiento (“interfaces”)

Page 289: Curso Javascript profesionales

SRP

“Una responsabilidad”...

• Subjetivo

• “Una sola razón para cambiar”...

- “Para qué todo funcione bien”

- Muy dependiente del nivel de abstracción

- Y de cada módulo

• El exceso es tan malo como el defecto

Page 290: Curso Javascript profesionales

Principios de diseño

• Tell, Don’t Ask

- “Dime lo que necesitas”

- Claridad y expresividad

- Encapsular las comprobaciones

Page 291: Curso Javascript profesionales

Tell, Don’t Ask

Los síntomas:

!!!!!!!!!!!!!!!!!!!!if!(typeof(variables)!===!'object'!&&!!Array.isArray(variables))!{!!!!!!!!!!!!!!!!!!!!!!!!variables!=!Object.keys(variables).map(function!(k)!{!!!!!!!!!!!!!!!!!!!!!!!!!!!!var!value!=!variables[k];

!!!!!!!!!!!!!!!!!!!!!!!!!!!!if!(!!(value!instanceof!tree.Value))!{!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!if!(!!(value!instanceof!tree.Expression))!{!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!value!=!new(tree.Expression)([value]);!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!}!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!value!=!new(tree.Value)([value]);!!!!!!!!!!!!!!!!!!!!!!!!!!!!}!!!!!!!!!!!!!!!!!!!!!!!!!!!!return!new(tree.Rule)('@'!+!k,!value,!false,!0);!!!!!!!!!!!!!!!!!!!!!!!!});!!!!!!!!!!!!!!!!!!!!!!!!frames!=![new(tree.Ruleset)(null,!variables)];!!!!!!!!!!!!!!!!!!!!}

Page 292: Curso Javascript profesionales

Tell, Don’t Ask

Los síntomas:

!!!!!!!!!!!!!!!!!!!!if!(typeof(variables)!===!'object'!&&!!Array.isArray(variables))!{!!!!!!!!!!!!!!!!!!!!!!!!variables!=!Object.keys(variables).map(function!(k)!{!!!!!!!!!!!!!!!!!!!!!!!!!!!!var!value!=!variables[k];

!!!!!!!!!!!!!!!!!!!!!!!!!!!!if!(!!(value!instanceof!tree.Value))!{!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!if!(!!(value!instanceof!tree.Expression))!{!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!value!=!new(tree.Expression)([value]);!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!}!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!value!=!new(tree.Value)([value]);!!!!!!!!!!!!!!!!!!!!!!!!!!!!}!!!!!!!!!!!!!!!!!!!!!!!!!!!!return!new(tree.Rule)('@'!+!k,!value,!false,!0);!!!!!!!!!!!!!!!!!!!!!!!!});!!!!!!!!!!!!!!!!!!!!!!!!frames!=![new(tree.Ruleset)(null,!variables)];!!!!!!!!!!!!!!!!!!!!}

Page 293: Curso Javascript profesionales

Tell, Don’t Ask

Programación:

• Estructurada: adquiere info y toma decisiones

• OO: manda a los objetos hacer cosas

Page 294: Curso Javascript profesionales

Tell, Don’t Ask

El error:

1. Preguntar a un objeto sobre su estado

2. Tomar una decisión

3. Decirle lo que tiene que hacer

Page 295: Curso Javascript profesionales

Tell, Don’t Ask

El error:

1. Preguntar a un objeto sobre su estado

2. Tomar una decisión

3. Decirle lo que tiene que hacer

¡Probablemente ese código pertenece al objeto!

Page 296: Curso Javascript profesionales

Tell, Don’t Ask

if (usuario.primerLogin) { usuario.mostrarMensajeBienvenida();} else { usuario.mostrarSaludo();}

Page 297: Curso Javascript profesionales

Tell, Don’t Ask

var Usuario = Class.extend({ saludar: function() { if (this.primerLogin) { this.mostrarMensajeBienvenida(); } else { this.mostrarSaludo(); } }});

// y después...

usuario.saludar();

Page 298: Curso Javascript profesionales

Tell, Don’t Ask

function comprobarTimeout(respuesta) { if ((Date.now() - respuesta.start) > 10000) { respuesta.notificarTimeout(); }}

Page 299: Curso Javascript profesionales

Tell, Don’t Ask

var Respuesta = Class.extend({ comprobarTimeout: function() { if ((Date.now() - this.start) > 10000) { this.notificarTimeout(); } }});

// y después...

respuesta.comprobarTimeout();

Page 300: Curso Javascript profesionales

Tell, Don’t Ask

var elementos = miColeccion.getItems();for (var i=0; i<elementos.length; i++) { var elemento = elementos[i]; console.log(elemento.nombre);}

Page 301: Curso Javascript profesionales

Tell, Don’t Ask

miColeccion.forEach(function(e) { console.log(e.nombre);});

Page 302: Curso Javascript profesionales

Tell, Don’t Ask

var elemento = new Elemento("hola", 12);var lista = miColeccion.getItems();lista.addElementAt(elemento.getOrder(), elemento);

Page 303: Curso Javascript profesionales

Tell, Don’t Ask

var elemento = new Elemento("hola", 12);miColeccion.add(elemento);

Page 304: Curso Javascript profesionales

Tell, Don’t Ask

Es decir:

• Los datos y las operaciones sobre esos datos deben estar en el mismo sitio (objeto)

• Encapsular, desacoplar

• “Command/Query Separation”

- Consulta información

- Da una orden y deja al objeto decidir

- Pero no las mezcles!

Page 305: Curso Javascript profesionales

Tell, Don’t Ask

Ventajas:

✓ Más robusto (menor acoplamiento)

✓ Menor tendencia a repetir lógica

✓ Mejor estructurado

Inconvenientes:

๏ Miles de métodos de 2 o 3 líneas

๏ “Ruido” en las clases

Page 306: Curso Javascript profesionales

Principios de diseño

• S.O.L.I.D.

- Single Responsibility

- Open-Closed

- Liskov Substitution

- Interface Segregation

- Dependency Inversion

Page 307: Curso Javascript profesionales

S.O.L.I.D.

Open-Closed

• “Un elemento ha de estar abierto a la extensión pero cerrado a la modificación”

- Abierto a la extensión: poder ser adaptado a las (futuras) necesidades de la aplicación

- Cerrado a la modificación: que la adaptación no implique modificar su código

Page 308: Curso Javascript profesionales

Open-Closed

var Lenguas = { Castellano: 0, Ingles: 1 };

var Persona = Class.extend({ init: function(lengua) { this.lengua = lengua; }, saludar: function(lengua) { if (this.lengua == Lenguas.Castellano) { alert("Hola!"); } else if (this.lengua == Lenguas.Ingles) { alert("Hello!"); } }});

new Persona(Lenguas.Castellano).saludar();

Page 309: Curso Javascript profesionales

Open-Closed

var Persona = Class.extend({ saludar: function() { alert(this.saludo); }});

var Angloparlante = Persona.extend({ init: function() { this.saludo = "Hello!"; }});

var Hispanohablante = Persona.extend({ init: function() { this.saludo = "Hola!"; }});

new Hispanohablante().saludar();

Page 310: Curso Javascript profesionales

Open-Closed

Pretende:

• Promover el uso de abstracciones

• Código modular y flexible ante el cambio

• Evitar un torrente de cambios en cascada!

Page 311: Curso Javascript profesionales

Open-Closed

Pretende:

• Promover el uso de abstracciones

• Código modular y flexible ante el cambio

• Evitar un torrente de cambios en cascada!

Es decir:

• Especificar y respetar interfaces

Page 312: Curso Javascript profesionales

Open-Closed

var Canvas = Class.extend({ render: function(figura) { if (figura instanceof Triangulo) { // ... } else if (figura instanceof Cuadrado) { // ... } }});

Page 313: Curso Javascript profesionales

Open-Closed

var Canvas = Class.extend({ render: function(figura) { figura.draw(this); }});

var Triangulo = Figura.extend({ draw: function(canvas) { ...}});

var Cuadrado = Figura.extend({ draw: function(canvas) { ...}});

Page 314: Curso Javascript profesionales

Open-Closed

Caso práctico: three.js

• https://github.com/mrdoob/three.js/blob/master/src/renderers/CanvasRenderer.js

• método render, línea 225

Page 315: Curso Javascript profesionales

Open-Closed

if!(!element!instanceof!THREE.RenderableParticle!)!{

//!...

}!else!if!(!element!instanceof!THREE.RenderableLine!)!{

//!...

}!else!if!(!element!instanceof!THREE.RenderableFace3!)!{

//!...

}!else!if!(!element!instanceof!THREE.RenderableFace4!)!{

//!...}

Page 316: Curso Javascript profesionales

Open-Closed

Caso práctico: jasmine.js

•https://github.com/pivotal/jasmine/blob/master/src/core/Reporter.js

Page 317: Curso Javascript profesionales

Open-Closed

/**!No3op!base!class!for!Jasmine!reporters.!*!*!@constructor!*/jasmine.Reporter!=!function()!{};

//noinspection!JSUnusedLocalSymbolsjasmine.Reporter.prototype.reportRunnerStarting!=!function(runner)!{};

//noinspection!JSUnusedLocalSymbolsjasmine.Reporter.prototype.reportRunnerResults!=!function(runner)!{};

//...

Page 318: Curso Javascript profesionales

S.O.L.I.D.

Sustitución de Liskov

• “Un objeto debe ser substituible por instancias de sus subclases”

- Si B es subclase de A- Y b es una instancia de B- Se debería poder usar b allí donde se espere un objeto de

clase A

Page 319: Curso Javascript profesionales

Sustitución de Liskov

var Animal = Class.extend({ caminar: function() { /*...*/ }, comer: function() { /* .. */ }});

Page 320: Curso Javascript profesionales

Sustitución de Liskov

var Animal = Class.extend({ caminar: function() { /*...*/ }, comer: function() { /* ... */ }});

var Serpiente = Animal.extend({ /* ... */});

var s = new Serpiente();s.caminar();

Page 321: Curso Javascript profesionales

Sustitución de Liskov

Pretende:

• Promover la reutilización segura de código

• Mantener una semántica coherente

• Si “B es un A”, entonces “B ha de comportarse como A”

Page 322: Curso Javascript profesionales

S.O.L.I.D.

Segregación de la Intefaz

• “muchas interfaces cliente específicas son mejores que una interfaz de propósito general”

- No obligues a un cliente a depender de interfaces que no necesita

- Polución de interfaz

Page 323: Curso Javascript profesionales

Segregación de la intefaz

var Modal = Class.extend({ show: function() { ... }, hide: function() { ... }});

Page 324: Curso Javascript profesionales

Segregación de la intefaz

var Modal = Class.extend({ show: function() { ... }, hide: function() { ... }});

var Timer = Class.extend({ setTimer: function(time) { ... }, startTimer: function() { ... }, onTimeout: function() { ... }});

Page 325: Curso Javascript profesionales

Segregación de la intefaz

var Modal = Class.extend({ show: function() { ... }, hide: function() { ... }});

var Timer = Class.extend({ setTimer: function(time) { ... }, startTimer: function() { ... }, onTimeout: function() { ... }});

var TimedModal = Modal.extend({ init: function() { this.setTimer(100); this.onTimeout = this.hide; this.show(); this.startTimer(); }});

Page 326: Curso Javascript profesionales

Segregación de la intefaz

var Timer = Class.extend({ setTimer: function(time) { ... }, startTimer: function() { ... }, onTimeout: function() { ... }});

var Modal = Timer.extend({ show: function() { ... }, hide: function() { ... }});

var TimedModal = Modal.extend({ init: function() { this.setTimer(100); this.onTimeout = this.hide; this.show(); this.startTimer(); }});

Page 327: Curso Javascript profesionales

Segregación de la intefaz

var WidgetView = Class.extend({ init: function(cont) { var cbind = curry(bind, cont); $("#E1").click(cbind(cont.onE1Click)); }});

Page 328: Curso Javascript profesionales

Segregación de la intefaz

var WidgetView = Class.extend({ init: function(cont) { var cbind = curry(bind, cont); $("#E1").click(cbind(cont.onE1Click)); $("#E2").click(cbind(cont.onE2Click)); $("#E3").click(cbind(cont.onE3Click)); $("#E4").click(cbind(cont.onE4Click)); }});

Page 329: Curso Javascript profesionales

Segregación de la intefaz

var WidgetView = Class.extend({ init: function(cont) { var cbind = curry(bind, cont); $("#E1").click(cbind(cont.onE1Click)); $("#E2").click(cbind(cont.onE2Click)); $("#E3").click(cbind(cont.onE3Click)); $("#E4").click(cbind(cont.onE4Click)); $("#save").click(cbind(cont.save)); $("#reset").click(cbind(cont.reset)); $("#validate").click(cbind(cont.validate)); $("#next-page").click(cbind(cont.getNextPage)); // ... }});

Page 330: Curso Javascript profesionales

Segregación de la intefaz

var WidgetView = Class.extend({ init: function(viewCont, cont, pagCont) { var vbind = curry(bind, cont), cbind = curry(bind, cont), pbind = curry(bind, pagCont); $("#E1").click(vbind(viewCont.onE1Click)); $("#E2").click(vbind(viewCont.onE2Click)); $("#E3").click(vbind(viewCont.onE3Click)); $("#E4").click(vbind(viewCont.onE4Click)); $("#save").click(cbind(cont.save)); $("#reset").click(cbind(cont.reset)); $("#validate").click(cbind(cont.validate)); $("#next-page").click(pbind(pagCont.getNextPage)); // ... }});

Page 331: Curso Javascript profesionales

Segregación de la intefaz

var WidgetView = Class.extend({ init: function(viewCont, cont, pagCont) { var vbind = curry(bind, cont), cbind = curry(bind, cont), pbind = curry(bind, pagCont); $("#E1").click(vbind(viewCont.onE1Click)); $("#E2").click(vbind(viewCont.onE2Click)); $("#E3").click(vbind(viewCont.onE3Click)); $("#E4").click(vbind(viewCont.onE4Click)); $("#save").click(cbind(cont.save)); $("#reset").click(cbind(cont.reset)); $("#validate").click(cbind(cont.validate)); $("#next-page").click(pbind(pagCont.getNextPage)); // ... }});

Page 332: Curso Javascript profesionales

S.O.L.I.D.

Dependency inversion

• “Depende de abstracciones. No dependas de cocreciones”

- Entidades de alto nivel no deben depender de entidades de bajo nivel. Ambos deben depender de abstracciones.

- Las abstracciones no deben depender de detalles. Los detalles deben depender de abstracciones.

Page 333: Curso Javascript profesionales

Dependency Inversion

var Model = Class.extend({ save: function() { var tbind = curry(bind, this), stop = bind(this.icon, this.icon.stop); $.post(this.url, this.getData()) .success(tbind(this.saved)) .error(tbind(this.saveFailed)) .complete(stop); }});

Page 334: Curso Javascript profesionales

Dependency Inversion

var Model = Class.extend({ save: function() { var tbind = curry(bind, this), stop = bind(this.icon, this.icon.stop); $.post(this.url, this.getData()) .success(tbind(this.saved)) .error(tbind(this.saveFailed)) .complete(stop); }});

Page 335: Curso Javascript profesionales

Dependency Inversion

var Model = Class.extend({ init: function(store) { this.store = store; } save: function() { var tbind = curry(bind, this), stop = bind(this.icon, this.icon.stop); this.store.save( this.data, tbind(this.saved), tbind(this.saveFailed), stop ); }});

var Store = Class.extend({ save: function(data, success, error, complete) { }});

Page 336: Curso Javascript profesionales

Dependency Inversion

var ServerStore = Store.extend({ save: function(data, success, error, complete) { $.post(this.url, data) .success(success) .error(error) .complete(complete); }});

var db = {};var MemStore = Store.extend({ save: function(data, success, error, complete) { db[this.url] = data; complete(); success(); }});

Page 337: Curso Javascript profesionales

Dependency Inversion

Caso práctico: backbone.js

• https://github.com/documentcloud/backbone/blob/master/backbone.js

• Backbone.Model#fetch, línea 335

Page 338: Curso Javascript profesionales

Dependency Inversion

!!!!fetch:!function(options)!{!!!!!!options!=!options!?!_.clone(options)!:!{};!!!!!!var!model!=!this;!!!!!!var!success!=!options.success;!!!!!!options.success!=!function(resp,!status,!xhr)!{!!!!!!!!if!(!model.set(model.parse(resp,!xhr),!options))!!!!!!!!!!return!false;!!!!!!!!if!(success)!success(model,!resp,!options);!!!!!!};!!!!!!return!this.sync('read',!this,!options);!!!!},

Page 339: Curso Javascript profesionales

Patrones de organización

Page 340: Curso Javascript profesionales

Patrones de organización

• Parámetros con nombre/por defecto

• Módulos y namespaces

• Control de acceso

• Mixins

Page 341: Curso Javascript profesionales

Parámetros con nombre

function ajax(url, data, method, success, error, complete) { url || url = "/"; data || data = {}; method || method = "POST"; // ...}

ajax("/", {}, "GET", function(){ ... }, function() { ... }, function() { ... });

Page 342: Curso Javascript profesionales

Parámetros con nombre

function ajax(options) { var url = options.url || "/", data = options.data || {}, method = options.method || "POST"; //...}

ajax({data: [1, 2], complete: function() { ... }});

Page 343: Curso Javascript profesionales

Parámetros por defecto

function ajax(options) { var fn = function() {}, defaults = {url: "/", data: [], method: "POST", success: fn, error: fn, complete: fn}; options = merge(defaults, options); // ...}

ajax({data: [1, 2], complete: function() { ... }});

Page 344: Curso Javascript profesionales

Parámetros por defecto

backbone.js:749

!!!!reset:!function(models,!options)!{!!!!!!for!(var!i!=!0,!l!=!this.models.length;!i!<!l;!i++)!{!!!!!!!!this._removeReference(this.models[i]);!!!!!!}!!!!!!this._reset();!!!!!!if!(models)!this.add(models,!_.extend({silent:!true},!options));!!!!!!if!(!options!||!!options.silent)!this.trigger('reset',!this,!options);!!!!!!return!this;!!!!},

Page 345: Curso Javascript profesionales

Intermedio: merge

¿Cómo sería esa función merge?

Page 346: Curso Javascript profesionales

Intermedio: merge

¿Cómo sería esa función merge?

function merge() { var slice = Array.prototype.slice, sources = slice.call(arguments), target = {}; sources.forEach(function(source) { for (var p in source) if (source.hasOwnProperty(p)) { target[p] = source[p]; } }); return target;}

Page 347: Curso Javascript profesionales

Módulos y namespaces

• JavaScript no tiene concepto de namespace

• Todo tirado en objeto global

- Mucha polución

- Colisión de nombres

- Difícil de navegar

Page 348: Curso Javascript profesionales

Módulos y namespaces

• JavaScript no tiene concepto de namespace

• Todo tirado en objeto global

- Mucha polución

- Colisión de nombres

- Difícil de navegar

•¡Pero tenemos funciones!

Page 349: Curso Javascript profesionales

Módulos y namespaces

function miHelper() { // ...}

var miVariableTemporal = 0;var estadoLocal = {};

Page 350: Curso Javascript profesionales

Módulos y namespaces

function sandbox() { function miHelper() { // ... }

var miVariableTemporal = 0; var estadoLocal = {}; }

Page 351: Curso Javascript profesionales

Módulos y namespaces

(function sandbox() { function miHelper() { // ... }

var miVariableTemporal = 0; var estadoLocal = {}; }())

Page 352: Curso Javascript profesionales

Módulos y namespaces

(function sandbox() { function miHelper() { // ... }

var miVariableTemporal = 0; var estadoLocal = {}; }())

Page 353: Curso Javascript profesionales

Módulos y namespaces

function miFuncionUtil() { // ...}

function miGranMetodo() { // ...}

function miEstupendoHelper() { // ...}

Page 354: Curso Javascript profesionales

Módulos y namespaces

function aux() { }var state = "off";

function miFuncionUtil() { // ...}

function miGranMetodo() { // ...}

function miEstupendoHelper() { // ...}

Page 355: Curso Javascript profesionales

Módulos y namespaces

(function() { function aux() { } var state = "off";

function miFuncionUtil() { }

function miGranMetodo() { }

function miEstupendoHelper() { }}())

Page 356: Curso Javascript profesionales

Módulos y namespaces

var Modulo = (function() { function aux() { } var state = "off";

function miFuncionUtil() { }

function miGranMetodo() { } return { miFuncionUtil: miFuncionUtil, miGranMetodo: miGranMetodo };}());

Page 357: Curso Javascript profesionales

Módulos y namespaces

var Modulo = (function() { function aux() { } var state = "off";

function miFuncionUtil() { }

function miGranMetodo() { } return { miFuncionUtil: miFuncionUtil, miGranMetodo: miGranMetodo };}());

Page 358: Curso Javascript profesionales

Módulos y namespaces

Modulo.miFuncionUtil();

Page 359: Curso Javascript profesionales

Módulos y namespaces

var Modulo = {};

(function(Modulo) { function aux() { } var state = "off";

Modulo.miFuncionUtil = function() { }

Modulo.miGranMetodo = function() { }

}(Modulo));

Page 360: Curso Javascript profesionales

Módulos y namespaces

var Modulo = {};

(function(Modulo) { function aux() { } var state = "off"; Modulo.miFuncionUtil = function() { }}(Modulo));

(function(Modulo) { Modulo.miGranMetodo = function() { }}(Modulo));

Page 361: Curso Javascript profesionales

Módulos y namespaces

var Modulo = (function(Modulo) { function aux() { } var state = "off"; Modulo.miFuncionUtil = function() { } return Modulo;}(Modulo || {}));

var Modulo = (function(Modulo) { Modulo.miGranMetodo = function() { } return Modulo;}(Modulo || {}));

Page 362: Curso Javascript profesionales

Módulos y namespaces

var Modulo = (function(Modulo) { function aux() { } var state = "off"; Modulo.miFuncionUtil = function() { } return Modulo;}(Modulo || {}));

var Modulo = (function(Modulo) { Modulo.miGranMetodo = function() { } return Modulo;}(Modulo || {}));

Page 363: Curso Javascript profesionales

Módulos y namespaces

Una truco más sofisticado:

• Tenemos un módulo

• Al que añadimos propiedades en varios ficheros

• Queremos compartir cierta información entre ficheros

• Pero que no sea accesible una vez terminada la carga

Page 364: Curso Javascript profesionales

Módulos y namespaces

var Modulo = (function(Modulo) { _private.password = "1234";}(Modulo || {}));

var Modulo = (function(Modulo) { Modulo.login = function(pass) { return pass == _private.password; };}(Modulo || {}));

Modulo._private; // undefined

Page 365: Curso Javascript profesionales

Módulos y namespaces

var Modulo = (function (Modulo) { var _private = Modulo._private = (Modulo._private || {}), _seal = Modulo._seal = (Modulo._seal || function () { delete Modulo._private; delete Modulo._seal; });

// acceso permanente a _private y _seal return Modulo;}(Modulo || {}));

Page 366: Curso Javascript profesionales

Módulos y namespaces

var Modulo = (function (Modulo) { var _private = Modulo._private; // acceso a _private

}(Modulo || {}));

Page 367: Curso Javascript profesionales

Módulos y namespaces

Modulo._seal();

Page 368: Curso Javascript profesionales

Intermedio: mejorar klass.js

Convierte klass.js en un módulo Class

Page 369: Curso Javascript profesionales

Módulos y namespaces

Submódulos: muy fácil!

var MiLibreria = {};

MiLibreria.eventos = (function(eventos) { eventos.on = function() { }; eventos.off = function() { }; return eventos;}(MiLibreria.eventos));

Page 370: Curso Javascript profesionales

Módulos y namespaces

Según crece la aplicación...

Page 371: Curso Javascript profesionales

Módulos y namespaces

Según crece la aplicación...

var MiLibreria = MiLibreria || {};

Page 372: Curso Javascript profesionales

Módulos y namespaces

Según crece la aplicación...

var MiLibreria = MiLibreria || {};MiLibreria.widgets = MiLibreria.widgets || {};

Page 373: Curso Javascript profesionales

Módulos y namespaces

Según crece la aplicación...

var MiLibreria = MiLibreria || {};MiLibreria.widgets = MiLibreria.widgets || {};MiLibreria.widgets.buttons = MiLibreria.widgets.buttons || {};

MiLibreria.widgets.buttons.actionButtons = (function(buttons) { buttons.ok = new Widget({ ... }); buttons.cancel = new Widget({ ... });}(MiLibreria.widgets.buttons.actionButtons || {}));

Page 374: Curso Javascript profesionales

Módulos y namespaces

Namespaces, pero más cómodos:

MiLib.namespace('widgets.buttons.actionButtons', function(my) { my.ok = new Widget({ ... }); my.cancel = new Widget({ ... });});

Page 375: Curso Javascript profesionales

Intermedio: namespace

¿Como sería la función MiLib.namespace?

Page 376: Curso Javascript profesionales

Intermedio: namespace

¿Como sería la función MiLib.namespace?

var MiLib = (function(my) { my.namespace = function(string, sandbox) { // ??? }; return my;}(MiLib || {}));

Page 377: Curso Javascript profesionales

Intermedio: namespace

¿Como sería la función MiLib.namespace?

var MiLib = (function(my) { my.namespace = function(string, sandbox) { var spaces = string.split('.'), root = my, space; while (space = spaces.shift()) { root = root[space] || (root[space] = {}); } return sandbox(root); }; return my;}(MiLib || {}));

Page 378: Curso Javascript profesionales

Mixins

• Otra forma de reutilizar código

• Sin las limitaciones de la herencia

• Para código de propósito general

• Algo similar a herencia múltiple

Page 379: Curso Javascript profesionales

Mixins

var Mixin = function() {};Mixin.prototype = { inspect: function() { var output = []; for(key in this) { output.push(key + ': ' + this[key]); } return output.join(', '); }};

Page 380: Curso Javascript profesionales

Mixins

var Persona = Class.extend({ init: function(nombre) { this.nombre = nombre; }});

augment(Persona, Mixin);

var pepito = new Persona("Pepito");pepito.inspect();

Page 381: Curso Javascript profesionales

Mixins

function augment(target, source) { for (var prop in source.prototype) { if(!target.prototype[prop]) { target.prototype[prop] = source.prototype[prop]; } }}

Page 382: Curso Javascript profesionales

Intermedio: Mejores Mixins

Lo podemos hacer mejor!

• Mejor integración con klass.js

• Callbacks de inclusión (estilo ruby)

Page 383: Curso Javascript profesionales

Intermedio: Mejores Mixins

var StaticModule = { propiedadDeClase: "Soy una propiedad de clase", included: function(klass) { console.log("Includido!"); }};

var InstanceModule = { diHola: function() { alert("HOLA!"); }, mixed: function(klass) { console.log("Mezclado!"); }};

var MiClase = Class.extend({ init: function() { }});

MiClase.include(StaticModule);MiClase.mixin(InstanceModule);

Page 384: Curso Javascript profesionales

Patrones de creación de objetos

Page 385: Curso Javascript profesionales

Factoría

Delegar la creación de un objeto

• Elegir el constructor dinámicamente

• Procesos de construcción complejos

• Desacoplar detalles de implementación

Page 386: Curso Javascript profesionales

Factoría

var locales = { es: {header: {title: "Mi Título"}}, en: {header: {title: "My Title"}}};

var I18n = Class.extend({ translate: function(path) { var position = locales[this.locale], path = path.split('.'),

currentPath; while (currentPath = path.shift()) { position = position[currentPath]; } return position; }});

var english = new I18n();english.locale = "en";english.translate('header.title'); // “My Title”

Page 387: Curso Javascript profesionales

Factoría

var GlobalConfig = {locale: "es"};

Page 388: Curso Javascript profesionales

Factoría

var GlobalConfig = {locale: "es"};

I18n.withCurrentLocale = function() { var instance = new this; instance.locale = GlobalConfig.locale; return instance;};

Page 389: Curso Javascript profesionales

Factoría

var i18n = I18n.withCurrentLocale();alert(i18n.translate('header.title'));

Page 390: Curso Javascript profesionales

Factoría

var Events = Class.extend({ on: function(event, cb) { }, off: function(event, cb) { }});

var IEEvents = Events.extend({ on: function(event, cb) { }, off: function(event, cb) { }});

Events.getInstance = function() { if (checkForIExplorer()) { return new IEEvents(); } else { return new Events(); }};

Page 391: Curso Javascript profesionales

Factoría

Controlar las instanciasvar Enemigo = (function() { var enemigos = []; var Enemigo = Class.extend({ /*...*/ }); Enemigo.crear = function() { var instance; if (enemigos.length < 5) { instance = new Enemigo(); enemigos.push(instance); return instance; } else { throw new Error("No puede haber más!"); } } return Enemigo;}());

Page 392: Curso Javascript profesionales

Factoría

var Recurso = (function() { var libres = []; var Recurso = Class.extend({ liberar: function() { libres.push(this); } }); Recurso.crear = function() { if (libres.length > 0) { return libres.pop(); } else { return new Recurso(); } } return Recurso;}());

Page 393: Curso Javascript profesionales

Factoría

¿Cuándo usar factorías?

• La construcción de un objeto es compleja

• Seleccionar el constructor adecuado según entorno

• Necesitamos controlar el instanciado

Page 394: Curso Javascript profesionales

Singleton

Clase con una única instancia

• Un tipo peculiar de Factoría

• Cuando no tiene sentido más de una instancia

Page 395: Curso Javascript profesionales

Intermedio: I18n.js

Librería de internacionalización

var Config = {locale: 'en'};

I18n.addTranslation('en', {header: {title: "My Title"}});I18n.addTranslation('es', {header: {title: "Mi Título"}});

var i18n = I18n.withCurrentLocale();alert(i18n.translate('header.title'));

// Singleton!console.log(i18n === I18n.withCurrentLocale())

Page 396: Curso Javascript profesionales

Patrones de abstracción

Page 397: Curso Javascript profesionales

Patrones de abstracción

• Iteradores

• Decorador

• Fachada

• Estrategia

• Inyección de dependencias

• Proxy

Page 398: Curso Javascript profesionales

Iteradores

Recorrer una colección

• Sin revelar detalles de implementación

• Mayor control sobre la iteración

Page 399: Curso Javascript profesionales

Iteradores

var ListadoAlumnos = Class.extend({ init: function() { this.alumnos = []; }, add: function(nombre, ciudad) { this.alumnos.push({ nombre: nombre, ciudad: ciudad }); }});

Page 400: Curso Javascript profesionales

Iteradores

var ListadoAlumnos = Class.extend({ init: function() { this.alumnos = []; }, add: function(nombre, ciudad) { this.alumnos.push({ nombre: nombre, ciudad: ciudad }); }});

var lista = new ListadoAlumnos();lista.add("Gonzalo", "Madrid");lista.add("Gerardo", "Madrid");lista.add("Guzman", "Valencia");

Page 401: Curso Javascript profesionales

Iteradores

var lista = new ListadoAlumnos();lista.add("Gonzalo", "Madrid");lista.add("Gerardo", "Madrid");lista.add("Guzman", "Valencia");

var alumnos = lista.alumnos;for (var i=0; i<alumnos.length; i++) { if (alumnos[i].ciudad == "Madrid") { console.log(alumnos[i].nombre); }}

Page 402: Curso Javascript profesionales

Iteradores

var lista = new ListadoAlumnos();lista.add("Gonzalo", "Madrid");lista.add("Gerardo", "Madrid");lista.add("Guzman", "Valencia");

var alumnos = lista.alumnos;for (var i=0; i<alumnos.length; i++) { if (alumnos[i].ciudad == "Madrid") { console.log(alumnos[i].nombre); }}

Page 403: Curso Javascript profesionales

Iteradores

var ListadoAlumnos = Class.extend({ init: function() { this.alumnos = []; }, add: function(nombre, ciudad) { this.alumnos.push({ nombre: nombre, ciudad: ciudad }); }, forEachIn: function(ciudad, fn) { for (var i=0; i<this.alumnos.length; i++) if (this.alumnos[i].ciudad == ciudad) { fn(this.alumnos[i].nombre); } }});

Page 404: Curso Javascript profesionales

Iteradores

var lista = new ListadoAlumnos();lista.add("Gonzalo", "Madrid");lista.add("Gerardo", "Madrid");lista.add("Guzman", "Valencia");

lista.forEachIn("Madrid", function(n) { console.log(n);});

Page 405: Curso Javascript profesionales

Iteradores

var ListadoAlumnos = Class.extend({ init: function() { this.alumnos = []; }, add: function(nombre, ciudad) { this.alumnos.push({ nombre: nombre, ciudad: ciudad }); }, next: function() { this.index = this.index || 0; return this.alumnos[this.index++]; }});

Page 406: Curso Javascript profesionales

Iteradores

var lista = new ListadoAlumnos();lista.add("Gonzalo", "Madrid");lista.add("Gerardo", "Madrid");lista.add("Guzman", "Valencia");

var alumno;while(alumno = lista.next()) { alert(alumno.nombre);}

Page 407: Curso Javascript profesionales

Iteradores

var Iterator = Class.extend({ init: function(collection) { this.col = collection; this.pos = 0; }, next: function() { return this.col[this.pos++]; }});

var ListadoAlumnos = Class.extend({ init: function() { this.alumnos = []; }, add: function(nombre, ciudad) { this.alumnos.push({ nombre: nombre, ciudad: ciudad }); }, getIterator: function() { return new Iterator(this.alumnos); }});

Page 408: Curso Javascript profesionales

Iteradores

var lista = new ListadoAlumnos();lista.add("Gonzalo", "Madrid");lista.add("Gerardo", "Madrid");lista.add("Guzman", "Valencia");

var alumno, iter = lista.getIterator();while(alumno = iter.next()) { if (alumno.ciudad == "Madrid") { console.log(alumno.nombre); }}

Page 409: Curso Javascript profesionales

Iteradores

var Fibonacci = Class.extend({ init: function() { this.a = 0; this.b = 1; }, next: function() { var next = this.a; this.a = this.b; this.b = this.b + next; return next; }});

var iter = new Fibonacci();for (var i=10; i--;) { console.log(iter.next());}

Page 410: Curso Javascript profesionales

Decorador

Añadir funcionalidad dinámicamente

• Evitar subclases innecesarias

• Modificar comportamientos “en vivo”

• Manteniendo la interfaz/contrato! (transparente)

• Muy apropiada para JS

Page 411: Curso Javascript profesionales

Decorador

var Producto = Class.extend({ init: function(precio) { this.precio = precio; }, getPrecio: function() { return this.precio; }});

Page 412: Curso Javascript profesionales

Decorador

var Producto = Class.extend({ init: function(precio) { this.precio = precio; }, getPrecio: function() { return this.precio; }});

var ProductoConIVA = Producto.extend({ init: function(precio) { this._super(precio); }, getPrecio: function() { return this._super() * 1.21; }});

Page 413: Curso Javascript profesionales

Decorador

var ProductoConDescuento = Producto.extend({ init: function(precio) { this._super(precio); }, getPrecio: function() { return this._super() * 0.90; }});

var ProductoConIVAConDescuento = ProductoConIVA.extend({ init: function(precio) { this._super(precio); }, getPrecio: function() { return this._super() * 0.90; }});

Page 414: Curso Javascript profesionales

Decorador

var ProductoTecnologico = Producto.extend({ });

var ProductoTecnologicoConDescuento = ProductoConDescuento.extend({});

var ProductoTecnologicoConIVA = ProductoConIVA.extend({});

var ProductoTecnologicoConIVAConDescuento = ProductoConIVAConDescuento.extend({});

Page 415: Curso Javascript profesionales

Decorador

var Producto = Class.extend({ init: function(precio) { this.precio = precio; }, getPrecio: function() { return this.precio; }});

Page 416: Curso Javascript profesionales

Decorador

var Producto = Class.extend({ init: function(precio) { this.precio = precio; }, getPrecio: function() { return this.precio; }});

var ProductoConIVA = Class.extend({ init: function(producto) { this.producto = producto; }, getPrecio: function() { return this.producto.getPrecio() * 1.21; }});

Page 417: Curso Javascript profesionales

Decorador

var ProductoConDescuento = Class.extend({ init: function(producto) { this.producto = producto; }, getPrecio: function() { return this.producto.getPrecio() * 0.90; }});

var ProductoTecnologico = Class.extend({ init: function(producto) { this.producto = producto; }, getPrecio: function() { return this.producto.getPrecio() * 0.90; }});

Page 418: Curso Javascript profesionales

Decorador

var producto = new Producto(20);producto = new ProductoConIVA(producto);producto = new ProductoConDescuento(producto);producto = new ProductoTecnologico(producto);producto.getPrecio(); // 19.602

Page 419: Curso Javascript profesionales

Decorador

var producto = new Producto(20);producto = new ProductoConIVA(producto);producto = new ProductoConDescuento(producto);producto = new ProductoTecnologico(producto);producto.getPrecio(); // 19.602

ProductoTecnológico

ProductoConDescuentoProductoConIVA

ProductogetPrecio()

getPrecio()

getPrecio()

getPrecio()

Page 420: Curso Javascript profesionales

Decorador

var producto = new Producto(20);producto = new ProductoConIVA(producto);producto = new ProductoConDescuento(producto);producto = new ProductoTecnologico(producto);producto.getPrecio(); // 19.602

ProductoTecnológico

ProductoConDescuentoProductoConIVA

Producto

(20 * 1.21) * 0.90

20 * 1.21

20

((20 * 1.21) * 0.90) * 0.90

19.602

Page 421: Curso Javascript profesionales

Decorador

Cuando usar decoradores:

• Modificar el comportamiento manteniendo interfaz

• Añadir post- o preproceso a la salida/entrada de un obj

- Serialización

- Interfaz

• Dentro de factorías

• Intervenir el llamadas a métodos

Page 422: Curso Javascript profesionales

Fachada

Simplificar el interfaz y desacoplar al cliente

• Patrón muy simple

• El mejor amigo del programador JavaScript

• Casi todas las librerías son fachadas

• Comodidad

Page 423: Curso Javascript profesionales

Fachada

Ejemplo por excelencia: jQuery

• $(“selector”)• $(“selector”).click(...)• $.ajax• ...

Page 424: Curso Javascript profesionales

Fachada

Nuestra librería klass.js

• Una capa sobre los mecanismos nativos

• Simplifica el “interfaz”

• Mucho más cómodo que hacerlo a mano

Page 425: Curso Javascript profesionales

Fachada

var DOMNode = Class.extend({ init: function(tag) { this.el = document.createElement(tag); }, setClass: function(klass) { this.el.className += (" " + klass); }, setId: function(id) { this.el.id = id; }, getElement: function() { return this.el; }});

var node = new DOMNode('div');node.setClass("big");node.setClass("red");node.setId("my-button");document.body.appendChild(node.getElement());

Page 426: Curso Javascript profesionales

Fachada

DOMNode.create = function(tag, classes, id) { var node = new this(tag); node.setClass(classes.join(" ")); node.setId(id); return node.getElement();}var el = DOMNode.create("div", ["big", "red"], "my-button");document.body.appendChild(el);

Page 427: Curso Javascript profesionales

Fachada

¿Cuándo usar fachadas?

• Clases muy potentes y complejas

- Simplificar los usos más comunes

• Operaciones con mucha preparación

- XMLHttpRequest

• Código repetitivo

• Para cambios grandes del código

Page 428: Curso Javascript profesionales

Estrategia

Seleccionar algoritmos dinámicamente

• Dada una familia de algoritmos

• Encapsulados bajo un mismo interfaz

• Elegir el más apropiado

Page 429: Curso Javascript profesionales

Estrategia

var validate = (function() { var validators = { email: function(value) { return /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\ ".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA -Z\-0-9]+\.)+[a-zA-Z]{2,}))$/.test(value); }, number: function(value) { return /^\d+$/.test(value); } }; return function(data) { var validation = validators[data.type]; return validation && validation(data.value); };}());

validate({type: "email", value: "[email protected]"});

Page 430: Curso Javascript profesionales

Estrategia

var MoodConsole = Class.extend({ init: function() { this.state = "normal"; this.filters = { normal: new DummyFilter(), relajado: new RelaxedFilter(), cabreado: new AngryFilter() }; }, log: function(msg) { msg = this.filters[this.state].filter(msg); console.log(msg); }, molestar: function() { this.state = "cabreado"; }, masajear: function() { this.state = "relajado"; }, dejarEnPaz: function() { this.state = "normal"; }});

Page 431: Curso Javascript profesionales

Estrategia

var DummyFilter = Class.extend({ filter: function(s) { return s; }});

var AngryFilter = DummyFilter.extend({ filter: function(s) { return s.toUpperCase() + "!!"; }});

var RelaxedFilter = DummyFilter.extend({ filter: function(s) { return "..." + s + "..."; }});

var moodConsole = new MoodConsole();moodConsole.log("hola!");moodConsole.molestar();moodConsole.log("grrr...");moodConsole.masajear();moodConsole.log("gracias");

Page 432: Curso Javascript profesionales

Estrategia

¿Cuándo usar estrategias?

• Una misma acción puede tener varios significados

- Según un estado variable (selector de contexto)

- click, touch, una botón “Guardar”, etc..

• Validaciones

• En general: cuando hay que elegir un algoritmo diferente para cada caso

Page 433: Curso Javascript profesionales

Inyección de dependencias

Selección dinámica de un componente

• Según el principio de Inversión de Dependencias

• El cliente consume siguiendo un interfaz

• La clase concreta se selecciona en tiempo de ejecución

Page 434: Curso Javascript profesionales

Inyección de dependencias

var View = Class.extend({ init: function(renderer, model) { this.renderer = renderer; this.model = model; }, render: function() { this.renderer.render(this.template, this.model); }});

var HtmlRenderer = Class.extend({ render: function() { ... }});

var XMLRenderer = Class.extend({ render: function() { ... }});

Page 435: Curso Javascript profesionales

Inyección de dependencias

var miVista = new View(new XMLRenderer('fast'), {});

Page 436: Curso Javascript profesionales

Proxy

Un objeto hace de interfaz de otro objeto o recurso

• Entre el cliente y el servicio

• Controlar y optimizar el acceso

• El Proxy implementa exactamente el mismo interfaz

- No es un decorador (no añade funcionalidad)

- No es una fachada (no simplifica)

Page 437: Curso Javascript profesionales

Proxy

Tres tipos de proxy:

• Virtual Proxy

- Sustituye al objeto real mientras carga

- Optimización

• Protection Proxy

- Controla el acceso

- Seguridad

• Remote Proxy

- Reflejo de un recurso remoto

- Abstracción

Page 438: Curso Javascript profesionales

Virtual Proxy

var Recurso = Class.extend({ init: function() { operacionMuyCostosa(); }, read: function() { ... }, write: function() { ... }});

Page 439: Curso Javascript profesionales

Virtual Proxy

var Recurso = Class.extend({ init: function() { operacionMuyCostosa(); }, read: function() { ... }, write: function() { ... }});

var RecursoProxy = Recurso.extend({ init: function() { this.super_init = this._super; }, read: function() { this.super_init(); this._super(); }, write: function() { this.super_init(); this._super(); }});

Page 440: Curso Javascript profesionales

Virtual Proxy

var Recurso = Class.extend({ init: function() { operacionMuyCostosa(); if (this.onLoad) this.onLoad(); }, write: function() { ... }});

var RecursoProxy = Recurso.extend({ init: function() { this.pending = []; this.loaded = false; this._super(); }, write: function() { if (loaded) { this._super(); } else { pending.push({op: 'write', params: arguments}); } }});

Page 441: Curso Javascript profesionales

Remote Proxy

var User = Class.extend({ init: function() { /*...*/ }, getName: function() { return this.name; }});

User.find = function(id, cb) { $.get('/user/' + id, function(data) { var user = new User(data); cb(user); });};

User.find(12, function(user) { console.log(user.getName());});

Page 442: Curso Javascript profesionales

Proxy

¿Cuándo usar un Proxy?

• Inicialización perezosa

• “Sustituto” que anote mientras el sujeto real arranca

• Cuando el sujeto real es remoto

• Cache!

Page 443: Curso Javascript profesionales

Patrones de interacción

Page 444: Curso Javascript profesionales

Patrones de interacción

• Pub/Sub u Observador

• Mediator

• Comandos y Cadena de Responsabilidad

• “Hydra”

Page 445: Curso Javascript profesionales

Observador

En vez de preguntar, escucha lo que sucede

• Muy común en JavaScript (eventos)

• Reaccionar ante cambios de estado o sucesos

• Dos entidades

- Publicador

- Suscriptor(es)

Page 446: Curso Javascript profesionales

Observador

var Input = Class.extend({ getValue: function() { return this.value; }});

var InputView = Class.extend({ init: function(input) { this.input = input; this.value = this.input.getValue(); setInterval(bind(this, this.onTimer), 100); }, onTimer: function() { var newValue = this.input.getValue(); if (newValue !== this.value) { this.value = newValue; console.log("CHANGED: " + this.value); } }});

Page 447: Curso Javascript profesionales

Observador

InputInputViewcambios?

no..

InputInputViewcambios?

no..

InputInputViewcambios?

no..

InputInputViewcambios?

no..

0ms

100ms

200ms

300ms

Page 448: Curso Javascript profesionales

Observador

InputInputViewavísame!

InputInputViewcambios!

0ms

T ms

Page 449: Curso Javascript profesionales

Observador

var Input = Class.extend({ init: function() { this.observers = []; }, getValue: function() { return this.value; }, setValue: function(v) { this.value = v; this.publish(v); }, onChange: function(cb) { this.observers.push(cb); }, publish: function(cb) { this.observers.forEach(function(o) { o(cb); }); }});

Page 450: Curso Javascript profesionales

Observador

var Input = Class.extend({ init: function() { this.observers = []; }, getValue: function() { return this.value; }, setValue: function(v) { this.value = v; this.publish(v); }, onChange: function(cb) { this.observers.push(cb); }, publish: function(cb) { this.observers.forEach(function(o) { o(cb); }); }});

Page 451: Curso Javascript profesionales

Observador

var InputView = Class.extend({ init: function(input) { this.input = input; this.input.onChange(bind(this, this.update)); }, update: function(newValue) { console.log("CHANGED: " + newValue); }});

Page 452: Curso Javascript profesionales

Observador

var input = new Input();

var observer1 = new InputView(input);var observer2 = new InputView(input);var observer3 = new InputView(input);

input.setValue("No te duermas!");

Page 453: Curso Javascript profesionales

Intermedio: Observable

¡Podemos hacerlo mejor!

Input.mixin(Observable);

var InputView = Class.extend({ init: function(input) { this.input.subscribe('change', bind(this, this.update)); this.input.subscribe('invalid', bind(this, this.invalid)); }, update: function(newValue) { console.log("CHANGED: " + newValue); }, invalid: function(value) { console.log("El valor " + value + " es inválido!" ); }});

Page 454: Curso Javascript profesionales

Intermedio: Observable

var Observable = { mixed: function(klass) { // ??? }, subscribe: function(event, callback) { // ??? }, unsubscribe: function(event, callback) { // ??? }, publish: function(event) { // ??? }};

Page 455: Curso Javascript profesionales

Observador

¿Cuándo utilizar Observador?

• Se utiliza muy a menudo!

• Propagar cambios

• Reaccionar a sucesos

• Comunicación uno-a-muchos

Page 456: Curso Javascript profesionales

Mediator

Un punto central de control del sistema

• Desacoplamiento a gran escala

• Sencillo y muy importante

• Dividir la lógica en dos niveles

- Componente

- Aplicación

Page 457: Curso Javascript profesionales

Mediator

Page 458: Curso Javascript profesionales

Mediator

x

Page 459: Curso Javascript profesionales

Mediator

x

Aceptar

Page 460: Curso Javascript profesionales

Mediator

x

Aceptar

Page 461: Curso Javascript profesionales

Mediator

componente

componente

componente

componente

componente

Page 462: Curso Javascript profesionales

Mediator

componente

componente componente

componente componente

mediador

Page 463: Curso Javascript profesionales

Mediator

componente

componente componente

componente componente

mediador

Page 464: Curso Javascript profesionales

Mediator

var Mediator = Class.extend({ init: function(fn) { this.connections = {}; if (fn) fn(this); }, on: function(msg, cb) { this.connections[msg] = cb; }, send: function(msg) { var args = [].slice.call(arguments, 1), action = this.connections[msg]; if (action) action.apply({}, args); }, addComponent: function(component) { component.setMediator(this); }});

Page 465: Curso Javascript profesionales

Intermedio: Mediator

• Mediador en acción:

- tema2/mediador-1/index.html

Page 466: Curso Javascript profesionales

Intermedio: Mediator

// Inicialización

$(function() {

var mediator = new Mediator(), button1 = new Button('pulsador', '#button-1'); mediator.addComponent(button1); mediator.on('pulsador:clicked', function() { alert("hola?"); }); });

Page 467: Curso Javascript profesionales

Intermedio: Mediator

// Inicialización

$(function() {

var mediator = new Mediator(function(mediator) { var button1 = new Button('pulsador', '#button-1'); mediator.addComponent(button1); mediator.on('pulsador:clicked', function() { alert("hola?"); }); }); });

Page 468: Curso Javascript profesionales

Intermedio: Mediator

• Mediador en acción:

- tema2/mediador-2/index.html

Page 469: Curso Javascript profesionales

Intermedio: Mediator

mediator.on('ignore:on', function() { enabled = false;});

mediator.on('ignore:off', function() { enabled = true;});

mediator.on('inc:clicked', function() { if (enabled) { count.increment(); } display.update(count.getCurrentValue());});

mediator.on('dec:clicked', function() { if (enabled) { count.decrement(); } display.update(count.getCurrentValue());});

Page 470: Curso Javascript profesionales

Intermedio: Mediator

mediator.on('ignore:on', function() { enabled = false;});

mediator.on('ignore:off', function() { enabled = true;});

mediator.on('inc:clicked', function() { if (enabled) { count.increment(); } display.update(count.getCurrentValue());});

mediator.on('dec:clicked', function() { if (enabled) { count.decrement(); } display.update(count.getCurrentValue());});

Page 471: Curso Javascript profesionales

Mediator

¿Cuándo usar un Mediador?

• Muchos componentes interactuando

• La interacción se puede separar del componente

• Es decir: coordinar diferentes widgets de la aplicación

Page 472: Curso Javascript profesionales

Mediator

¿Cuándo usar un Mediador?

• Muchos componentes interactuando

• La interacción se puede separar del componente

• Es decir: coordinar diferentes widgets de la aplicación

¡Cuidado!

๏ El mediador tiende a convertirse en un cajón de sastre

๏ Una Gran Función que Todo lo Hace!

Page 473: Curso Javascript profesionales

Comandos

Separar la preparación de una acción y su ejecución

• Flujos de ejecución no convencionales

• Tres actores: cliente, invocador, receptor

- cliente: prepara o configura una acción

- invocador: controla la ejecución

- receptor: lleva a cabo la acción

• Generalizar comportamientos

Page 474: Curso Javascript profesionales

Comandos

tema2/comandos-1/index.html

Page 475: Curso Javascript profesionales

Comandos

var Command = Class.extend({ init: function(msg) { this.msg = msg; }, execute: function() { alert(this.msg); }});

var ActionList = Class.extend({ init: function(selector) { this.ul = $(selector); }, append: function(name, command) { var li = $("<li>") .append($('<a/>', {html:name, href:'#'})) .click(bind(command, command.execute)) .appendTo(this.ul); }});

var list = new ActionList('#menu');list.append('saludo', new Command("Hola!"));list.append('achis!', new Command("Salud!"));

Page 476: Curso Javascript profesionales

Comandos

var Command = Class.extend({ init: function(msg) { this.msg = msg; }, execute: function() { alert(this.msg); }});

var ActionList = Class.extend({ init: function(selector) { this.ul = $(selector); }, append: function(name, command) { var li = $("<li>") .append($('<a/>', {html:name, href:'#'})) .click(bind(command, command.execute)) .appendTo(this.ul); }});

var list = new ActionList('#menu');list.append('saludo', new Command("Hola!"));list.append('achis!', new Command("Salud!"));

Page 477: Curso Javascript profesionales

Comandos

var AlertCommand = Class.extend({ init: function(msg) { this.msg = msg; }, execute: function() { alert(this.msg); }});

var LogCommand = Class.extend({ init: function(msg) { this.msg = msg; }, execute: function() { console.log(this.msg); }});

Page 478: Curso Javascript profesionales

Comandos

Encapsular las acciones y controlar su ejecución

• Una idea muy potente

• Deshacer! (estados reversibles)

• Historial

• Control de cambios

• Sincronización de estados

Page 479: Curso Javascript profesionales

Comandos

Un ejemplo sencillo de estado reversible:

• tema2/comandos-2/index.html

var Movement = Class.extend({ init: function(amount, axis) { this.amount = amount; this.axis = axis; }, execute: function(mosca) { mosca.move(this.amount, this.axis); }, undo: function(mosca) { mosca.move(-this.amount, this.axis); }});

Page 480: Curso Javascript profesionales

Comandos

¿Cuándo usar Comandos?

• Controlar la ejecución de acciones

• Separar la definición de la acción y su ejecución

• Es decir:

- Intervienen actores que pueden fallar (servidor, usuario)

- Menús, selectores y otros “contenedores de acciones”

- Sincronizar cambios simultáneos (control de versiones)

Page 481: Curso Javascript profesionales

Cadena de Responsabilidad

Cada eslabón decide actuar o delegar en el próximo

• Elegir dinámicamente quien responde a una petición

• Dividir una operación compleja en varios pasos

• Desacoplar la complejidad del cliente

Page 482: Curso Javascript profesionales

Cadena de Responsabilidad

var AlmacenVehiculos = Class.extend({ init: function(tipo) { this.tipo = tipo; this.vehiculos = []; }, guardar: function(vehiculo) { if (vehiculo.getTipo() == this.tipo) { this.vehiculos.push(vehiculo); } }});

var hangar = new AlmacenVehiculos('avion'), parking = new AlmacenVehiculos('coche'), puerto = new AlmacenVehiculos('barco');

Page 483: Curso Javascript profesionales

Cadena de Responsabilidad

var Vehiculo = Class.extend({ init: function(tipo) { this.tipo = tipo; }, getTipo: function() { return this.tipo; }, guardar: function() { hangar.guardar(this); puerto.guardar(this); parking.guardar(this); }});

Page 484: Curso Javascript profesionales

Cadena de Responsabilidad

var AlmacenVehiculos = Class.extend({ init: function(tipo, siguiente) { this.tipo = tipo; this.vehiculos = []; this.siguiente = siguiente; }, guardar: function(vehiculo) { if (vehiculo.getTipo() == this.tipo) { this.vehiculos.push(vehiculo); } else if (siguiente.next) { this.siguiente.guardar(vehiculo); } }});

Page 485: Curso Javascript profesionales

Cadena de Responsabilidad

var hangar = new AlmacenVehiculos('avion'), parking = new AlmacenVehiculos('coche', hangar), puerto = new AlmacenVehiculos('barco', parking), almacenes = new AlmacenVehiculos('', puerto);

var Vehiculo = Class.extend({ init: function(tipo) { this.tipo = tipo; }, getTipo: function() { return this.tipo; }, guardar: function() { almacenes.guardar(this); }});

Page 486: Curso Javascript profesionales

Cadena de Responsabilidad

¿Cuándo usar Cadenas de Reponsabilidad?

• Jerarquías de objetos

- Los eventos del navegador

• Diferentes fuentes para responder a una petición

- Ej: buscar por nombre, por fecha, por ciudad, ...

• El input puede afectar a varias entidades

- Ej: Colisiones

Page 487: Curso Javascript profesionales

Hydra

Árbol de comportamientos

• No existe

• Organización al más alto nivel

• Mediador + Comandos + Cadena de Responsabilidad

• Extremadamente escalable!

• Para aplicaciones muy grandes

Page 488: Curso Javascript profesionales

Hydra

Librería DOM

Componente

Mediador Global

Componente Componente

Page 489: Curso Javascript profesionales

Hydra

Librería DOM

Mediador Global

Mediador Local

Componente

Componente

Componente

Mediador Local

Componente

Componente

Componente

Mediador Local

Componente

Componente

Componente

Page 490: Curso Javascript profesionales

Hydra

Librería DOM

Mediador Global

Mediador Local

Componente

Ajax + DOM

Coordinación general

Coordinación local

Lógica del componente

Page 491: Curso Javascript profesionales

Hydra

Librería DOM

Mediador Global

Mediador Local

Componente

Ajax + DOM

Coordinación general

Coordinación local

Lógica del componente

click en “Guardar”

Page 492: Curso Javascript profesionales

Hydra

Librería DOM

Mediador Global

Mediador Local

Componente

Ajax + DOM

Coordinación general

Coordinación local

Lógica del componente

click en “Guardar”

Dame los datosdel formulario

Componente

Page 493: Curso Javascript profesionales

Hydra

Librería DOM

Mediador Global

Mediador Local

Componente

Ajax + DOM

Coordinación general

Coordinación local

Lógica del componente

click en “Guardar”

Guardar nuevosdatos de usuario

Page 494: Curso Javascript profesionales

Hydra

Librería DOM

Mediador Global

Mediador Local

Componente

Ajax + DOM

Coordinación general

Coordinación local

Lógica del componente

click en “Guardar”

Guardar nuevosdatos de usuario

Actualiza lalista decontactos

Mediador Local

Page 495: Curso Javascript profesionales

Hydra

Librería DOM

Mediador Global

Mediador Local

Componente

Ajax + DOM

Coordinación general

Coordinación local

Lógica del componente

click en “Guardar”

Guardar nuevosdatos de usuario

Haz un POSTa /users

Page 496: Curso Javascript profesionales

Hydra

Separación por capas de significado

• Cada mediador gestiona lo que hay a su nivel

• El significado de las acciones va cobrando sentido

• La lógica de los componentes se limita a hablar con su mediador

• La coordinación es “recursiva”

Page 497: Curso Javascript profesionales

Hydra

¿Cuándo utilizar Hydra?

• Aplicaciones grandes!

• Widgets complicados que se beneficien de mediación

• En general, en cuanto un mediador engorde demasiado

Page 498: Curso Javascript profesionales

Hydra

¿Cuándo utilizar Hydra?

• Aplicaciones grandes!

• Widgets complicados que se beneficien de mediación

• En general, en cuanto un mediador engorde demasiado

¡Cuidado!

๏ El alto desacoplamiento hace complicado leer el código

๏ El significado TOTAL de cada acción está diseminado

Page 499: Curso Javascript profesionales

MVC: Backbone.js

made with love by Redradix (www.redradix.com)

Page 500: Curso Javascript profesionales

El problema: acoplamiento

Las aplicaciones JS tienden a ser un caos

• La lógica del interfaz se mezcla con

• La lógica de control y validación de datos y con

• La lógica de comunicación con el servidor y con

• La lógica de reacción a eventos!

Page 501: Curso Javascript profesionales

El problema: acoplamiento

El peor tipo de código espagueti!

$.ajax({ url: 'events/since', data: { timestamp: old_id }, dataType: 'json', type: 'GET', global: false, cache: false, success: function(newEvents) { if (newEvents.events && newEvents.events.length != 0) { if (prepend_events) { if (page <= '1') { if ($('div.new_events').length == 0) { $('#events').prepend('<div class="note new_events"><strong class="new_event_count">'+newEvents.events.length+'</strong> New Events Are Available Click here To View Them.</div>');

Page 502: Curso Javascript profesionales

MVC

En los 70 se propuso una solución:

• Separar el código en 3 componentes:

- Modelo: datos y lógica de negocio

- Vista: presentación de los datos

- Controlador: gestión de interacciones

• Limitar su comunicación

• Muy popular desde hace mucho tiempo!

- Aplicaciones de escritorio

- Aplicaciones móviles

- Una interpretación peculiar en los frameworks web

Page 503: Curso Javascript profesionales

MVC

Se ha extendido hace “poco” por JS

• Backbone.js

• Spine.js

• Batman.js

• Ember.js

• ...

Page 504: Curso Javascript profesionales

MVC

M

V C

Usuario

Ve Usa

ManipulaActualiza

Page 505: Curso Javascript profesionales

MVC

Vamos a aplicar el patrón MVC

• Entender su filosofía

• Comprender los mecanismos fundamentales

• Apreciar sus ventajas

• Ver sus limitaciones

• Estudiar diferentes soluciones

Page 506: Curso Javascript profesionales

MVC

JS con todas nuestras utilidades:

• tema4/lib/prelude.js

- Class con herencia de propiedades de clase

- bind, curry, clone, merge

- Namespaces

- Observable

- Algunas utilidades extra

Page 507: Curso Javascript profesionales

Modelo

Page 508: Curso Javascript profesionales

Modelo

El Modelo se encarga de manejar los datos

• Representa una entidad

• Mecanismos de lectura y modificación

• Operaciones con los datos

• Validación

• Serialización

Page 509: Curso Javascript profesionales

Modelo

Representar una entidad

var Usuario = Backbone.Model.extend({ /* ... */})

Page 510: Curso Javascript profesionales

Modelo

Un modelo, en esencia, un conjunto de atributos

var u = new Usuario();

/* Crear o modificar un atributo */u.set({nombre: "Pepito"});

/* Leer el valor de un atributo */var nombre = u.get("nombre");console.log(nombre);

Page 511: Curso Javascript profesionales

Modelo

Valores por defecto para los atributos: defaults

var Usuario = Backbone.Model.extend({ defaults: { nombre: "Anónimo", nacionalidad: "Español" }});

var u = new Usuario();console.log(u.get("nombre"));

u.set({nombre: "Pepito"});console.log(u.get("nombre"));

Page 512: Curso Javascript profesionales

Modelo

Un modelo es un objeto, ¡Pero ten cuidado!

var Usuario = Backbone.Model.extend({ defaults: { nombre: "Anónimo", nacionalidad: "Español" }, saludar: function() { return "Hola, soy " + this.nombre; }});

var u = new Usuario();u.set({nombre: "Pepito"});console.log(u.saludar()); // "Hola, soy undefined"

Page 513: Curso Javascript profesionales

Modelo

Los atributos del modelo se consultan con get/set

var Usuario = Backbone.Model.extend({ defaults: { nombre: "Anónimo", nacionalidad: "Español" }, saludar: function() { return "Hola, soy " + this.get("nombre"); }});

var u = new Usuario();u.set({nombre: "Pepito"});console.log(u.saludar()); // "Hola, soy Pepito"

Page 514: Curso Javascript profesionales

Modelo

Serializar los atributos: .toJSON()

var Usuario = Backbone.Model.extend({ defaults: { nombre: "Anónimo", nacionalidad: "Español" }, saludar: function() { return "Hola, soy " + this.get("nombre"); }});

var u = new Usuario();u.set({nombre: "Pepito"});

var attrs = u.toJSON();console.log(attrs);

Page 515: Curso Javascript profesionales

Modelo

Validez: método .validate(attrs)• El método no viene definido por defecto

• Nosotros lo creamos para validar nuestro modelo

• Se llama automáticamente desde save

• Se puede forzar al hacer .set()

Page 516: Curso Javascript profesionales

Modelo

Validez: método .validate(attrs)• Si los valores de attrs no son válidos:

- Devuelve una descripción del error

- Puede ser cualquier cosa...

• Si son válidos:

- No se devuelve nada

Page 517: Curso Javascript profesionales

Modelo

var Usuario = Backbone.Model.extend({ defaults: { nombre: "Anónimo", nacionalidad: "Español" }, validate: function (attrs) { if (/^\s*$/.test(attrs["nombre"])) { return "El nombre no puede quedar en blanco!"; } }});

var u = new Usuario();

u.set({nombre: "Pepito"}, {validate: true});console.log(u.get("nombre"));

u.set({nombre: " "}, {validate: true});console.log(u.get("nombre")); // "Pepito"

Page 518: Curso Javascript profesionales

Modelo

Backbone.js es una librería agnóstica en muchos sentidos

• No impone templates

• No impone estructura

• No impone almacenamiento

Page 519: Curso Javascript profesionales

Modelo

Backbone.js es una librería agnóstica en muchos sentidos

• No impone templates

• No impone estructura

• No impone almacenamiento

Excepto...

• Impone su propio sistema de herencia y clases

• Muy simple

• Muy limitado

Page 520: Curso Javascript profesionales

Modelo

var Usuario = Backbone.Model.extend({ initialize: function () { // Constructor }});

var Ninja = Usuario.extend({ initialize: function () { Usuario.prototype.initialize.apply(this, arguments); // Subclase }});

Page 521: Curso Javascript profesionales

Modelo

Parecida a la nuestra, pero:

• No se pueden crear objetos que no hereden de una clase base de Backbone (Model, View, Collection o Router)

• La manera de llamar al súper método es incómoda y viola el principio DRY

Page 522: Curso Javascript profesionales

Modelo

Por suerte...

• Javascript es tremendamente flexible

• El modelo de Backbone es muy simple

• klass.js la hemos escrito nosotros y entendemos cómo funciona

Page 523: Curso Javascript profesionales

Modelo

Podemos integrar ambas librerías fácilmente!

• “Envolviendo” las clases base de Backbone en clases de klass.js

• Así tenemos lo mejor de las dos

Page 524: Curso Javascript profesionales

klass.js <-> backbone.js

var ProJS = (function (my) { var wrapBackboneClass = function(className) { var backboneWrapped = Backbone[className], F = function() {}, K = function() {}; F.prototype = backboneWrapped.prototype; K.prototype = new F(); _.extend(K, ProJS.Class, {constructor: backboneWrapped}); _.extend(K.prototype, ProJS.Class.prototype); K.prototype.init = function() { return backboneWrapped.apply(this, arguments); }; return K; };

my.Model = wrapBackboneClass('Model'); my.View = wrapBackboneClass('View'); my.Collection = wrapBackboneClass('Collection');

return my;}(ProJS || {}));

Page 525: Curso Javascript profesionales

klass.js <-> backbone.js

Ahora podemos hacer:

var Usuario = ProJS.Model.extend({ init: function() { // constructor this._super(); }, defaults: { // etc... }});

Page 526: Curso Javascript profesionales

Modelo

Ejercicio (uno fácil para empezar):

tema4/model-1/index.html

• Escribe un modelo Producto

- Id (incremento automático)

- Nombre (obligatorio)

- Categoría (obligatorio)

- País (España, Portugal o Francia)

- Precio (obligatorio, sin IVA)

• Escribe un decorador para obtener precios con IVA

Page 527: Curso Javascript profesionales

Modelo

Fíjate en...

• Lo fácil que resulta sobreescribir la funcionalidad de un método con klass.js

• Si no tuviéramos klass.js, ¿Cómo habríamos hecho el decorador? ¿De quién tendría que haber heredado?

• Puedes pasar varios atributos a la vez a .set()

• Puedes pasar los atributos directamente al constructor

• Es una base bastante sólida para controlar los datos de nuestra aplicación!

Page 528: Curso Javascript profesionales

Modelo

Los modelos generan eventos

• Cuando alguno de sus atributos cambia: change

• Cuando un atributo en concreto cambia: change:[attr]

• Cuando las validaciones fallan: invalid

• Al ser destruidos: destroy

• Al ser sincronizado con el servidor: sync

• Si surge algún error al guardar: error

Page 529: Curso Javascript profesionales

Modelo

var p1 = new Producto();

p1.on("change", function (model, options) { console.log("El producto", model.get("nombre"), "ha cambiado!");});

p1.set({ nombre: "Jamón", categoria: "Comida", pais: "España", precio: 65});

Page 530: Curso Javascript profesionales

Modelo

Ejercicio:

• Modifica el ejercicio anterior de modo que:

• Cuando una validación falla, se informe al usuario del error por la consola

Page 531: Curso Javascript profesionales

Modelo

Otras operaciones útiles:

.unset(): elimina un atributo

.clear(): elimina todos los atributos

.previous(attr): durante un evento change, devuelve el valor anterior de un atributo

.has(attr): ¿Tiene el modelo el atributo attr?

.escape(attr): como .get(), pero escapando el HTML

Page 532: Curso Javascript profesionales

Modelo

Persistencia

• La “gracia” de Backbone es que sabe como hablar con el servidor

• Pedir y guardar modelos automáticamente por AJAX

• Muy flexible

• Soporta también localStorage

Page 533: Curso Javascript profesionales

Modelo

Para hablar con el servidor, el modelo necesita:

• urlRoot: la base con la que construir su URL

• id: el identificador del recurso

• parse (opcional): una función que interprete la respuesta del servidor

Page 534: Curso Javascript profesionales

Modelo

Las operaciones fundamentales de persistencia:

.fetch()1. dado un id, construye una url del tipo [baseURL]/[id]

2. GET al servidor

3. pasa la respuesta a .parse()4. con el resultado llama a .set() para establecer los atributos

del modelo

Page 535: Curso Javascript profesionales

Modelo

Ejercicio

tema4/model-2/index.html

Con una api AJAX tal que:

GET /products -> lista de {nombre: “”, id: #}

GET /products/:id -> detalles del producto :id

PUT /products/:id -> guarda cambios del producto :id

Haz:

- Construye un array con un modelo por cada producto del listado

- Modifica algún producto y guarda los cambios

- Escucha los eventos “change”y “sync” e informa por consola

Page 536: Curso Javascript profesionales

Colecciones

Colección: conjunto ordenado de modelos

• Se identifica con un listado de recursos

- GET a URLs de tipo “índice” (/users, /products, etc...)

• Los modelos de una colección son del mismo tipo

Page 537: Curso Javascript profesionales

Colecciones

• Objetivo similar al del un modelo, pero con conjuntos

- Manejar colecciones de datos

- Ordenar, añadir, eliminar entidades a la colección

- Consultar y guardar la colección en el servidor

- Serializar el conjunto de datos

Page 538: Curso Javascript profesionales

Colecciones

var ListadoProductos = ProJS.Collection.extend({ model: Producto});

var listado = new ListadoProductos();

Page 539: Curso Javascript profesionales

Colecciones

Operaciones fundamentales:

- add(model, {at: i}): añadir un modelo a la colección

- remove(model|id|cid): eleminar un modelo

- get(id|cid): acceder a un objeto de la colección por id

- at(idx): acceder a un objeto de la colección por índice

- length: número de elementos en la colección

- where(attrs): query de atributos

- push/pop, shift/unshift

Page 540: Curso Javascript profesionales

Colecciones

var listado = new ListadoProductos();

listado.add(new Producto({nombre: "Uno"}));listado.add(new Producto({nombre: "Dos"}));listado.add(new Producto({nombre: "Tres"}));

console.log(listado.at(2).toJSON());

Page 541: Curso Javascript profesionales

Colecciones

Para identificar a los modelos dentro de una colección, podemos usar:

• id- Generalmente otorgado por el servidor

- Se utiliza para construir la URL del modelo

- Universal dentro de la app

- Se corresponde, habitualmente, con el ID de la tabla en BBDD

• cid- Generado automáticamente por Backbone

- Válido solo dentro de la página

- Modelos no guardados o que no tienen que ver con BBDD

Page 542: Curso Javascript profesionales

Colecciones

console.log(new Producto().cid); // c1console.log(new Producto().cid); // c2console.log(new Producto().cid); // c3

Page 543: Curso Javascript profesionales

Colecciones

var p = new Producto({nombre: "Zapatos"}), cid = p.cid;

console.log(listado.get(cid)); // undefined

listado.add(p);console.log(listado.get(cid)); // p

var resultado = listado.where({nombre: "Zapatos"});console.log(resultado.toJSON()); // p

Page 544: Curso Javascript profesionales

Colecciones

Una colección sabe como interpretar datos “crudos” (instancia automáticamente un modelo)

listado.add({ nombre: "Corbata", categoría: "Caballero", precio: 40});

listado.at(0).constructor === Producto; // true

Page 545: Curso Javascript profesionales

Colecciones

Cargar datos iniciales en una colección: reset

var listado = new ListadoProductos();

listado.reset([ {nombre: "Uno", categoria: "A", precio: 2}, {nombre: "Dos", categoria: "B", precio: 1}, {nombre: "Tres", categoria: "C", precio: 8},]);

Page 546: Curso Javascript profesionales

Colecciones

Las 28 funciones de underscore para manipular listas se pueden aplicar a colecciones

mapreducefindfiltermax/minsortshuffleetc...

Page 547: Curso Javascript profesionales

Colecciones

Persistencia:

url: dirección para pedir la colección

- Los modelos de la colección usarán esta URL como urlRoot para construir sus URLs individuales

fetch: pide la colección al servidor

- GET a url, esperando un array de hashes (atributos)

set: refresca los datos de la colección

- Aproximadamente igual que hacer un fetch y mergear

Page 548: Curso Javascript profesionales

Colecciones

Ejercicio:

tema4/collection-1/index.html

Crea una colección de Productos que lea de /products

Y tenga los métodos:

listado(): todos los productos

ordenaPorNombre(): filtro

precioMenorQue(p): filtro

borrarProducto(id): lo elimina de la BBDD

nuevoProducto(attrs): añade el producto a la colección y lo guarda en BBDD

Page 549: Curso Javascript profesionales

Colecciones

Las colecciones también emiten eventos:

add(model, col): se ha añadido un nuevo modelo

remove(model, col): se ha eliminado un modelo

sort(col): se ha reordenado

Page 550: Curso Javascript profesionales

Vista

Es una representación del modelo

• Asociada a una instancia de un modelo

• Generalmente utiliza un template

• Actualización automática

Page 551: Curso Javascript profesionales

Vista

Templates

• “Plantillas” que mezclan HTML y código JS

• Backbone funciona con cualquier librería de templates

• Trae una preinstalada: _.template()

Page 552: Curso Javascript profesionales

Vista

_.template(texto, datos)• texto: el texto de nuestra plantilla

• datos: un objeto con las variables que queramos utilizar al evaluar el template

Page 553: Curso Javascript profesionales

Vista

En el texto de la plantilla:

<%= expresión %>Se sustituye por el resultado de evaluar la expresión

<% código %>Ejecuta el código javascript

Page 554: Curso Javascript profesionales

Vista

var plantilla = "<h1> Hola! </h1>";console.log( _.template(plantilla, {}));

Page 555: Curso Javascript profesionales

Vista

var plantilla = " <h1> \ <% console.log('Hola!') %> \ </h1>";

console.log( _.template(plantilla, {}));

Page 556: Curso Javascript profesionales

Vista

var plantilla = " <h1> \ <%= 10 + 10 %> \ </h1>";

console.log( _.template(plantilla, {}));

Page 557: Curso Javascript profesionales

Vista

var plantilla = " <h1> \ Bienvenido, <%= nombre %> \ </h1>";

console.log( _.template(plantilla, {nombre: "Ulises"}));

Page 558: Curso Javascript profesionales

Vista

Anatomía de una vista

• Un modelo del que se extraen los datos

• Un template que se rendea con los datos del modelo

• Un nodo del DOM donde se inserta el template rendeado

• Un método .render() que se encarga de ejecutar este proceso

Page 559: Curso Javascript profesionales

Vista

el Vista Modeloescuchaactualiza

Page 560: Curso Javascript profesionales

Vista

En Backbone:

.model: el modelo asociado a la vista

.tagName: la etiqueta para generar el nodo del DOM

.attributes: los atributos para generar el nodo

.el: el nodo, ya creado

.$el: el nodo, envuelto con jQuery (o Zepto)

.$: un atajo a jquery pero con el scope fijado en .el

.render: el método que se encarga de rendear el template y actualizar el contenido de .el

Page 561: Curso Javascript profesionales

Vista

var MiVista = ProJS.View.extend({ init: function(options) { this._super(options); }, tagName: "div", attributes: {"class": "box large"}, template: "<h1> <%= nombre %> </h1>", render: function() { var data = this.model.toJSON(); this.$el.html( _.template(this.template, data) ); return this; }});

var producto = new Producto({nombre: "Vino"}), miVista = new MiVista({model: producto}).render();

$("body").append(miVista.el);

Page 562: Curso Javascript profesionales

Vista

Ejercicio: una vista sencilla

tema4/view-1/index.html

Crea una vista que utilice el template #producto-template para mostrar una instancia de Producto

Page 563: Curso Javascript profesionales

Vista

Ejercicio: vistas y colecciones

tema4/view-2/index.html

Crea una colección que se inicialice con los datos de la ruta /products

Para cada uno de los modelos:

Instancia una vista VistaListado

Muéstrala por pantalla

Una pista: ¡las colecciones emiten eventos!

Page 564: Curso Javascript profesionales

Vista

La vista debería “escuchar” al modelo

Cambios automáticos cuando el modelo cambia

El patrón habitual:

var VistaListado = ProJS.View.extend({ init: function (options) { this._super(options); this.model.on("change", bind(this, this.render)); }, /* ... */});

Page 565: Curso Javascript profesionales

Y ahora, ¿qué?

Tenemos Modelo y Vista

• Todo el mundo está de acuerdo en estos dos puntos

• Datos + presentación

• Todavía falta algo...

Page 566: Curso Javascript profesionales

MV*

MV*

• El papel del Controlador no está tan claro

• Gestionar la interacción?

• Gestionar los eventos de la vista?

• Gestionar las rutas de la página?

• Gestionar al modelo?

• ...

Page 567: Curso Javascript profesionales

MV*

La visión tradicional:

Modelo Controlador Vista

Page 568: Curso Javascript profesionales

MV*

En JavaScript...

M Controlador V

Page 569: Curso Javascript profesionales

MV*

En JavaScript...

M Controlador V

TemplateProxy ¡Todo lo demás!

Page 570: Curso Javascript profesionales

Controlador

El modelo “estándar” de Backbone.js

• Nadie lo dice abiertamente, pero:

• No hay Backbone.Controller

• La vista propiamente dicha es el template

• Pero Backbone.View gestiona la interacción...

• Es decir, hace de controlador

Page 571: Curso Javascript profesionales

Controlador

Si no te he convencido...

¿Quién reacciona al input del usuario?

Backbone.View

¿Quién se encarga de actualizar los datos del modelo según ese input?

Backbone.View

¿Dónde se programa la lógica del interfaz de usuario?

Backbone.View

Page 572: Curso Javascript profesionales

Controlador

Escuchar eventos en la vista:

var VistaProducto = ProJS.View.extend({ events: { "click a": "marcarComoActivo" }, init: function(options) { this._super(options); this.template = $("#template-producto").html(); this.model.on("change", bind(this, this.render)); },

// Event handlers marcarComoActivo: function() { this.model.set({active: true}); }

/* ... */});

Page 573: Curso Javascript profesionales

Controlador

Ejercicio: cogiendo el feeling del controlador

tema4/controller-1/index.html

Haz que las vistas VistaListado:

- Escuchen los cambios del modelo y se auto-rendeen

- Escuchen el evento click en el <a> dentro de this.el y lo asocien a marcarComoActivo

- marcarComoActivo pone a true la propiedad “activo” del modelo

- Si la propiedad “activo” del modelo es true, añade la clase CSS “active” a this.el (que debería ser un <li>)

Page 574: Curso Javascript profesionales

Controlador

El ejemplo anterior tiene problemas:

¿Cómo podemos decirle a los demás elementos que se desactiven?

¿Cómo podemos avisar al resto de la aplicación que se ha seleccionado un nuevo Producto?

Page 575: Curso Javascript profesionales

MV*

¡Un Mediador!

• La mejor solución para coordinar componentes de una página

• Muy bajo acoplamiento:

- Modificar los componentes sin problemas

- Añadir o eliminar funcionalidad en la página

• MV* + Mediador = un patrón muy común y muy flexible

Page 576: Curso Javascript profesionales

MV*

Un ejemplo:

tema4/controller-2/index.html

Fíjate como VistaListado simplemente notifica al mediador

Y el mediador es quien se encarga de orquestar los demás elementos

Las ventajas son:

Tenemos todo el flujo “a vista de pájaro” de la página en un solo sitio: el mediador

Ningún componente está acoplado a ningún otro, solo notifica a su mediador

Page 577: Curso Javascript profesionales

MV*

Ejercicio: un poco de todo!

Modifica el ejemplo anterior para que al hacer click en un elemento de la barra lateral se muestre ese modelo con una vista VistaProducto (la tabla de los primeros ejercicios)

Page 578: Curso Javascript profesionales

Testing

made with love by Redradix (www.redradix.com)

Page 579: Curso Javascript profesionales

¿Qué es?

Comprobación automática del código

• Organizada por casos

• Cada caso comprueba un aspecto

• Comparando el resultado obtenido con el esperado

Page 580: Curso Javascript profesionales

¿Para qué sirve?

¡Para garantizar que todo funciona!

• Que el nuevo código es correcto

• Que no se ha roto nada de lo anterior

• Que una refactorización no ha introducido bugs

Page 581: Curso Javascript profesionales

¿En JavaScript?

Una práctica que va penetrando poco a poco

• Aunque sigue sin estar muy extendida

• Necesaria para aplicaciones complejas

• En general, una garantía de calidad

Page 582: Curso Javascript profesionales

Testing

Hay muchos tipos de tests:

• Unitarios: comprueban un componente o una parte específica del código

• Integración: comprueban la interacción de componentes

• Aceptación: comprueban los requisitos del proyecto

• Regresión: comprueban la corrección de cambios

• etc...

Page 583: Curso Javascript profesionales

Tests Unitarios

La idea de test unitario es muy simple:

• Dado un componente del sistema

• Para cada caso posible

• Comprobar que se comporta de la manera adecuada

Page 584: Curso Javascript profesionales

Test Unitarios

var Contador = ProJS.Class.extend({ init: function() { this.i = 0; }, get: function() { return this.i; }, inc: function() { this.i++; }, dec: function() { this.i--; }, reset: function() { this.i = 0; }});

Page 585: Curso Javascript profesionales

Test Unitarios

¿Cómo podríamos comprobar, programáticamente, que Contador funciona bien?

Haciendo algo así:

➡ tema5/unitarios-1/index.html

Page 586: Curso Javascript profesionales

Test Unitarios

Es bastante tedioso!

• Mucha repetición de código similar

• Se puede abstraer bastante

Page 587: Curso Javascript profesionales

Test Unitarios

Segundo intento

➡ tema5/unitarios-2/index.html

Page 588: Curso Javascript profesionales

Test Unitarios

var ContadorTests = Test.extend({ casos: { debe_empezar_a_cero: function(contador) { var i = contador.get(); this.assertEqual(i, 0, "Empieza a %1".format(i)); },

// ... }});

Page 589: Curso Javascript profesionales

Jasmine

Estupenda librería de testing

• Al estilo rspec

• Sencilla

• Potente

• http://pivotal.github.com/jasmine/

Page 590: Curso Javascript profesionales

Jasmine

¿Qué pinta tiene?

• tema5/jasmine-1/index.html

describe("Conjunto de tests", function() { it("debería ser un caso válido", function() { expect(true).toBe(true); }); it("debería ser un caso con error", function() { expect(true).toBe(false); });});

Page 591: Curso Javascript profesionales

Jasmine

Test del contador con Jasmine

• tema5/jasmine-2/index.html

Page 592: Curso Javascript profesionales

Jasmine

Test asíncronos

• ¿Cómo testearías que esta función llama al cb con true?

• tema5/jasmine-3/index.html

function asyncFn(cb) { setTimeout(function() { cb(true); }, 250);}

Page 593: Curso Javascript profesionales

Jasmine

describe("Test asíncrono", function() { it("debería llamar al callback con true", function() { var result, callback = function(response) { result = response; }; runs(function() { asyncFn(callback); }); waitsFor(function() { return result == true; }, 300); runs(function() { expect(result).toBe(true); }); });});

Page 594: Curso Javascript profesionales

Intermedio: Jasmine

¡Testea alguna de las funciones que hemos visto!

• La que te parezca más confusa

• Documentación y “matchers” de Jasmine en

➡ http://pivotal.github.com/jasmine/

Page 595: Curso Javascript profesionales

Jasmine

Jasmine en la consola:

• Cambiar a ConsoleReporter

• Y un poco de magia funcional...

➡ tema5/consola-1/index.html

Page 596: Curso Javascript profesionales

Jasmine

var lazyPrint = (function() { var buffer = "", print = function() { console.log(buffer); buffer = ""; }; print = debounce(print, 300); return function(msg) { buffer += msg; print(); };}());

Page 597: Curso Javascript profesionales

Jasmine

¿Para qué sirve Jasmine en la consola?

• Dejar la página libre

• Poder cargar nuestro propio HTML

•¡Testear interacciones e interfaces!

Page 598: Curso Javascript profesionales

Test de Integración (interfaz)

Comprobar que el UI funciona correctamente

• Simular la interacción del usuario disparando eventos DOM

• Observar el estado del programa inspeccionando el interfaz

• Asegurar la correcta integración de los componentes de la página

Page 599: Curso Javascript profesionales

Jasmine

El resultado:

• tema5/integration/index.html

• Queremos testear que el intefaz funciona bien

• “Inc” incrementa el contador y el display

• “Dec” decrementa el contador y el display

• “Reset” lo pone a 0

• Salida por consola...

- Podríamos ver esta salida en algún emulador de DOM de node.js

- O hacer un reporter que se comunique con el servidor de integración continua

Page 600: Curso Javascript profesionales

Spam Mode: ON

Al escribir test JS acaba surgiendo un problema:

• ¡Los datos!

• ¿De dónde saco datos válidos para testear?

• ¿Del servidor?

- No es fácil de conseguir modificar/resetear un set de datos cada vez que ejecuto un test

- Dependencia del backend

• Lo ideal sería:

- Factorías de datos (estilo FactoryGirl)

- Simular la interacción con el servidor de forma inocua

Page 601: Curso Javascript profesionales

Solipsist.js

Solipsist.js es una librería auxiliar para testear

➡ https://github.com/WeRelax/solipsist-js

• Tests JS aislados

• Factorías

• Mocking de peticiones AJAX

• Otro uso: programar el frontend independiente del backend