85

Apuntes Javascript Avanzado

Embed Size (px)

DESCRIPTION

Avanzado

Citation preview

Page 1: Apuntes Javascript Avanzado
Page 2: Apuntes Javascript Avanzado

Apuntes de Javascript IINivel Avanzado

JuanMa Garrido

Este libro está a la venta en http://leanpub.com/apuntes-javascript-avanzado

Esta versión se publicó en 2015-01-30

This is a Leanpub book. Leanpub empowers authors and publishers with the Lean Publishingprocess. Lean Publishing is the act of publishing an in-progress ebook using lightweight toolsand many iterations to get reader feedback, pivot until you have the right book and buildtraction once you do.

This work is licensed under a Creative Commons Attribution-NonCommercial-NoDerivs 3.0Unported License

Page 3: Apuntes Javascript Avanzado

A mi abuelo…

“Hijo, tu di que si, y luego haz lo que te de la gana” (mi abuelo)

Page 4: Apuntes Javascript Avanzado

Índice general

1. Prologo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11.1 Referencias . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11.2 Agradecimientos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1

2. Same-origin policy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22.1 JSONP vs CORS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2

3. JSONP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43.1 Peticiones JSONP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5

4. CORS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84.1 Peticiones CORS sencillas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8

5. El valor de this . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95.1 En una función global . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95.2 En un método de un objeto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105.3 En un metodo de un prototipo . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115.4 En una función constructora . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115.5 Utilizando call o apply . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125.6 Utilizando bind . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 135.7 En una función asociada a un evento . . . . . . . . . . . . . . . . . . . . . . . . 13

6. Prototype . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15

7. Herencia . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 227.1 Encadenamiento de Prototipos (Prototype Chaining) . . . . . . . . . . . . . . . . 227.2 Moviendo metodos re-utilizables al prototypo . . . . . . . . . . . . . . . . . . . . 247.3 Herencia sólo del prototipo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 267.4 Función constructora temporal F() . . . . . . . . . . . . . . . . . . . . . . . . . . 277.5 Encapsulando la herencia en una función . . . . . . . . . . . . . . . . . . . . . . 297.6 Robando el constructor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30

8. Contexto de Ejecución . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 328.1 Objeto Variable (Variable Object) . . . . . . . . . . . . . . . . . . . . . . . . . . 328.2 Fases de Procesamiento del Código . . . . . . . . . . . . . . . . . . . . . . . . . 358.3 Tipos de Funciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 378.4 Hoisting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39

9. Scope . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41

Page 5: Apuntes Javascript Avanzado

ÍNDICE GENERAL

9.1 Ciclo de vida de una Función . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42

10.Patrones de Código . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4510.1 Separación de capas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4510.2 Namespaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4510.3 Init-time branching . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4610.4 Lazy Definition . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4710.5 Objeto de Configuración . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4810.6 Propiedades y Métodos Privados . . . . . . . . . . . . . . . . . . . . . . . . . . . 4910.7 Métodos Privilegiados . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4910.8 Funciones Privadas como Métodos Públicos (Revealing Module Pattern ) . . . . . 5010.9 Funciones Inmediatas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5110.10Memoization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5310.11 Patrón Modulo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5410.12 Patrón Sandbox . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54

11.Patrones de Diseño . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5711.1 Patrón Singleton . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5711.2 Patrón Factory . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5811.3 Patrón Iterator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6011.4 Patrón Mixins . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6111.5 Patrón Decorator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6211.6 Patrón Façade . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6411.7 Patrón Observer – Subscriber/Publisher . . . . . . . . . . . . . . . . . . . . . . . 6511.8 Patrón Mediator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68

12.Unit Testings en Javascript . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7012.1 Unit Testings (pruebas unitarias) . . . . . . . . . . . . . . . . . . . . . . . . . . . 7012.2 TDD y BDD . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7212.3 Testing Frameworks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7212.4 Sinon . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77

Page 6: Apuntes Javascript Avanzado

1. PrologoEste libro contiene la segunda parte de los materiales que he ido realizando para diferentestrainings JAVASCRIPT impartidos desde 2010.

Esta segunda parte abarca conceptos más avanzados de Javascript incluyen:

• Interactuación con API’s externas (Same-Origin Policy, JSONP, CORS)• Javascript a fondo (scope, valor de this, prototype, contexto de ejecución)• Javascript “orientado a objetos” (prototype, herencia)• Cómo estructurar mejor tu codigo JS (patrones de diseño y de codigo)• Cómo testear tu codigo js (unit testings)

Los conceptos cubiertos en este libro te permitiran que tu código JS sea orientado a objetos,testado, escalable, más comprensible y más organizado.

Espero que este libro te ayude a entender/implementar el codigo javascript que necesites.

Cualquier feedback será bien recibido :)

1.1 Referencias

Ademas de los enlaces reflejados, este material está basado en los siguientes libros:

• JavaScript: The Good Parts by Douglas Crockford• Object-Oriented JavaScript by Stoyan Stefanov• JavaScript Patterns by Stoyan Stefanov

1.2 Agradecimientos

Gracias a @gemmabeltra por la super-portada del libro (…y por aguantarme en general)

A @amischol por elevar mi nivel de javascript y el de todos los que hemos trabajado a su lado(cuando nosotros vamos, él ya tiene su chalet alli montado)

A @cvillu por animarme a escribir el libro y descubrirme LeanPub (South-Power!!!)

A @crossrecursion por nuestras charlas/reuniones hablando de Javascript (me excitas… intelec-tualmente)

Y a @softonic por darme la oportunidad de impartir los trainings JS y rodearme de tanta gentecrack en JavaScript. No hay universidad que me pueda enseñar lo que he podido aprender alli.

1

Page 7: Apuntes Javascript Avanzado

2. Same-origin policyLa “Same-Origin policy” (politica del mismo origen) controla que sólo que los scripts queprovengan del mismo origen (mismo esquema, hostname y puerto) y que son ejecutados endiferentes paginas (ventanas, tabs) puedan acceder sin restricciones a sus respectivos DOM

Mismo Origen:

1 http://site.com

2 http://site.com/

3 http://site.com/my/page.html

Diferente Origen:

1 http://www.site.com (another domain)

2 http://site.org (another domain)

3 https://site.com (another protocol)

4 http://site.com:8080 (another port)

Esta politica se aplica tambien a las peticiones AJAX (XMLHttpRequest) lo que significa que sólopodremos hacer peticiones AJAX al host que sirve la pagina web donde se ejecuta el código

Los WebSockets no estan sujeto a esta politica (con ellos podras comunicar partes en diferentesdominios)

Esta politica viene implementada en TODOS los navegadores (antiguos y modernos)

De forma nativa, sólo podremos incluir recursos de otros dominios con los siguientes elementos:

• Archivos Javascript con <script src="..."></script>

• Archivos CSS con <link rel="stylesheet" href="...">

• Imágenes con <img>

• Archivos Multimedia con <video> y <audio>

• Plug-ins con <object>, <embed> y <applet>

• Fuentes con @font-face

• Cualquier otra pagina con <frame> and <iframe>

Sin embargo existen maneras de “saltarse” esta politica: JSONP y CORS

2.1 JSONP vs CORS

2

Page 8: Apuntes Javascript Avanzado

Same-origin policy 3

..

→ CORS y JSONP: Solicitudes AJAX entre dominios | formandome.es→ Sopa de Siglas: AJAX, JSON, JSONP y CORS | blog.koalite.com→ So, JSONP or CORS? | StackOverflow

La recomendación general es usar CORS siempre que se pueda, pero hay que tener en cuenta losiguiente:

• CORS soporta mas metodos HTTP (GET, PUT, POST, DELETE) que JSONP (sólo GET)• Puedes utilizar JSONP en cualquier navegador (antiguo y moderno). Sin embargo CORSsolo funcionará en algunos navegadores (los que soporten xhr2)

• Hay mas API’s publicas que ofrecen acceso a datos via JSONP que via CORS• CORS (el servidor decide a quien da acceso y cómo) es mas seguro que JSONP (cross-originvia script injection)

• Con CORS hay un mejor manejo de errores que con JSONP• Tanto CORS como JSONP requieren que el servidor esté preparado para ellos

Page 9: Apuntes Javascript Avanzado

3. JSONP

..

→ Defining Safer JSON-P | json-p.org→ Remote JSON - JSONP | bob.ippoli.to→ JSONP: How Does it Work? | johnnywey.com

JSONP (JSON con Padding) es una técnicamediante la que podemos obtener y tratar JSON desdeotros dominios (desde javascript).

Con esta tecnica/hack obtenemos el JSON pasado como parametro a una funcion que se ejecutaen el cliente

El problema

→ Si desde la pagina MYsite.com ejecuto…

1 $.ajax({

2 url: 'http://www.ANOTHERsite.com/datos.json',

3 success: function(data) {

4 console.log(data)

5 }

6 });

…el navegador (Chrome) me devuelve algo asi:

1 Refused to connect to 'http://www.anothersite.com/datos.json' because it viol\

2 ates the following Content Security Policy directive...

…debido a la “Same-origin policy” del objeto XMLHttpRequest

→ Sin embargo si hacemos esto…

1 <script type="text/javascript" src="http://www.ANOTHERsite.com/datos.json"></\

2 script>

…si que podriamos obtener el JSON

4

Page 10: Apuntes Javascript Avanzado

JSONP 5

1 {

2 "api_key": "224Wrf2asfSDfcea23reSDfqW",

3 "status": "good",

4 "name": "wikipedia",

5 "date": "27-09-1995"

6 }

…pero no podriamos acceder al JSON obtenido ya que no queda almacenado en niguna variable

La solución

La solucion para poder tratar este JSON es preparar el servidor para que devuelva el JSONenvuelto en la llamada a una función

1 handleMyJSONResponse ({

2 "api_key": "224Wrf2asfSDfcea23reSDfqW",

3 "status": "good",

4 "name": "wikipedia",

5 "date": "27-09-1995"

6 });

Asi, si definimos en el cliente una funcion global handleMyJSONResponse preparada para recibirun JSON como parametro, ya podriamos recibir y tratar estos datos desde JS.

1 window.handleMyJSONResponse = function (datosJSON) {

2 console.log (datosJSON);

3 };

Por convención, el nombre de la función callback se especifica en un parámetro (jsonp, callbacko cualquier otro) de la URL que hace la peticion al servidor.

1 <script type="text/javascript" src="http://www.ANOTHERsite.com/datos.json?cal\

2 lback=handleMyJSONResponse"></script>

3.1 Peticiones JSONP

Con código nativo

..

→ jsFiddle: Ejemplos JSONP con JS nativo

Page 11: Apuntes Javascript Avanzado

JSONP 6

1 window.do_things = function (data) {

2 console.log ( "do_things : %o", data.responseData.results );

3 }

4

5 function loadScript (id, src, callback) {

6

7 // Crear elemento

8 var script = document.createElement("script");

9

10 // Atributos del script

11 script.setAttribute("type", "text/javascript");

12 script.setAttribute("src", src + "&callback=" + callback);

13 script.setAttribute("id", id);

14

15 // Insertar script en la cabecera

16 document.getElementsByTagName("head")[0].appendChild(script);

17

18 }

19

20 loadScript ("my_script_tag_id", "http://ajax.googleapis.com/ajax/services/sea\

21 rch/web?v=1.0&q=casas+alquiler", "do_things");

Con jQuery

JQuery se encarga (de forma transparente al developer) de darle un nombre a la funcion callback,pasarla en la peticion, crearla globalmente en el cliente y se encarga también de añadir y eliminarel tag script utilizado internamente

..

→ jsFiddle: Ejemplos JSONP jQuery→ jsFiddle: Ejemplo JSONP jQuery (Google Search Form)

1 $.getJSON(

2 'http://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=casas+alquiler&c\

3 allback=?',

4 function( googleResults) {

5 console.log ( "$.getJSON : %o", googleResults.responseData.results );

6 }

7 );

8

9 $.ajax({

10 url: "http://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=casas+alqui\

11 ler",

12 dataType: "jsonp",

Page 12: Apuntes Javascript Avanzado

JSONP 7

13 success: function( response ) {

14 console.log ( "$.ajax minimal : %o", response.responseData.results );

15 }

16 });

API’s publicas

La mayoria de las API’s publicas vienen ya preparadas para devolver JSONP:

..

→ API Notebook

→ Instagram

1 https://api.instagram.com/v1/tags/coffee/media/recent?access_token=fb2e77d.47\

2 a0479900504cb3ab4a1f626d174d2d&callback=callbackFunction

→ Github

1 https://api.github.com/?callback=foo

→ Flickr

1 https://www.flickr.com/services/rest/?method=flickr.test.echo&format=json&api\

2 _key=929033444e3a0d9a3859195d56d36552

→ LinkedIn

1 https://api.linkedin.com/v1/people/~:(id)?callback=firstNameResponse&error-ca\

2 llback=firstNameError

→ SoundCloud

1 https://api.soundcloud.com/tracks.json?client_id=YOUR_CLIENT_ID&callback=proc\

2 essTracks

Page 13: Apuntes Javascript Avanzado

4. CORS

..

→ Enable cross→ Cross-domain Ajax with Cross-Origin Resource Sharing | Nicholas C. Zakas→ Cross-Origin Resource Sharing | w3.org

Cross-Origin Resource Sharing (CORS) permite realizar peticiones a otros dominios siemprey cuando el dominio de destino (servidor) esté de acuerdo en recibir peticiones del dominio deorigen.

Es una tecnología (especificación W3C) implementada en los navegadores actuales, que inter-cambia ciertas cabeceras HTTP con el servidor de destino para saber si debe permitir o no elintercambio de datos.

Cada vez más API’s publicas permiten CORS pero la mayoria ofrecen solo JSONP.

4.1 Peticiones CORS sencillas

con jQuery

..

→ Ejemplo CORS request (HTML)→ Ejemplo CORS request (JSON)

Una peticion CORS utilizando jQuery es, en la mayoria de los casos, transparente al cliente, yaque si nuestro navegador lo soporta y el servidor (por ejemplo GitHub) está preparado, puedeser tan sencillo como esto:

1 $.ajax({

2 url: "https://api.github.com/users/juanmaguitar/repos",

3 success: function( response_json ) {

4 console.info (response_json);

5 }

6 });

8

Page 14: Apuntes Javascript Avanzado

5. El valor de this

..

→ value of this | quirksmode.org

El valor this es una propiedad del Contexto de Ejecución

1 activeExecutionContext = {

2 VO: {...},

3 this: thisValue

4 };

El valor de this queda determinado al entrar en el contexto de ejecución (al llamar a la función)y no se puede cambiar mientras el código está corriendo en el contexto

El valor de this en el Código Global es siempre el Objeto Global

1 // explicit property definition of

2 // the global object

3 this.a = 10; // global.a = 10

4 alert(a); // 10

5

6 // implicit definition via assigning

7 // to unqualified identifier

8 b = 20;

9 alert(this.b); // 20

10

11 // also implicit via variable declaration

12 // because variable object of the global context

13 // is the global object itself

14 var c = 30;

15 alert(this.c); // 30

El valor de this en el Código de Función depende de cómo se llame a esta función

5.1 En una función global

Cuando se llama a una función que está a nivel de programa, el valor de su this correspondeal objeto global

9

Page 15: Apuntes Javascript Avanzado

El valor de this 10

1 function f1(){

2 return this;

3 }

4 >>> f1() === window; // global object

5 true

Nota: En ECMASCRIPT 5 el valor de this no se convierto al global cuando es null o undefined

1 function f2(){

2 "use strict"; // see strict mode

3 return this;

4 }

5 >>> f2() === undefined;

6 true

..

→ ECMAScript 5 support in Mozilla | MDN→ ECMA 262

5.2 En un método de un objeto

Cuando una función es llamada comométodo de un objeto, su this se corresponde con el objetosobre el que se la llama

1 var o = {

2 prop: 37,

3 f: function() {

4 return this.prop;

5 }

6 };

7 >>> o.f() === 37

8 true

1 var o = {prop: 37};

2 function independent() {

3 return this.prop;

4 }

5 o.f = independent;

6 >>> o.f() === 37

7 true

… la manera en que se define el método no afecta al valor de this

Page 16: Apuntes Javascript Avanzado

El valor de this 11

1 o.b = {g: independent, prop: 42};

2 >>> o.b.g() === 42

3 true

… a this se le asigna el objeto más “cercano“ (la referencia más inmediata)

5.3 En un metodo de un prototipo

Si el método pertenece a un objeto que está en la cadena de prototipos, su this también secorresponderá con el objeto sobre el que se le llama

1 var Class = function (){}

2 Class.prototype.f = function(){ return this.a + this.b; };

3

4 var oInstance = new Class();

5 oInstance.a = 1;

6 oInstance.b = 4;

7

8 >>> oInstance.f() === 5

9 true

5.4 En una función constructora

Si la función se utiliza como constructor (con new), su this apuntará al nuevo objeto creado

1 function Class(){ this.a = 37; }

2 var o = new Class ();

3

4 >>> o.a === 37

5 true

6

7 function Class2(){ this.a = 37; return {a:38}; }

8 o = new Class2 ();

9

10 >>> o.a === 38

11 true

Page 17: Apuntes Javascript Avanzado

El valor de this 12

1 function Hero(name) {

2 this.name = name;

3 this.occupation = 'Ninja';

4 this.whoAreYou = function() {

5 return "I'm " + this.name + " and I'm a " + this.occupation;

6 }

7 }

8

9 var h1 = new Hero('Michelangelo');

10 var h2 = new Hero('Donatello');

11

12 >>> h1.whoAreYou() === "I'm Michelangelo and I'm a Ninja"

13 true

14 >>> h2.whoAreYou() === "I'm Donatello and I'm a Ninja"

15 true

16

17 >>> h1.occupation = "Turtle Ninja Super Hero";

18 >>> h1.whoAreYou() === "I'm Michelangelo and I'm a Turtle Ninja Super Hero"

19 true

5.5 Utilizando call o apply

Si en la llamada a la función utilizamos call o apply, podemos asociar this a un objetodeterminado (que pasaremos como parámetro)

1 function add(c, d){

2 return this.a + this.b + c + d;

3 }

4

5 var o = {a:1, b:3};

6

7 // The first parameter is the object to use as 'this', subsequent

8 // parameters are passed as arguments in the function call

9 >>> add.call(o, 5, 7) === 16; // 1 + 3 + 5 + 7 = 16

10 true

11

12 // The first parameter is the object to use as 'this', the second is an array\

13 whose

14 // members are used as the arguments in the function call

15 >>> add.apply(o, [10, 20]) === 34 // 1 + 3 + 10 + 20 = 34

16 true

Page 18: Apuntes Javascript Avanzado

El valor de this 13

5.6 Utilizando bind

Tambien podemos hacer un bind a una función y asociarle de forma permanente como this elobjeto que queramos (que utilizará siempre, independientemente de cómo se la llame)

1 Function.prototype.bind = function(scope) {

2 var _function = this;

3 return function() {

4 return _function.apply(scope, arguments);

5 }

6 }

7

8 alice = {

9 name: "alice"

10 }

11

12 eve = {

13 name: "eve",

14 talk: function(greeting) {

15 return (greeting + ", my name is " + this.name);

16 }.bind(alice) // <- bound to "alice"

17 }

18

19 >>> eve.talk("hello") === "hello, my name is alice"

20 true

21 >>> eve.talk.call({name:"paco"},"HOLA") === "HOLA, my name is alice"

22 true

5.7 En una función asociada a un evento

Cuando una función se utiliza como event handler, su this queda asociado al elemento queprovoca el evento

1 // When called as a listener, turns the related element blue

2 function bluify(e){

3 this.style.backgroundColor = '#A5D9F3';

4 e.preventDefault();

5 }

6

7 // Get a list of every link in the document

8 var elements = document.getElementsByTagName('a');

9

10 // Add bluify as a click listener so when the element is clicked on,

11 // it turns blue

Page 19: Apuntes Javascript Avanzado

El valor de this 14

12 for(var i=0 ; i<elements.length ; i++){

13 elements[i].addEventListener('click', bluify, false);

14 }

Page 20: Apuntes Javascript Avanzado

6. PrototypeTodas las funciones tienen una propiedad prototype que contiene inicialmente un objeto

Podemos añadir propiedades ymétodos a este objeto prototype. También podemos reemplazarlocompletamente por otro objeto

Al crear objetos usando una función como constructor (con new), estos objetos adquieren unenlace secreto (__proto__ en Firebug) que apunta al prototipo de esta función constructora (“clase” ) lo que les permite acceder a sus propiedades (y métodos) como si fueran propias.

Las propiedades propias del objetos tienen prioridad sobre las propiedades del prototipo con elmismo nombre

1 function Person( gender ) {

2 this.gender = gender;

3 console.log (this.gender + ' instantiated');

4 }

5

6 var peter = new Person('Male');

7 => Male instantiated

8

9 var mary = new Person('Female');

10 => Female instantiated

11

12 peter.gender === 'Male'

13 => true

14 mary.gender === 'Female'

15 => true

16

17 Person.prototype.type = 'Human being';

18

19 peter.type === 'Human being'

20 => true

21 mary.type === 'Human being'

22 => true

23

24 peter.type = 'Super Hero';

25

26 peter.type === 'Super Hero'

27 => true

28 mary.type === 'Human being'

29 => true

15

Page 21: Apuntes Javascript Avanzado

Prototype 16

..

See example online

Cadena de Prototipos

Los objetos disponen de lo que se llama la cadena de prototipos

• Si un objeto foo no dispone de la propiedad bar al hacer foo.bar, Javascript buscará estápropiedad en su prototipo (el de la función constructora que lo creó)

• Si no lo encuentra ahí, lo buscará en el prototipo de su prototipo• Y asi hasta llegar al objeto de más alto nivel, el objeto Object

cadena de prototipos

1 function Person(gender) {

2 this.gender = gender;

3 this.shoutGender = function () {

4 return this.gender.toUpperCase();

5 }

6 }

7

8 Person.prototype.sayGender = function() {

9 return this.gender;

10 };

11

12 var eric = new Person('Male');

13

Page 22: Apuntes Javascript Avanzado

Prototype 17

14 eric.sayGender() === "Male"

15 => true

16 eric.shoutGender() === "MALE"

17 => true

18

19 var genderTeller = eric.sayGender;

20

21 genderTeller() === "Male"

22 => false

23 eric.sayGender() === "Male"

24 => true

25 genderTeller === Person.prototype.sayGender

26 => true

27

28 var Obj = {gender : 'Male'}

29

30 genderTeller.call(Obj) === "Male"

31 => true

32

33 var jessica = new Person('Female');

34 Person.prototype.shoutGender = function () {

35 return "From the Class, you’re " + this.gender.toUpperCase();

36 }

37

38 eric.shoutGender() === "MALE"

39 => true

40 jessica.shoutGender() === "FEMALE"

41 => true

42

43 delete eric.shoutGender

44 => true

45

46 eric.shoutGender() === "MALE"

47 => false

48 jessica.shoutGender() === "FEMALE"

49 => true

50 eric.shoutGender() === "From the Class, you’re MALE"

51 => true

52

53 eric.shoutGender = function () {

54 return "I’m eric and I’m " + this.gender.toUpperCase();

55 }

56

57 eric.shoutGender() === "From the Class, you’re MALE"

58 => false

59 eric.shoutGender() === "I’m eric and I’m MALE"

Page 23: Apuntes Javascript Avanzado

Prototype 18

60 => true

61 jessica.shoutGender() === "FEMALE"

62 => true

..

See example online

El metodo hasOwnProperty()

Cuando recorremos un objeto con un for-in pasamos por:

1 - Las propiedades propias del objeto

2 - Todas las propiedades accesibles a través de la cadena de prototipos

Con el método hasOwnProperty() podemos diferenciar las propiedades propias del objeto de lasdel prototipo

1 function Smartphone(name, version) {

2 this.name = name;

3 this.version = version;

4 this.getVersion = function(){ return this.version;}

5 }

6

7 Smartphone.prototype.price = 100;

8 => 100

9 Smartphone.prototype.rating = 3;

10 => 3

11

12 var iphone = new Smartphone('iphone', '5');

13

14 for (var prop in iphone) { console.log(prop + ' = ' + iphone[prop]); }

15 => name = iphone

16 => version = 5

17 => getVersion = function (){ return this.version;}

18 => price = 100

19 => rating = 3

20

21 iphone.hasOwnProperty('name');

22 => true

23 iphone.hasOwnProperty('price');

24 => false

… por tanto, para recorrer solamente las propiedades propias del objeto podemos hacer:

Page 24: Apuntes Javascript Avanzado

Prototype 19

1 for (var prop in iphone) {

2 if ( iphone.hasOwnProperty(prop) ) {

3 console.log(prop + ' = ' + iphone[prop]);

4 }

5 }

6 => name = iphone

7 => version = 5

8 => getVersion = function (){ return this.version;}

..

See example online

El metodo isPrototypeOf()

Cada objeto dispone del método isPrototypeOf() que nos dice si un objeto en cuestión se estáutilizando como prototipo de otro objeto

1 var monkey = {

2 hair: true,

3 feeds: 'bananas',

4 breathes: 'air'

5 };

6

7 function Human(name) {

8 this.name = name;

9 }

10

11 Human.prototype = monkey;

12

13 >>> var george = new Human('George');

14 >>> monkey.isPrototypeOf(george)

15 true

..

See example online

La propiedad constructor

El constructor del objeto prototype de una función constructora (clase), apunta a la funciónconstructora

_Ejemplo en Firefox:

Page 25: Apuntes Javascript Avanzado

Prototype 20

1 >>> function Dog(){ this.tail = true; }

2

3 >>>> Dog.prototype.constructor === Dog

4 true

5

6 >>>> Dog.prototype

7 Dog {}

8

9 >>> var myDog = new Dog()

10 >>> myDog.__proto__

11 Dog {}

12 >>> myDog.__proto__.constructor === Dog

13 true

Sustituyendo completamente el prototype

La cadena de prototipos es dinámica excepto cuando sustituimos completamente el objetoprototype

1 >>> function Dog(){this.tail = true;}

2 >>> var benji = new Dog();

3 >>> var rusty = new Dog();

4

5 >>> typeof(benji.say) === "function"

6 false

7

8 >>> Dog.prototype.say = function(){return 'Woof!';}

9

10 >>> typeof(benji.say) === "function"

11 true

12

13 >>> benji.constructor === Dog

14 true

15 >>> typeof(benji.constructor.prototype.say) === "function"

16 true

17 >>> benji.constructor.prototype.tail === undefined

18 true

Si reescribimos el prototype con otro objeto perdemos la “dinamicidad” del prototipo…

Page 26: Apuntes Javascript Avanzado

Prototype 21

1 >>> Dog.prototype = {paws:4, hair:true};

2

3 >>> benji.paws === undefined //old instance cannot locate new prototype

4 True

5 >>> typeof(benji.say) === "function" //but still can access old

6 prototype

7 true

8

9 >>> var lucy = new Dog()

10

11 >>> lucy.say !== undefined //new instance can locate new prototype

12 false

13 >>> lucy.paws !== undefined //but cannot access old prototype

14 true

15

16 >>> lucy.constructor === Object

17 true

18 >>> benji.constructor === Dog

19 true

…pero hay un comportamiento extraño

1 >>> lucy.constructor.prototype.paws === undefined // doesn’t exist?

2 true

3 >>> benji.constructor.prototype.paws === undefined // and here it does?

4 false

Si hubiéramos hecho

1 >>> Dog.prototype = {paws:4, hair:true};

2 >>> Dog.prototype.constructor = Dog;

…tendríamos lo que se espera

1 >>> lucy.constructor.prototype.paws === undefined // does exist!

2 false

3 >>> benji.constructor.prototype.paws === undefined // and here not!

4 true

Por tanto, siempre que sustituyamos completamente el prototype por otro objeto, convienerestituir el constructor de ese ‘prototype

Page 27: Apuntes Javascript Avanzado

7. Herencia7.1 Encadenamiento de Prototipos (Prototype

Chaining)

Es el mecanismo por defecto que describe el standard ECMA para implementar la herencia enJavascript

1 function Shape(){

2 this.name = 'shape';

3 this.toString = function() {return this.name;};

4 }

5

6 function TwoDShape(){

7 this.name = '2D shape';

8 }

9

10 function Triangle(side, height) {

11 this.name = 'Triangle';

12 this.side = side;

13 this.height = height;

14 this.getArea = function(){

15 return this.side * this.height / 2;

16 };

17 }

18

19 TwoDShape.prototype = new Shape();

20 Triangle.prototype = new TwoDShape();

21

22 TwoDShape.prototype.constructor = TwoDShape;

23 Triangle.prototype.constructor = Triangle;

24

25 >>> var myTriangle = new Triangle(5,10)

26

27 >>> myTriangle.getArea() === 25

28 true

29 >>> myTriangle.hasOwnProperty("getArea")

30 true

31

32 >>> myTriangle.toString() === "Triangle"

33 true

22

Page 28: Apuntes Javascript Avanzado

Herencia 23

34 >>> myTriangle.hasOwnProperty("toString")

35 false

36

37 >>> myTriangle.constructor === Triangle

38 true

39

40 >>> myTriangle instanceof Shape

41 true

42 >>> myTriangle instanceof TwoDShape

43 true

44 >>> myTriangle instanceof Triangle

45 true

46

47 >>> Shape.prototype.isPrototypeOf(myTriangle)

48 true

49 >>> TwoDShape.prototype.isPrototypeOf(myTriangle)

50 true

51 >>> Triangle.prototype.isPrototypeOf(myTriangle)

52 true

53

54 >>> String.prototype.isPrototypeOf(myTriangle)

55 false

Encadenamiento de prototipos

Page 29: Apuntes Javascript Avanzado

Herencia 24

1 >>> TwoDShape.prototype.hasOwnProperty("toString")

2 true

3 >>> Shape.prototype.hasOwnProperty("toString")

4 false

5 >>> myTriangle.hasOwnProperty("getArea")

6 true

7 >>> Triangle.prototype.hasOwnProperty("getArea")

8 false

9

10 >>> var td = new TwoDShape();

11

12 >>> td.constructor === TwoDShape

13 true

14 >>> td.toString() === "2D shape"

15 true

16

17 >>> var s = new Shape()

18

19 >>> s.constructor === Shape

20 true

21 >>> s.toString() === "shape"

22 true

Se recomienda mover a los prototipos todas las propiedades/métodos reutilizables, y dejar lasno-reutilizables como propias de las instancias

7.2 Moviendo metodos re-utilizables al prototypo

1 function Shape(){}

2

3 // augment prototype

4 Shape.prototype.name = 'shape';

5 Shape.prototype.toString = function() {

6 return this.name;

7 };

8

9 function TwoDShape(){}

10

11 // take care of inheritance

12 TwoDShape.prototype = new Shape();

13 TwoDShape.prototype.constructor = TwoDShape;

14

15 // augment prototype

16 TwoDShape.prototype.name = '2D shape';

17

Page 30: Apuntes Javascript Avanzado

Herencia 25

18 function Triangle(side, height) {

19 this.side = side;

20 this.height = height;

21 }

22

23 // take care of inheritance

24 Triangle.prototype = new TwoDShape();

25 Triangle.prototype.constructor = Triangle;

26

27 // augment prototype

28 Triangle.prototype.name = 'Triangle';

29 Triangle.prototype.getArea = function(){

30 return this.side * this.height / 2;

31 };

32

33 var myTriangle = new Triangle(5,10)

Moving shared methods

1 >>> TwoDShape.prototype.hasOwnProperty("toString")

2 false

3 >>> Shape.prototype.hasOwnProperty("toString")

4 true

5 >>> myTriangle.hasOwnProperty("getArea")

6 false

7 >>> Triangle.prototype.hasOwnProperty("getArea")

Page 31: Apuntes Javascript Avanzado

Herencia 26

7.3 Herencia sólo del prototipo

Es un mecanismo más eficiente ya que no se crean nuevas instancias sólo para implementar laherencia

La búsqueda por la cadena de prototipos es más rápida (ya que no hay cadena de prototipos,todos los prototipos apuntan al mismo objeto )

1 function Shape(){}

2

3 // augment prototype

4 Shape.prototype.name = 'shape';

5 Shape.prototype.toString = function() {

6 return this.name;

7 };

8

9 function TwoDShape(){}

10

11 // take care of inheritance

12 TwoDShape.prototype = Shape.prototype;

13 TwoDShape.prototype.constructor = TwoDShape;

14

15 // augment prototype

16 TwoDShape.prototype.name = '2D shape';

17

18 function Triangle(side, height) {

19 this.side = side;

20 this.height = height;

21 }

22

23 // take care of inheritance

24 Triangle.prototype = TwoDShape.prototype;

25 Triangle.prototype.constructor = Triangle;

26

27 // augment prototype

28 Triangle.prototype.name = 'Triangle';

29 Triangle.prototype.getArea = function(){return this.side * this.height /

30 2;}

Page 32: Apuntes Javascript Avanzado

Herencia 27

Herencia solo del prototipo

1 >>> Triangle.prototype.hasOwnProperty("getArea")

2 true

3 >>> Shape.prototype.hasOwnProperty("getArea")

4 true

5 >>> Shape.prototype.hasOwnProperty("toString")

6 true

7 >>> Triangle.prototype.hasOwnProperty("toString")

8 true

El problema de este método es que al apuntar todos los prototipos al mismo objeto, cuandomodificamos alguno de los prototipos, los modificamos todos.

1 >>> Triangle.prototype.name = 'Triangle';

2 >>> var s = new Shape()

3 >>> s.name

4 "Triangle"

7.4 Función constructora temporal F()

Para solucionar esto podemos usar una función constructora temporal F() vacia y asignarle asu prototype el prototipo de la función constructora padre

De esta manera podemos hacer new F() y crear objetos que no tengan propiedades por si mismospero que hereden todo del prototype del padre

Page 33: Apuntes Javascript Avanzado

Herencia 28

1 function Shape(){}

2

3 // augment prototype

4 Shape.prototype.name = 'shape';

5 Shape.prototype.toString = function() {return this.name;};

6

7 function TwoDShape(){}

8

9 // take care of inheritance

10 var F = function(){};

11 F.prototype = Shape.prototype;

12 TwoDShape.prototype = new F();

13 TwoDShape.prototype.constructor = TwoDShape;

14

15 // augment prototype

16 TwoDShape.prototype.name = '2D shape';

17

18 function Triangle(side, height) {

19 this.side = side;

20 this.height = height;

21 }

22

23 // take care of inheritance

24 var F = function(){};

25 F.prototype = TwoDShape.prototype;

26 Triangle.prototype = new F();

27 Triangle.prototype.constructor = Triangle;

28

29 // augment prototype

30 Triangle.prototype.name = 'Triangle';

31 Triangle.prototype.getArea = function(){return this.side * this.height /

32 2;};

33

34 >>> var myTriangle = new Triangle(5, 10);

35 >>> myTriangle.getArea() === 25

36 true

37 >>> myTriangle.toString() === "Triangle"

38 true

39

40 >>> var myShape = new Shape()

41 >>> myShape.name === "shape"

42 True

43

44 >>> Triangle.prototype.name = "super-Triangle"

45 >>> myTriangle.name === "super-Triangle"

46 true

Page 34: Apuntes Javascript Avanzado

Herencia 29

47 >>> myShape.name === "shape"

48 true

Funcion Intermedia F()

1 >>> myTriangle.__proto__.__proto__.__proto__.constructor === Shape

2 true

3 >>> myTriangle.__proto__.__proto__.constructor === TwoDShape

4 true

5

6 >>> myTriangle.__proto__.constructor === Triangle

7 true

7.5 Encapsulando la herencia en una función

Podemos encapsular este código en una función extend que nos facilite implementar la herencia

1 function extend(Child, Parent) {

2 var F = function(){};

3 F.prototype = Parent.prototype;

4 Child.prototype = new F();

5 Child.prototype.constructor = Child;

6 }

7

8 function Shape(){};

9 // augment prototype

10 Shape.prototype.name = 'shape';

Page 35: Apuntes Javascript Avanzado

Herencia 30

11 Shape.prototype.toString = function() {return this.name;};

12

13 function TwoDShape(){};

14 extend(TwoDShape, Shape);

15 TwoDShape.prototype.name = '2D shape';

16

17 function Triangle(side, height) {

18 this.side = side;

19 this.height = height;

20 }

21 extend(Triangle, TwoDShape);

22 Triangle.prototype.name = 'Triangle';

23 Triangle.prototype.getArea = function(){return this.side * this.height /

24 2;};

25

26 >>> var myTriangle = new Triangle(5, 10);

27 >>> myTriangle.getArea() === 25

28 true

29 >>> myTriangle.toString() === "Triangle"

30 true

31

32 >>> Triangle.prototype.hasOwnProperty("getArea")

33 true

34 >>> Shape.prototype.hasOwnProperty("getArea")

35 false

36 >>> Shape.prototype.hasOwnProperty("toString")

37 true

38 >>> Triangle.prototype.hasOwnProperty("toString")

39 false

7.6 Robando el constructor

Otro patrón para implementar la herencia es llamando al constructor padre desde el constructorhijo mediante apply o call.

De esta manera las propiedades del padre son recreadas como propias en el hijo (son valoresnuevos, no referencias a objetos)

Page 36: Apuntes Javascript Avanzado

Herencia 31

1 function Shape(id) {

2 this.id = id;

3 }

4 Shape.prototype.name = 'shape';

5 Shape.prototype.toString = function(){return this.name;};

6

7 function Triangle() {

8 Shape.apply(this, arguments);

9 }

10 Triangle.prototype = new Shape();

11 Triangle.prototype.name = 'Triangle';

12

13 >>> var myTriangle = new Triangle(101)

14 >>> myTriangle.name === "Triangle"

15 true

16 >>> myTriangle.toString() === "Triangle"

17 true

18 >>> myTriangle.hasOwnProperty("name")

19 false

20 >>> myTriangle.hasOwnProperty("id")

21 true

22 >>> myTriangle.hasOwnProperty("toString")

23 false

Page 37: Apuntes Javascript Avanzado

8. Contexto de EjecuciónHay 3 Tipos de Código Ejecutable en ECMAScript:

• Código Global: El código que está a nivel de Programa. No incluye el código que estádentro de las funciones

• Código de Función: El código dentro de una función• Código de Eval: El código dentro de una expresión eval

Cuando alguno de estos tipos de código se ejecuta, lo hace dentro de un Contexto de Ejecución

Los contextos de ejecución forman una pila.Es decir, primero se ejecutará código global en su propio contexto de ejecución, este código puedellamar a una función que se ejecutará en su propio contexto de ejecución, y así sucesivamenteCon cada nueva invocación a una función se entra en un nuevo contexto de ejecución

..

→ Activation Object & Variable Object | perfectionkills.com→ Execution Contexts | dmitrysoshnikov.com→ Functions and Execution Contexts | blog.tuenti.com

8.1 Objeto Variable (Variable Object)

1 AbstractVO (generic behavior of the variable instantiation process)

2 ║

3 ╠══> GlobalContextVO

4 ║ (VO === this === global)

5 ║

6 ╚══> FunctionContextVO

7 (VO === AO, <arguments> object and <formal parameters> are added)

Cada contexto de ejecución tiene lo que se llama un Objeto Variable.

Las variables ( var, VariableDeclaration), funciones (FunctionDeclaration, FD) y parámetrosformales de la función declaradas en el contexto son añadidas como propiedades de este ObjetoVariable

En un código global

Cuando se entra en el contexto de ejecución de un Código Global, se utiliza un Objeto Globalcomo Objeto Variable.

32

Page 38: Apuntes Javascript Avanzado

Contexto de Ejecución 33

1 VO(globalContext) === global;

El Objeto Global es el objeto que se crea antes de entrar en ningún contexto de ejecución. Suspropiedades son accesibles desde cualquier parte del código, y existe durante la ejecución delprograma.

1 /* remember that `this` refers to global object when in global scope */

2 var GLOBAL_OBJECT = this;

3 var foo = 1;

4 GLOBAL_OBJECT.foo; // 1

5 foo === GLOBAL_OBJECT.foo; // true

6 function bar(){}

7 typeof GLOBAL_OBJECT.bar; // "function"

8 GLOBAL_OBJECT.bar === bar; // true

1 >>> var a = 'test'

2 >>> a === 'test' // directly, is found in VO(globalContext): "test"

3 true

4 >>> window === this

5 true

6 >>> window['a'] === 'test' // indirectly via global===VO(globalContext):

7 true

8 >>> a === this.a

9 true

10 >>> var aKey = 'a';

11 >>> window[aKey] === 'test' // indirectly, with dynamic property name: "test"

12 true

En un código de función

Cuando se entra en el contexto de ejecución de un Código de Función, se utiliza un Objeto deActivación (AO) como Objeto Variable.

1 VO(functionContext) === AO;

Un Objeto de Activación es creado al entrar en el contexto de una función e inicializado con lapropiedad arguments (objeto arguments con las propiedades callee y length)

..

→ Objeto de Activación | interglacial.com

Page 39: Apuntes Javascript Avanzado

Contexto de Ejecución 34

1 function foo(x, y, z) {

2

3 // quantity of defined function arguments (x, y, z)

4 console.log(foo.length == 3); // true

5

6 // quantity of really passed arguments (only x, y)

7 console.log(arguments.length === 2); // true

8

9 // reference of a function to itself

10 console.log(arguments.callee === foo); // true

11

12 // parameters sharing

13 console.log(x === arguments[0]); // true

14 console.log(x === 10); // true

15

16 arguments[0] = 20;

17 console.log(x === 20); // true

18

19 var x = 30;

20 console.log(arguments[0] === 30); // true

21

22 // however, if we don’t pass and argument z

23 // related index-property of the arguments

24 // is not ‘linked’ with z

25

26 var z = 40;

27 console.log(arguments[2] === undefined); // true

28 console.log(z === 40); // true

29

30 arguments[2] = 50;

31 console.log(arguments[2] === 50); // true

32

33 console.log(arguments);

34 console.log(arguments[2]);

35

36 }

37

38 foo(10, 20);

..

→ Variable Object | dmitrysoshnikov.com→ Execution context within JavaScript (Variable/Activation Object) | gist.github.com

Page 40: Apuntes Javascript Avanzado

Contexto de Ejecución 35

8.2 Fases de Procesamiento del Código

El procesamiento del código (al llamar a una función) se divide principalmente en 2 fases:

1. La entrada en el Contexto de Ejecución (preparamos el terreno)2. La ejecución del Código

1. Al entrar en el Contexto de Ejecución

Al entrar en el Contexto de Ejecución (pero antes de la ejecución del código), el Objeto Variablese rellena con las siguientes propiedades (y en este orden):

1.- Para cada Parámetro Formal de la función (si estamos en el contexto de ejecución de unafunción)

• Se crea una propiedad en elObjeto Variable con el mismo nombre y valor que el parámetroformal

• Si no se le pasa el parámetro a la función, se crea una propiedad en el Objeto Variable conel mismo nombre y con valor undefined

2.- Para cada Declaración de Función (FunctionDeclaration, FD)

• Se crea una propiedad en elObjeto Variable con el mismo nombre y con su correspondienteobjeto-función como valor

• Si el Objeto Variable ya contiene una propiedad con el mismo nombre, se reemplaza suvalor y atributos

3.- Para cada Declaración de Variable (var, VariableDeclaration)

• Se crea una propiedad en el Objeto Variable con el nombre de la variable y con el valorundefined

• Si el Objeto Variable ya contiene una propiedad con el mismo nombre, esta declaración devariable NO reemplaza su valor

1 function test(a, b) {

2 var c = 10;

3 function d() {}

4 var e = function _e() {};

5 (function x() {});

6 }

7 test(10); // call

… al entrar en el contexto de la function test con el parámetro pasado 10, elObjeto de Activaciónqueda asi:

Page 41: Apuntes Javascript Avanzado

Contexto de Ejecución 36

1 AO(test) = {

2 a: 10,

3 b: undefined,

4 c: undefined,

5 d: <reference to FunctionDeclaration "d">

6 e: undefined

7 };

2. Al empezar la Ejecución del Código

Al empezar la Ejecución del Código el Objeto de Activación (Objeto Variable) ya está relleno consus propiedades (aunque no todas ellas tienen los valores reales pasados, la mayoría tienen elvalor inicial undefined ):

… en el ejemplo anterior el Objeto de Activación (AO/VO) se modificaría asi durante lainterpretación del código

1 AO['c'] = 10;

2 AO['e'] = <reference to FunctionExpression "_e">;

1 (function () {

2

3 console.log (typeof(x) === 'function'); // true

4

5 var x = 10;

6 console.log (x === 10); // true

7

8 x = 20;

9 function x() {}

10

11 console.log (x === 20); // true

12

13 }())

… al entrar en el Contexto de Ejecución

Page 42: Apuntes Javascript Avanzado

Contexto de Ejecución 37

1 VO = {};

2 VO['x'] = <reference to FunctionDeclaration "x">

3 // found var x = 10;

4 // if function "x" would not be already defined

5 // then "x" be undefined, but in our case

6 // variable declaration does not disturb

7 // the value of the function with the same name

8 VO['x'] = <the value is not disturbed, still function>

… y al ejecutar el código

1 VO['x'] = 10;

2 VO['x'] = 20;

8.3 Tipos de Funciones

Una Function Declaration (FD) es una función que:

• Tiene un nombre obligatorio• En el código puede aparecer a nivel raíz o en el cuerpo de otra función• Se guardan en elObjeto Variable delContexto de Ejecución (es decir, en tiempo de ejecuciónya están disponibles)

1 foo();

2

3 function foo() {

4 alert('foo');

5 }

Una Function Expression (FE) es una función que:

• Puede tener un nombre opcional• En el código sólo puede aparecer en la posición de una expresión• Su definición no afecta al Objeto Variable del Contexto de Ejecución• Se crea en tiempo de ejecución

Page 43: Apuntes Javascript Avanzado

Contexto de Ejecución 38

1 var foo = function () {

2 alert('foo');

3 };

4

5 foo();

1 // in parentheses (grouping operator) can be only an expression

2 (function foo() {});

3 // in the array initialiser – also only expressions

4 [function bar() {}];

5 // comma also operates with expressions

6 1, function baz() {};

… pese a que una FE puede tener nombre, las distinguimos de una FD en que las FE sólo puedeestar en la posición de una expresión

1 // FE is not available neither before the definition

2 // (because it is created at code execution phase),

3 alert(foo); // "foo" is not defined

4 (function foo() {});

5 // nor after, because it is not in the VO

6 alert(foo); // "foo" is not defined

… las FE son creadas en tiempo de ejecución

1 var foo = {};

2

3 (function initialize() {

4 var x = 10;

5 foo.bar = function () {

6 alert(x);

7 };

8 })();

9

10 foo.bar(); // 10;

11 alert(x); // "x" is not defined

Si queremos llamar a una función directamente desde su definición y la función no está en unaexpression position, tendremos que encerrarla entre paréntesis

Con esto, lo que hacemos en transformar manualmente la función en una Function Expression(FE)

Page 44: Apuntes Javascript Avanzado

Contexto de Ejecución 39

1 (function foo(x) {

2 alert(x);

3 })(1); // OK, it's a call, not a grouping operator, 1

1 1, function () {

2 alert('anonymous function is called');

3 }();

4

5 // or this one

6 !function () {

7 alert('ECMAScript');

8 }();

9

10 // and any other manual

11 // transformation

… en el caso en que la función ya esté en una expression position, no hacen falta los paréntesis

..

→ Functions | dmitrysoshnikov.com→ Understanding Delete | perfectionkills.com→ Functions and Scope | MDN

8.4 Hoisting

Se le llama Hoisting al efecto particular que tiene el intérprete interno de separar la definiciónde una variable en declaración e inicializacion, y de “mover” las declaraciones al principio de lafunción (aplicandoles el valor undefined)

1 var joe = “plumber”

2 // var joe <- declaración

3 // joe = “plumber” <- inicializacion

Este efecto es particular de javascript y puede producir efectos “inesperados”

Page 45: Apuntes Javascript Avanzado

Contexto de Ejecución 40

1 var myvar = 'my value';

2 (function() {

3 alert(myvar);

4 var myvar = 'local value';

5 })();

6 // que valor devolverá el alert??

Es por esto, que se considera una buena practica en Javascript declarar todas las variables alprincipio de la función

..

→ Hoisting Explained | net.tutsplus.com→ Hoisting en Javascript | etnassoft.com

Page 46: Apuntes Javascript Avanzado

9. ScopeEn la creación de una función se define su propiedad (interna) Scope que es la lista jerárquicade todos los Objetos Variable de sus “padres” (que estan por encima de su contexto)

En la llamada a una función se crea el ScopeChain (cadena de ámbitos) del contexto de ejecuciónque es: su Objeto de Activación + la propiedad Scope de esta función

La resolución de identificadores ( Identifier Resolution ) es el proceso en el cual se determina aqué Objeto Variable dentro de la cadena de ámbitos pertenece una variable determinada

1 var x = 10;

2

3 function foo() {

4 var y = 20;

5 function bar() { alert(x + y); }

6 return bar;

7 }

8

9 foo()(); // 30

Al llamar a la función su Contexto de Ejecución queda asi…

1 activeExecutionContext = {

2 VO: {...}, // or AO

3 this: thisValue,

4 Scope: [ // Scope chain

5 // list of all variable objects

6 // for identifiers lookup

7 ]

8 };

donde su Scope seria…

1 Scope = AO + [[Scope]]

es decir…

1 Scope = AO + [VO1, VO2, ..., VOn]

donde…

41

Page 47: Apuntes Javascript Avanzado

Scope 42

1 var VO1 = {__parent__: null, ... other data}; -->

2 var VO2 = {__parent__: VO1, ... other data}; -->

3 // etc.

..

→ Scope Chain | dmitrysoshnikov.com→ Functions Objects and Scope | p2pu.org→ Javascript Scope and Binding | alternateidea.com→ Functions and functions scope | MDN

9.1 Ciclo de vida de una Función

1 var x = 10;

2

3 function foo() {

4 var y = 20;

5

6 function bar() {

7 var z = 30;

8 alert(x + y + z);

9 }

10

11 bar();

12 }

13

14 foo(); // 60

Partimos del objeto global ya creado, que además es la Variable Objeto del Contexto Global…

1 globalContext.VO === Global = {

2 x: 10

3 foo: <reference to function>

4 };

En la creación de “foo”, la propiedad “Scope” de “foo” es…

1 foo.[[Scope]] = [

2 globalContext.VO

3 ];

En la activación de “foo” (al llamar a la función, al entrar en su contexto) , el objeto de activacióndel contexto “foo” es…

Page 48: Apuntes Javascript Avanzado

Scope 43

1 fooContext.AO = {

2 y: 20,

3 bar: <reference to function>

4 };

Y el Scope Chain del contexto “foo” queda…

1 fooContext.Scope = fooContext.AO + foo.[[Scope]] // i.e.:

2

3 fooContext.Scope = [

4 fooContext.AO,

5 globalContext.VO

6 ];

En la creación de la función interna “bar” su _Scope es…_

1 bar.[[Scope]] = [

2 fooContext.AO,

3 globalContext.VO

4 ];

En la activación de “bar”, el objeto de activación del contexto “bar” es…

1 barContext.AO = {

2 z: 30

3 };

Y el Scope Chain de “bar” queda…

1 barContext.Scope = barContext.AO + bar.[[Scope]] // i.e.:

2

3 barContext.Scope = [

4 barContext.AO,

5 fooContext.AO,

6 globalContext.VO

7 ];

Asi, la resolución de identificadores para “x”, “y” y “z” se haría asi…

Page 49: Apuntes Javascript Avanzado

Scope 44

1 - "x"

2 -- barContext.AO // not found

3 -- fooContext.AO // not found

4 -- globalContext.VO // found - 10

1 - "y"

2 -- barContext.AO // not found

3 -- fooContext.AO // found - 20

1 - "z"

2 -- barContext.AO // found – 30

Page 50: Apuntes Javascript Avanzado

10. Patrones de CódigoEstos patrones ayudan a organizar el código, a mejorar su performance, a la creación de objetosy a aplicar características perdidas en JS como “propiedades privadas”

10.1 Separación de capas

Las 3 capas de una página web son

• Content (HTML)• Presentation (CSS)• Behavior (JavaScript)

Estas capas deben estar lo más separadas posibles, es decir no debemos tocar directamente desdeuna capa elementos pertenecientes a otra capa.

Esto, en la capa de comportamiento (Javascript) significa:

• Evitar los tags <script> con código javascript dentro del marcado (intentar utilizar sólolos que carguen otros archivos)

• Evitar attributos inline en las etiquetas como onclick, onmouseover etc.. (mejor capturar elevento desde un archivo externo utilizando los métodos addEventListener o ‘attachEvent)

• No alterar directamente propiedades del CSS (en todo caso, añadir o quitar clases)• Añadir dinámicamente el marcado que no tenga sentido con el Javascript desactivado• Que el código JS quede en archivos *.js separados que incluyamos en la página

10.2 Namespaces

[creación de objetos]

Debemos evitar el uso de variables globales para minimizar las posibles colisiones de nombresde variable.

Una manera de hacer esto es utilizando un Namespace, es decir, creando un único objeto globaly haciendo que todas las variables y funciones que necesitemos sean propiedades de este objeto

45

Page 51: Apuntes Javascript Avanzado

Patrones de Código 46

1 // global namespace

2 var MYAPP = MYAPP || {};

3 // sub-object

4 MYAPP.event = {};

5 // object together with the method declarations

6 MYAPP.event = {

7 addListener: function(el, type, fn) {

8 // .. do the thing

9 },

10 removeListener: function(el, type, fn) {

11 // ...

12 },

13 getEvent: function(e) {

14 // ...

15 }

16 // ... other methods or properties

17 };

18 MYAPP.dom = {};

19 MYAPP.dom.style = {};

Podemos crear un método que nos ayude con la creación de elementos en el Namespace

1 var MYAPP = MYAPP || {};

2 MYAPP.namespace = function(name){

3 var parts = name.split('.');

4 var current = MYAPP;

5 for (var i in parts) {

6 if (!current[parts[i]]) {

7 current[parts[i]] = {};

8 }

9 current = current[parts[i]];

10 }

11 }

12 >>> typeof (MYAPP.dom.style) === "object"

13 TypeError: MYAPP.dom is undefined

14 >>> MYAPP.namespace('dom.style')

15 >>> typeof (MYAPP.dom.style) === "object"

16 true

10.3 Init-time branching

[performance]

Cuando sabemos que cierta condición no va a cambiar durante la ejecución del programa,podemos hacer que nuestro código evalué esa condición una sola vez (durante la carga delprograma) y que devuelva funciones “customizadas” según estas condiciones (ej.- FeatureDetection)

Page 52: Apuntes Javascript Avanzado

Patrones de Código 47

1 var MYAPP = {};

2 MYAPP.event = {

3 addListener: null,

4 removeListener: null

5 };

6

7 if (typeof window.addEventListener === 'function') {

8 MYAPP.event.addListener = function(el, type, fn) {

9 el.addEventListener(type, fn, false);

10 };

11 MYAPP.event.removeListener = function(el, type, fn) {

12 el.removeEventListener(type, fn, false);

13 };

14 } else if (typeof document.attachEvent === 'function'){ // IE

15 MYAPP.event.addListener = function(el, type, fn) {

16 el.attachEvent('on' + type, fn);

17 };

18 MYAPP.event.removeListener = function(el, type, fn) {

19 el.detachEvent('on' + type, fn);

20 };

21 } else { // older browsers

22 MYAPP.event.addListener = function(el, type, fn) {

23 el['on' + type] = fn;

24 };

25 MYAPP.event.removeListener = function(el, type, fn) {

26 el['on' + type] = null;

27 };

28 };

10.4 Lazy Definition

[performance]

La Lazy Definition es muy parecida al Init-time branching, con la diferencia es que aquí la“customización” se realiza solo cuando se llama a la función por primera vez

1 var MYAPP = {};

2 MYAPP.myevent = {

3 addListener: function(el, type, fn){

4 if (typeof el.addEventListener === 'function') {

5 MYAPP.myevent.addListener = function(el, type, fn) {

6 el.addEventListener(type, fn, false);

7 };

8 } else if (typeof el.attachEvent === 'function'){

9 MYAPP.myevent.addListener = function(el, type, fn) {

10 el.attachEvent('on' + type, fn);

Page 53: Apuntes Javascript Avanzado

Patrones de Código 48

11 };

12 } else {

13 MYAPP.myevent.addListener = function(el, type, fn) {

14 el['on' + type] = fn;

15 };

16 }

17 MYAPP.myevent.addListener(el, type, fn);

18 }

19 };

10.5 Objeto de Configuración

[creación de objetos]

Cuando una función necesita muchos parámetros (más de 3 por ejemplo) es una buena ideaagruparlos en un objeto de configuración y utilizar éste como único parámetro (las propiedadesdel objeto serán los parámetros).

Algunas ventajas de utlizar un objeto de configuracion son:

• El orden no importa• Puedes setear sólo los parámetros que necesites• La función queda más escalable• El código queda más legible (los nombres de las propiedades están presentes en la llamada)

1 // a constructor that creates buttons

2 var MYAPP = {};

3 MYAPP.dom = {};

4 MYAPP.dom.Button = function(text, conf) {

5 var type = conf.type || 'submit';

6 var font = conf.font || 'Courier';

7 var color = conf.color || 'white';

8 var b = document.createElement('input');

9

10 b.type = type;

11 b.font = font;

12 b.color = color;

13 b.value = text;

14 return b;

15 }

16

17 document.body.appendChild(new MYAPP.dom.Button('puuush'));

18 var config = {

19 font: 'Arial, Verdana, sans-serif',

20 color: 'white'

Page 54: Apuntes Javascript Avanzado

Patrones de Código 49

21 };

22

23 var oMyButton = new MYAPP.dom.Button('puuush', config);

24 document.body.appendChild(oMyButton);

25 var oMyButton2 = new MYAPP.dom.Button('puuush', {color: 'red'});

26 document.body.appendChild(oMyButton2);

10.6 Propiedades y Métodos Privados

[creación de objetos]

Podemos definir propiedades/metodos privados en un objeto (aunque Javascript no tenga unasintaxis especial para ello) usando variables locales en la función constructora

1 var MYAPP = {};

2 MYAPP.dom = {};

3 MYAPP.dom.Button = function(text, conf) {

4 // private property

5 var _styles = {

6 font: 'Verdana',

7 border: '1px solid black',

8 color: 'black',

9 };

10 // private method

11 function _setStyles() {

12 for (var i in _styles) {

13 b.style[i] = conf[i] || _styles[i];

14 }

15 }

16 conf = conf || {};

17 var b = document.createElement('input');

18 b.type = conf['type'] || 'submit';

19 b.value = text;

20 _setStyles();

21 return b;

22 };

23

24 var oMyButton = new MYAPP.dom.Button('puuush', {color: 'blue'});

25 document.body.appendChild(oMyButton);

Para mejorar la legibilidad las variables/métodos privados se marcan con “_” delante del nombre

10.7 Métodos Privilegiados

[creación de objetos]

Page 55: Apuntes Javascript Avanzado

Patrones de Código 50

..

→ Private Members in JavaScript | Douglas Crockford

Metodos Privilegiados (según Douglas Crockford) son métodos públicos que pueden acceder amétodos o propiedades privadas

Actuan de filtro para hacer accesible alguna funcionalidad privada pero de una manera contro-lada

Son los metodos que definimos en la función constructora como parte del this, a diferencia delos metodos definidos en el prototipo.

1 var Person = function(options){

2 //private properties

3 var _name = options.name

4 var _birthYear = options.birthYear;

5 //private method

6 var _calculateAge = function(){

7 var today = new Date();

8 return today.getFullYear() - _birthYear;

9 }

10 //Privileged methods

11 this.getAge = function(){

12 return _calculateAge(); //calling private method

13 }

14 this.getName = function(){

15 return _name.toUpperCase(); //return private variable filtered

16 }

17 }

18

19 //new Person instance

20 var myPerson = new Person( {name:'Peter', birthYear:1983} );

21 >>> myPerson.getAge() === 29

22 true

23 >>> myPerson.name === 'Peter'

24 false

25 >>> myPerson.name === undefined

26 true

27 >>> myPerson.getName() === 'PETER'

28 true

10.8 Funciones Privadas como Métodos Públicos(Revealing Module Pattern )

[creación de objetos]

Page 56: Apuntes Javascript Avanzado

Patrones de Código 51

..

→ Private Members in JavaScript | Douglas Crockford→ JavaScript’s Revealing Module Pattern | blog.alexanderdickson.com

Si queremosmantener intacta una función para nuestro “código interno”, pero también queremosdar visibilidad externa a esa función, podemos asignar esa función a una propiedad pública.

A este patrón tambien se le conoce como Revealing Module Pattern

1 var MYAPP = {};

2 MYAPP.dom = (function(){

3 var _setStyle = function(el, prop, value) {

4 return 'setStyle';

5 };

6 var _getStyle = function(el, prop) {

7 return 'getStyle';

8 };

9 return {

10 setStyle: _setStyle,

11 getStyle: _getStyle,

12 yetAnother: _setStyle

13 }; })()

14 MYAPP.dom.setStyle = function(){ return ‘Hey, Ho, Let’s Go!!’ };

15 >>> MYAPP.dom.setStyle() === 'Hey, Ho, Let’s Go!!'

16 true

17 >>> MYAPP.dom.yetAnother() === 'setStyle'

18 true

10.9 Funciones Inmediatas

[creación de objetos]

Otro patrón útil para crear objetos es utilizar una función anónima que devuelva un objeto yejecutarla inmediatamente

De estamanera las variables dentro de la función son locales (declaradas con var) y son destruidascuando la función termina (siempre que no formen parte de un closure)

Page 57: Apuntes Javascript Avanzado

Patrones de Código 52

1 var MYAPP = {};

2 MYAPP.dom = (function() {

3 // initialization code...

4 function _private() { console.log('_private'); }

5 // ... body }

6 return {

7 getStyle: function(el, prop) {

8 console.log('getStyle');

9 _private();

10 },

11 setStyle: function(el, prop, value) {

12 console.log('setStyle');

13 }

14 };

15 })();

Otra manera de crear objetos es ejecutar directamente la función anónima y desde dentro hacerlas asignaciones que correspondan.

Podemos pasarle parámetros a esta función inmediata (normalmente relacionadas con el entorno)y hacer nuestro código más rápido y robusto

..

→ Self-Executing Anonymous Functions | Mark Dalgleish

1 var MYAPP = {};

2 (function(global, doc, ns, $, _undefined_){

3 console.log ( window === global);

4 console.log ( document === doc );

5 console.log ( ns === MYAPP );

6 console.log ( $ === jQuery );

7 ns.existClass = (function () {

8 if (doc.getElementsByClassName) {

9 return function(sClassName) {

10 return doc.getElementsByClassName(sClassName).length > 0;

11 }

12 }

13 else {

14 return function(sClassName) {

15 return $("*").hasClass(sClassName);

16 }

17 }

18 })();

19 })(window, document, MYAPP, jQuery);

20

Page 58: Apuntes Javascript Avanzado

Patrones de Código 53

21 MYAPP.existClass('hidden');

22 MYAPP.existClass('___hidden____');

… si te estas preguntando para qué se define el parámetro undefined prueba esto en Safari:

..

→ Understanding JavaScript’s ‘undefined’ | Angus Croll→ Type Checks | jQuery

1 undefined = 5;

2 (function( _undefined_ ){

3 console.log ( undefined === _undefined_ );

4 console.log ( typeof(undefined) === "undefined" );

5 console.log ( typeof(_undefined_) === "undefined" );

6 console.log ( myVar === undefined );

7 console.log ( myVar === _undefined_ ); // this is faster

8 console.log ( typeof(myVar) === "undefined" );

9 })();

10.10 Memoization

[performance]

..

→ Faster JavaScript Memoization For Improved Application Performance | Addy Osmani→ Memoization in JavaScript | Nicolas Garcia Belmonte

Esta técnica se utiliza para cachear resultados de operaciones costosas:

• Si no se ha realizado aun la operación, se realiza y se guarda en caché (objeto o array) conun identificador único

• Si ya se ha realizado, se devuelve el resultado directamente de la caché

Page 59: Apuntes Javascript Avanzado

Patrones de Código 54

1 var hasClassName = (function(){

2 var cache = { };

3 return function(element, className) {

4 var re = (cache[className] ||

5 (cache[className] = new RegExp('(?:^|\\s)' + className + '(?:\\s|$)') ));

6 return re.test(element.className);

7 };

8 })();

10.11 Patrón Modulo

[creación de objetos]

..

→ A JavaScript Module Pattern | http://yuiblog.com/

La idea del patrón modulo es la de encapsular la lógica privada y exponer solamente determinados métodos “público”.

Se aplica utilizando funciones autoejecutables que devuelven objetos

1 var functionUtils = (function(){

2 /* private `slice` method */

3 function slice(arrayLike, index) {

4 return Array.prototype.slice.call(arrayLike, index || 0);

5 }

6 return {

7 /* exposed, public `bind` method */

8 bind: function(fn, thisArg) {

9 var args = slice(arguments, 2);

10 return function() {

11 return fn.apply(thisArg, args.concat(slice(arguments)));

12 };

13 }

14 };

15 })();

10.12 Patrón Sandbox

[creación de objetos]

Page 60: Apuntes Javascript Avanzado

Patrones de Código 55

..

→ javascript-patterns | github shichuan

El patrón Sandbox soluciona dos problemas que hay con el patrón Namespace :

• No se pueden crear 2 versiones de la misma aplicación en la misma página ya que todocuelga del mismo objeto global (MYAPP)

• Largas rutas a resolver (y que escribir) para acceder a los metodos (‘MYAPP.utilities.array)

El patrón Sandbox utiliza una función constructora global, en vez de un objeto “estático” global.

Al pasarle una función fpCallback a este constructor, se creará un objeto (box) donde estafunción fpCallback tendrá disponible los metodos que necesita.

1 function Sandbox() {

2 // turning arguments into an array

3 var args = Array.prototype.slice.call(arguments),

4 // the last argument is the callback

5 callback = args.pop(),

6 // modules can be passed as an array or as individual parameters

7 modules = (args[0] && typeof args[0] === "string") ? args : args[0], i;

8

9 // make sure the function is called

10 // as a constructor

11 if (!(this instanceof Sandbox)) {

12 return new Sandbox(modules, callback);

13 }

14

15 // add properties to `this` as needed:

16 this.a = 1;

17 this.b = 2;

18

19 // now add modules to the core `this` object

20 // no modules or "*" both mean "use all modules"

21 if (!modules || modules == '*') {

22 modules = [];

23 for (i in Sandbox.modules) {

24 if (Sandbox.modules.hasOwnProperty(i)) {

25 modules.push(i);

26 }

27 }

28 }

29

30 // initialize the required modules

Page 61: Apuntes Javascript Avanzado

Patrones de Código 56

31 for (i = 0; i < modules.length; i += 1) {

32 Sandbox.modules[modules[i]](this);

33 }

34 // call the callback

35 callback(this);

36 }

37

38 // any prototype properties as needed

39 Sandbox.prototype = {

40 name:"My Application",

41 version:"1.0",

42 getName:function () {

43 return this.name;

44 }

45 };

46 Sandbox.modules = {};

47 Sandbox.modules.dom = function (box) {

48 box.getElement = function () {};

49 box.getStyle = function () {};

50 box.foo = "bar";

51 };

52 Sandbox.modules.event = function (box) {

53 // access to the Sandbox prototype if needed:

54 // box.constructor.prototype.m = "mmm";

55 box.attachEvent = function () {};

56 box.detachEvent = function () {};

57 };

58 Sandbox.modules.ajax = function (box) {

59 box.makeRequest = function () {};

60 box.getResponse = function () {};

61 };

62 // how to use

63 Sandbox(['ajax', 'event'], function (box) {

64 console.log(box);

65 });

66 Sandbox('ajax', 'dom', function (box) {

67 console.log(box);

68 });

69 Sandbox('*', function (box) {

70 console.log(box);

71 });

Page 62: Apuntes Javascript Avanzado

11. Patrones de Diseño

..

→ Patrones de Diseño con Javascript (Video)→ design-patterns - github shichuan

11.1 Patrón Singleton

[creación de objetos]

..

→ The Singleton Pattern | Addy Osmani→ Patrones de Diseño en Javascript: El patrón Singleton | pixelovers.com→ Alternatives to singletons in javascript | javascriptkata.com→ singleton - github shichuan

La idea general de este patrón es la de asegurar que una clase genera una única instancia, esdecir, limitar la instanciación de una clase a un único objeto

1 var mySingleton = (function () {

2 // Instance stores a reference to the Singleton

3 var instance;

4 function init() {

5 // Singleton

6 // Private methods and variables

7 function privateMethod(){

8 console.log( "I am private" );

9 }

10 var privateVariable = "Im also private";

11 return {

12 // Public methods and variables

13 publicMethod: function () {

14 console.log( "The public can see me!" );

15 },

16 publicProperty: "I am also public"

17 };

18 };

19

57

Page 63: Apuntes Javascript Avanzado

Patrones de Diseño 58

20 return {

21 // Get the Singleton instance if one exists

22 // or create one if it doesn't

23 getInstance: function () {

24 if ( !instance ) {

25 instance = init();

26 }

27 return instance;

28 }

29 };

30 })();

31

32 // Usage:

33 >>> var singleA = {}, singleB = {};

34 >>> singleA === singleB

35 false

36 >>> singleA = mySingleton.getInstance()

37 >>> singleB = mySingleton.getInstance()

38 >>> singleA === singleB

39 true

11.2 Patrón Factory

[creación de objetos]

..

→ The Factory Pattern | Addy Osmani→ factory - github shichuan

El objetivo de este patrón es el de crear objetos

En vez de crear objetos directamente con new, utilizaremos un objeto Factory al que le pediremosque tipo de objeto queremos y éste objeto lo instanciará y nos lo devolverá

Este patrón es util en las siguientes situaciones:

• Cuando necesitamos repetir operaciones al crear objetos similares• Cuando necesitamos generar diferentes instancias de objetos dependiendo del entorno• Cuando trabajamos con pequeños objetos o componentes que comparten las mismaspropiedades

Page 64: Apuntes Javascript Avanzado

Patrones de Diseño 59

1 // Types.js - Constructors used behind the scenes

2 // A constructor for defining new cars

3 function Car( options ) {

4 // some defaults

5 this.doors = options.doors || 4;

6 this.state = options.state || "brand new";

7 this.color = options.color || "silver";

8 }

9

10 // A constructor for defining new trucks

11 function Truck( options){

12 this.state = options.state || "used";

13 this.wheelSize = options.wheelSize || "large";

14 this.color = options.color || "blue";

15 }

16

17 // FactoryExample.js

18 // Define a skeleton vehicle factory

19 function VehicleFactory() {}

20

21 // Define the prototypes and utilities for this factory

22 // Our default vehicleClass is Car

23 VehicleFactory.prototype.vehicleClass = Car;

24

25 // Our Factory method for creating new Vehicle instances

26 VehicleFactory.prototype.createVehicle = function ( options ) {

27 if( options.vehicleType === "car" ){

28 this.vehicleClass = Car;

29 }else{

30 this.vehicleClass = Truck;

31 }

32 return new this.vehicleClass( options );

33 };

34

35 // Create an instance of our factory that makes cars

36 var carFactory = new VehicleFactory();

37 var car = carFactory.createVehicle( {

38 vehicleType: "car",

39 color: "yellow",

40 doors: 6 } );

41

42 // Test to confirm our car was created using the vehicleClass/prototype Car

43 // Outputs: true

44 console.log( car instanceof Car );

45 // Outputs: Car object of color "yellow", doors: 6 in a "brand new" state

46 console.log( car );

Page 65: Apuntes Javascript Avanzado

Patrones de Diseño 60

Approach #1: Modify a VehicleFactory instance to use the Truck class

1 var movingTruck = carFactory.createVehicle( {

2 vehicleType: "truck",

3 state: "like new",

4 color: "red",

5 wheelSize: "small" } );

6

7 // Test to confirm our truck was created with the vehicleClass/prototype Truck

8 // Outputs: true

9 console.log( movingTruck instanceof Truck );

10

11 // Outputs: Truck object of color "red", a "like new" state

12 // and a "small" wheelSize

13 console.log( movingTruck );

Approach #2: Subclass VehicleFactory to create a factory class that builds Trucks

1 function TruckFactory () {}

2 TruckFactory.prototype = new VehicleFactory();

3 TruckFactory.prototype.vehicleClass = Truck;

4 var truckFactory = new TruckFactory();

5 var myBigTruck = truckFactory.createVehicle( {

6 state: "omg..so bad.",

7 color: "pink",

8 wheelSize: "so big" } );

9

10 // Confirms that myBigTruck was created with the prototype Truck

11 // Outputs: true

12 console.log( myBigTruck instanceof Truck );

13

14 // Outputs: Truck object with the color "pink", wheelSize "so big"

15 // and state "omg. so bad"

16 console.log( myBigTruck );

11.3 Patrón Iterator

..

→ iterator - github shichuan

El objetivo de este patrón es el de encapsular la lógica para recorrer los datos de un objeto

En el patron Iterator nuestro objeto necesita proporcionar un metodo next() que nos devuelva elsiguiente elemento de la secuencia

Page 66: Apuntes Javascript Avanzado

Patrones de Diseño 61

Tambien se suele proporcionar en este objeto el método hasNext() para verificar si hemos llegadoal final de la secuencia de datos

1 var agg = (function () {

2 var index = 0,

3 data = [1, 2, 3, 4, 5],

4 length = data.length;

5

6 return {

7 next: function () {

8 var element;

9 if (!this.hasNext()) {

10 return null;

11 }

12 element = data[index];

13 index = index + 2;

14 return element

15 },

16 hasNext: function () {

17 return index < length;

18 }

19 };

20 }());

21

22 // this loop logs 1, then 3, then 5

23 while (agg.hasNext()) {

24 console.log(agg.next());

25 }

11.4 Patrón Mixins

[estructura, sub‐classing, code re‐use]

..

→ The Mixin Pattern | Addy Osmani

Los Mixins son una manera de recolectar funcionalidades a traves de las extensiones

Los Mixins los podemos considerar objetos cuyos atributos y metodos pueden ser compartidospor otros objetos prototipos.

Page 67: Apuntes Javascript Avanzado

Patrones de Diseño 62

1 var myMixins = {

2 moveUp: function(){

3 console.log( "move up" );

4 },

5 moveDown: function(){

6 console.log( "move down" );

7 },

8 stop: function(){

9 console.log( "stop! in the name of love!" );

10 }

11 };

12

13 // A skeleton carAnimator constructor

14 function carAnimator(){

15 this.moveLeft = function(){

16 console.log( "move left" );

17 };

18 }

19

20 // A skeleton personAnimator constructor

21 function personAnimator(){

22 this.moveRandomly = function(){ /*..*/ };

23 }

24

25 // Extend both constructors with our Mixin

26 _.extend( carAnimator.prototype, myMixins );

27 _.extend( personAnimator.prototype, myMixins );

28

29 // Create a new instance of carAnimator

30 var myAnimator = new carAnimator();

31 myAnimator.moveLeft();

32 myAnimator.moveDown();

33 myAnimator.stop();

34 // Outputs:

35 // move left

36 // move down

37 // stop! in the name of love!

11.5 Patrón Decorator

[estructura, sub-classing, code re-use]

..

→ The Decorator Pattern | Addy Osmani

Page 68: Apuntes Javascript Avanzado

Patrones de Diseño 63

El patron Decorator se centra en cómo extender las funcionalidades de los objetos

Con el patrón Decorator podemos añadir/sobreescribir dinamicamente comportamiento a losmétodos existentes de un objeto

Ejemplo: Decorating Constructors With New Functionality

1 // A vehicle constructor

2 function vehicle( vehicleType ){

3 // some sane defaults

4 this.vehicleType = vehicleType || "car";

5 this.model = "default";

6 this.license = "00000-000";

7 }

8

9 // Test instance for a basic vehicle

10 var testInstance = new vehicle( "car" );

11 console.log( testInstance );

12 // Outputs:

13 // vehicle: car, model:default, license: 00000-000

14 // Lets create a new instance of vehicle, to be decorated

15 var truck = new vehicle( "truck" );

16 // New functionality we're decorating vehicle with

17 truck.setModel = function( modelName ){

18 this.model = modelName;

19 };

20 truck.setColor = function( color ){

21 this.color = color;

22 };

23 // Test the value setters and value assignment works correctly

24 truck.setModel( "CAT" );

25 truck.setColor( "blue" );

26 console.log( truck );

27 // Outputs:

28 // vehicle:truck, model:CAT, color: blue

29 // Demonstrate "vehicle" is still unaltered

30 var secondInstance = new vehicle( "car" );

31 console.log( secondInstance );

32 // Outputs:

33 // vehicle: car, model:default, license: 00000-000

Example 2: Decorating Objects With Multiple Decorators

Page 69: Apuntes Javascript Avanzado

Patrones de Diseño 64

1 // The constructor to decorate

2 function MacBook() {

3 this.cost = function () { return 997; };

4 this.screenSize = function () { return 11.6; };

5 }

6

7 // Decorator 1

8 function Memory( macbook ) {

9 var v = macbook.cost();

10 macbook.cost = function() {

11 return v + 75;

12 };

13 }

14

15 // Decorator 2

16 function Engraving( macbook ){

17 var v = macbook.cost();

18 macbook.cost = function(){

19 return v + 200;

20 };

21 }

22

23 // Decorator 3

24 function Insurance( macbook ){

25 var v = macbook.cost();

26 macbook.cost = function(){

27 return v + 250;

28 };

29 }

30

31 var mb = new MacBook();

32 Memory( mb );

33 Engraving( mb );

34 Insurance( mb );

35

36 // Outputs: 1522

37 console.log( mb.cost() );

38 // Outputs: 11.6

39 console.log( mb.screenSize() );

11.6 Patrón Façade

El objetivo del patron Façade es simplificar la interfaz (los metodos/propiedades “públicos”)ocultando el código complejo. Tambien se utiliza para desacoplar nuestro código de las API’sde librerias externas

Page 70: Apuntes Javascript Avanzado

Patrones de Diseño 65

1 var addMyEvent = function( el,ev,fn ){

2 if( el.addEventListener ){

3 el.addEventListener( ev,fn, false );

4 }else if(el.attachEvent){

5 el.attachEvent( "on" + ev, fn );

6 } else{

7 el["on" + ev] = fn;

8 }

9 };

1 var module = (function() {

2 var _private = {

3 i:5,

4 get : function() {

5 console.log( "current value:" + this.i);

6 },

7 set : function( val ) {

8 this.i = val;

9 },

10 run : function() {

11 console.log( "running" );

12 },

13 jump: function(){

14 console.log( "jumping" );

15 }

16 };

17 return {

18 facade : function( args ) {

19 _private.set(args.val);

20 _private.get();

21 if ( args.run ) {

22 _private.run();

23 }

24 }

25 };

26 }());

27

28 // Outputs: "running", 10

29 module.facade( {run: true, val:10} );

11.7 Patrón Observer – Subscriber/Publisher

Page 71: Apuntes Javascript Avanzado

Patrones de Diseño 66

..

→ The Observer Pattern | Addy Osmani→ Observer | jspatterns.com

El principal objetivo de este patrón es el de desacoplar las partes de un código.

En vez de tener un objeto llamando al método de otro objeto, con este patron un objeto sesubscribe a la actividad de otro objeto y recibe una notificación cuando esta actividad se produce.

1 Subscriber = Observer

2 Publisher = Subject

1 var publisher = {

2 subscribers: {

3 any: [] // event type: subscribers

4 },

5 subscribe: function (fn, type) {

6 type = type || 'any';

7 if (typeof this.subscribers[type] === "undefined") {

8 this.subscribers[type] = [];

9 }

10 this.subscribers[type].push(fn);

11 },

12 unsubscribe: function (fn, type) {

13 this.visitSubscribers('unsubscribe', fn, type);

14 },

15 publish: function (publication, type) {

16 this.visitSubscribers('publish', publication, type);

17 },

18 visitSubscribers: function (action, arg, type) {

19 var pubtype = type || 'any',

20 subscribers = this.subscribers[pubtype],

21 i,

22 max = subscribers.length;

23 for (i = 0; i < max; i += 1) {

24 if (action === 'publish') {

25 subscribers[i](arg);

26 } else {

27 if (subscribers[i] === arg) {

28 subscribers.splice(i, 1);

29 }

30 }

31 }

32 }

33 };

Page 72: Apuntes Javascript Avanzado

Patrones de Diseño 67

1 var s1 = {log: console.log},

2 s2 = {err: console.error},

3 s3 = {warn: console.warn};

4

5 publisher.subscribe(s1.log);

6 publisher.subscribe(s2.err);

7 publisher.subscribe(s3.warn);

8 publisher.publish({hello: "World"});

9 publisher.unsubscribe(s2.err);

10 publisher.publish("hello");

11 publisher.subscribe(s1.log, "log");

12 publisher.publish({obj: "log this object"}, "log");

1 function makePublisher(o) {

2 var i;

3 for (i in publisher) {

4 if (publisher.hasOwnProperty(i) && typeof publisher[i] === "function"\

5 ) {

6 o[i] = publisher[i];

7 }

8 }

9 o.subscribers = {any: []};

10 }

11

12 var paper = {

13 daily: function () {

14 this.publish("big news today");

15 },

16 monthly: function () {

17 this.publish("interesting analysis", "monthly");

18 }

19 };

20

21 makePublisher(paper);

22 var joe = {

23 drinkCoffee: function (paper) {

24 console.log('Just read ' + paper);

25 },

26 sundayPreNap: function (monthly) {

27 console.log('About to fall asleep reading this ' + monthly);

28 }

29 };

30

31 paper.subscribe(joe.drinkCoffee);

32 paper.subscribe(joe.sundayPreNap, 'monthly');

Page 73: Apuntes Javascript Avanzado

Patrones de Diseño 68

33 paper.daily();

34 paper.daily();

35 paper.daily();

36 paper.monthly();

37 makePublisher(joe);

38

39 joe.tweet = function (msg) {

40 this.publish(msg);

41 };

42 paper.readTweets = function (tweet) {

43 alert('Call big meeting! Someone ' + tweet);

44 };

45

46 joe.subscribe(paper.readTweets);

47 joe.tweet("hated the paper today");

11.8 Patrón Mediator

..

→ The Mediator Pattern | Addy Osmani→ mediator | github shichuan

El patrónMediator es un patrón de comportamiento que nos permite utilizar una unica interfaza traves de la cual se pueden comunciar las diferentes partes de un sistema (ej. torre controlaeropuerto)

En Javascript este patron se suele implementar como un objeto compartido a traves del cual losotros objetos (modulos) de nuestro sistema se pueden comunicar (patron Observer centralizado)

1 var mediator = (function(){

2

3 // Storage for topics that can be broadcast or listened to

4 var topics = {};

5

6 // Subscribe to a topic, supply a callback to be executed

7 // when that topic is broadcast to

8 var subscribe = function( topic, fn ){

9 if ( !topics[topic] ){

10 topics[topic] = [];

11 }

12 topics[topic].push( { context: this, callback: fn } );

13 return this;

14 };

15

Page 74: Apuntes Javascript Avanzado

Patrones de Diseño 69

16 // Publish/broadcast an event to the rest of the application

17 var publish = function( topic ){

18 var args;

19 if ( !topics[topic] ){

20 return false;

21 }

22 args = Array.prototype.slice.call( arguments, 1 );

23 for ( var i = 0, l = topics[topic].length; i < l; i++ ) {

24 var subscription = topics[topic][i];

25 subscription.callback.apply( subscription.context, args );

26 }

27 return this;

28 };

29

30 return {

31 Publish: publish,

32 Subscribe: subscribe,

33 installTo: function( obj ){

34 obj.subscribe = subscribe;

35 obj.publish = publish;

36 }

37 };

38

39 }());

Page 75: Apuntes Javascript Avanzado

12. Unit Testings en Javascript

..

→ Desarrollo guiado por pruebas | wikipedia.org→ Introduction To JavaScript Unit Testing | Smashing Magazine

12.1 Unit Testings (pruebas unitarias)

UnUnit testing es un trozo de código que sirve para comprobar que otro trozo de código funcionacorrectamente. Es código que sirve para testear otro código.

Estas pruebas unitarias (unit testings) deben:

• ser poder lanzadas de forma automática (esto es especialmente importante para unaintegración continua)

• testear la mayor cantidad de código posible (cobertura de código más alta posible)• ser poder ejecutadas infinidad de veces• ser independientes, la ejecución de una prueba no debe afectar a la ejecución de otra.• mantener la calidad de código (convención de código, buenas practicas, documentación,…)

Conforme va creciendo el código fuente de un proyecto se va haciendo más importante el podercomprobar de una manera sencilla que los cambios y el nuevo código no rompen nada del códigoexistente

En el caso de Javascript esto es más importante, porque teniendo los tests podremos comprobarautomaticamente que nuestro código funciona bien en diferentes entornos (IE, Firefox, Chro-me,…)

El unit (La unidad a testear)

En TDD, la unidad (el unit) es el trozo más pequeño de código que puede ser testeado. En lamayoria de los casos se corresponde con una función/método.

El test suele ser tambien un método/función asi que un test unitario sencillo suele ser un metódo(test) que testea otro metodo (unit)

A veces se escriben varios unit tests para el mismo método/objeto de manera que cada uno testeaun comportamiento específico. Estos tests que testean un conjunto de código relacionado (comoun objeto con diferentes métodos) se suelen agrupar en un test case

Los test cases se suelen agrupar a su vez en test suites

70

Page 76: Apuntes Javascript Avanzado

Unit Testings en Javascript 71

Code coverage (Cobertura de código)

El code coverage es una forma de medir que proporción del código fuente está siendo realmentetesteanda por una test suite

La cobertura del código se suele medir utilizando software que analiza el código y los tests. ParaPHP se suele utilizar PHPUnit con XDebug. Para JS, podemos utilizar Sonar con jsTestDriver

Mi primer Unit Testing

Este es el código a testear (el ‘unit’)

1 function add(a, b) {

2 return a + b;

3 }

Y este el código con el que lo testeamos (el ‘testing’)

1 // Test function

2 if (add(1, 2) !== 3) {

3 document.write('<p style="color: red;">add(1, 2) does not add up to 3</\

4 p>');

5 } else {

6 document.write('<p style="color: green;">add(1, 2) OK</p>');

7 }

8

9 if (add("2", 2) !== 4) {

10 var msg = "Numbers as strings don't add";

11 document.write('<p style="color: red;">' + msg + '</p>');

12 } else {

13 document.write('<p style="color: green;">add("2", 2) OK</p>');

14 }

Este test lo podemos ejecutar desde aqui

Este ejemplo es muy sencillo pero ilustra varias cosas sobre los unit testings:

• La función se testea desde varios angulos (se comprueba que devuelve la función al pasarlediferentes tipos de datos)

• Comparamos el valor devuelto con un valor esperado• Si estos valores coinciden mostramos un OK verde• Si estos valores no coinciden mostramos un mensaje de error que nos ayude a localizar elproblema

El ejemplo anterior no pasaba todos los tests, pero si mejoramos el código

Page 77: Apuntes Javascript Avanzado

Unit Testings en Javascript 72

1 function add(a, b) {

2 return parseInt(a) + parseInt(b);

3 }

Este nuevo código si que pasa los tests, es decir, que cumple con los criterios especificados en eltest

12.2 TDD y BDD

Test Driven Development (TDD)

→ TDD (Test Driven Development) es una metodología, forma de programar, un workflow, quebasicamente consiste en hacer primero los tests (especificando lo que debe hacer nuestro código)y despues hacer el código que pase estos tests.

El workflow recomendado de TDD es el siguiente:

1. Escribimos los tests para una nueva funcionalidad (asumiendo nombres de metodos,parametros de entrada, valores devueltos…)

2. Ejecutamos los tests y comprobamos que fallan (aun no hemos escrito el codigo a testear)3. Escribimos la solución más simple que cumpla con los tests4. Refactorizamos el código (código más limpio y eficiente y que siga pasando los tests)5. Repetimos estos pasos con otra funcionalidad

Aplicando TDD a la hora de programar nos permite centrarnos en la interfaz (API, methods,inputs & outputs) más que en los detalles de la implementación

Behavior Driven Development (BDD)

→ BDD (Behavior Driven Development) es una versión especializada de TDD que se focalizaen testear (especificaciones de) comportamientos de un software

Utiliza un lenguaje más humano para escribir los tests y no se centra tanto en describir comodebe funcionar la API sino en describir que una funcionalidad concreta haga lo que se espera deella

12.3 Testing Frameworks

..

→ Looking for a better JavaScript unit test tool | stackoverflow.com

Existen unos cuantos Frameworks que nos facilitan la tarea de realizar los tests a nuestro código.

Estos frameworks ofrecen un numero determinado de assertions (afirmaciones) con los quepodremos escribir los tests

Page 78: Apuntes Javascript Avanzado

Unit Testings en Javascript 73

jsTestDriver

jsTestDriver es un framework y servidor de tests escrito en Java y que nos permite ejecutar lostests en diferentes maquinas, diferentes sistemas operativos y diferentes browsers a la vez

Algunos assertions:

• assert([msg], actual)

assertTrue([msg], actual)

Fails if the result isn’t truthy. To use a message when fails, add it as the first parameter.• assertFalse([msg], actual)

Fails if the result isn’t falsy.• assertEquals([msg], expected, actual)

Fails if the expected and actual values can not be compared to be equal.• assertNotEquals([msg], expected, actual)

Fails if the expected and actual values can be compared to be equal.• assertNull([msg], actual)

_Fails if the given value is not exactly null.• assertUndefined([msg], actual)

Fails if the given value is not undefined.• assertNotUndefined([msg], actual)

Fails if the given value is undefined.• assertArray([msg], actual)

Fails if the given value is not an Array.• assertFunction([msg], actual)

Fails if the given value is not a Function. Convenience function to assertTypeOf.• assertObject([msg], actual)

Fails if the given value is not an Object. Convenience function to assertTypeOf.• assertNumber([msg], actual)

Fails if the given value is not a Number. Convenience function to assertTypeOf.• assertString([msg], actual)

Fails if the given value is not a String. Convenience function to assertTypeOf.• assertElementId([msg], id, element)

Fails if the given DOM element does not have given ID.

Para este trozo de código:

1 myapp = {};

2

3 myapp.Greeter = function() { };

4

5 myapp.Greeter.prototype.greet = function(name) {

6 return "Hello " + name + "!";

7 };

Un Unit Testing en jsTestDriver podria ser:

Page 79: Apuntes Javascript Avanzado

Unit Testings en Javascript 74

1 GreeterTest = TestCase("GreeterTest");

2

3 GreeterTest.prototype.testGreet = function() {

4 var greeter = new myapp.Greeter();

5 assertEquals("Hello World!", greeter.greet("World"));

6 };

..

→ JavaScript Testing with JSTestDriver | meri-stuff.blogspot.com.es→ GettingStarted w/ JS Test Driver| code.google.com→ TestCase w/ JS Test Driver| code.google.com→ Assertions w/ JS Test Driver| code.google.com

QUnit

QUnit es un framework de unit testing (para cliente) que nos permite testear nuestro codigoJavascript de una manera sencilla.

Es el que se utiliza para los proyectos jQuery, jQuery UI, jQuery Mobile… y el propio QUnit

Algunos assertions:

• ok( state, message )

A boolean assertion, equivalent to CommonJS’s assert.ok() and JUnit’s assertTrue(). Passesif the first argument is truthy.

1 // Let's test this function

2 function isEven(val) {

3 return val % 2 === 0;

4 }

5 test('isEven()', function() {

6 ok(isEven(0), 'Zero is an even number');

7 ok(isEven(2), 'So is two');

8 ok(isEven(-4), 'So is negative four');

9 ok(!isEven(1), 'One is not an even number');

10 ok(!isEven(-7), 'Neither is negative seven');

11 })

• equal( actual, expected, message )

A non-strict comparison assertion, roughly equivalent to JUnit assertEquals.

Page 80: Apuntes Javascript Avanzado

Unit Testings en Javascript 75

1 test( "equal test", function() {

2 equal( 0, 0, "Zero; equal succeeds" );

3 equal( "", 0, "Empty, Zero; equal succeeds" );

4 equal( "", "", "Empty, Empty; equal succeeds" );

5 equal( 0, 0, "Zero, Zero; equal succeeds" );

6

7 equal( "three", 3, "Three, 3; equal fails" );

8 equal( null, false, "null, false; equal fails" );

9 });

• strictEqual( actual, expected, message )

A strict type and value comparison assertion.

1 test( "strictEqual test", function() {

2 strictEqual( 1, 1, "1 and 1 are the same value and type" );

3 });

Para este trozo de código:

1 // your applications custom code

2 function addValues( a, b ) {

3 return a + b;

4 };

Un Unit Testing en QUnit podria ser:

1 // the QUnit test code

2 test("test addValues(a, b) function", function() {

3 equal(addValues( 1, 2), 3, "1 + 2 = 3" );

4 equal(addValues( 1.75, 2), 3.75, "1.75 + 2 = 3.75" );

5 notEqual(addValues( 1, 2), "3", "1 + 2 != '3' as a String");

6 });

..

→ How to Test your JavaScript Code with QUnit | code.tutsplus.com→ Asserts | api.qunitjs.com→ Cookbook | qunitjs.com→ QUnit, testeando nuestras aplicaciones Javascript | etnassoft.com→ Getting Started With jQuery QUnit for Client-Side Javascript Testing | lostechies.com

Page 81: Apuntes Javascript Avanzado

Unit Testings en Javascript 76

Jasmine

Jasmine es un framework para testear código Javascript orientado a BDD (testar funcionalidades),pero se puede utilizar también para TDD (testear API).

Un test suite en Jasmine se declara con la función global describe que recibe:

• el nombre de la suite• una función con el código que implementa los tests

Las specs se declaran con la función global it que recibe:

• una descripción de la especificación• una función con una o más expectations

Los expectations (comportamientos esperados) se deescriben con las función expect y unmatcher (toBe, ‘toEqual, …):

1 describe("A suite", function() {

2 it("contains spec with an expectation", function() {

3 expect(true).toBe(true);

4 });

5 });

Algunos matchers que ofrece Jasmine por defecto (podemos construirnos los nuestros propios)

• expect(x).toEqual(y);

compares objects or primitives x and y and passes if they are equivalent• expect(x).toBe(y);

compares objects or primitives x and y and passes if they are the same object• expect(x).toMatch(pattern);

compares x to string or regular expression pattern and passes if they match• expect(x).toBeDefined();

passes if x is not undefined• expect(x).toBeUndefined();

passes if x is undefined• expect(x).toBeNull();

passes if x is null• expect(x).toBeTruthy();

passes if x evaluates to true• expect(x).toBeFalsy();

passes if x evaluates to false• expect(x).toContain(y);

passes if array or string x contains y

Page 82: Apuntes Javascript Avanzado

Unit Testings en Javascript 77

• expect(x).toBeLessThan(y);

passes if x is less than y

• expect(x).toBeGreaterThan(y);

passes if x is greater than y

• expect(function(){fn();}).toThrow(e);

passes if function fn throws exception e when executed

Para este trozo de código:

1 // your applications custom code

2 function addValues( a, b ) {

3 return a + b;

4 };

Un Unit Testing en Jasmine podria ser:

1 // the Jasmine test code

2 describe("addValues(a, b) function", function() {

3 it("should equal 3", function(){

4 expect( addValues(1, 2) ).toBe( 3 );

5 });

6 it("should equal 3.75", function(){

7 expect( addValues(1.75, 2) ).toBe( 3.75 );

8 });

9 it("should NOT equal '3' as a String", function(){

10 expect( addValues(1, 2) ).not.toBe( "3" );

11 });

12 });

..

→ Magnum CI: The Jenkins Chronicles #1 – Intro to JsTestDriver | transitioning.to→ Testing Your JavaScript with Jasmine | net.tutsplus.com→ Unit test JavaScript applications with Jasmine | adobe.com

12.4 Sinon

Para testear ciertas partes del código necesitaremos “customizar” temporalmente algunos me-todos y objetos. Sinon es una libreria que nos permite hacer “fake” de objetos (spies, stubs ymocks)

Page 83: Apuntes Javascript Avanzado

Unit Testings en Javascript 78

..

→ Sinon JS | sinonjs.org

Test Spies

Un test spy es una función que registra los argumentos, el valor return, el valor de this y lasexcepciones lanzadas (si las hay) para cada una de sus llamadas.

Un test spy puede ser una función anónima o puede “envolver” una función existente.

Un test spy anónimo es útil, por ejemplo, para testar el uso que se hace de las funciones callbacken nuestro código.

1 "test should call subscribers on publish": function () {

2 var callback = sinon.spy();

3 PubSub.subscribe("message", callback);

4

5 PubSub.publishSync("message");

6

7 assertTrue(callback.called);

8 }

Si aplicamos sinon.spy a un metodo existente, podremos ejecutarlo normalmente y ademástener disponibles los datos registrados en cada una de sus llamadas.

1 {

2 setUp: function () {

3 sinon.spy(jQuery, "ajax");

4 },

5

6 tearDown: function () {

7 jQuery.ajax.restore(); // Unwraps the spy

8 },

9

10 "test should inspect jQuery.getJSON's usage of jQuery.ajax": function (\

11 ) {

12 jQuery.getJSON("/some/resource");

13

14 assert(jQuery.ajax.calledOnce);

15 assertEquals("/some/resource", jQuery.ajax.getCall(0).args[0].url);

16 assertEquals("json", jQuery.ajax.getCall(0).args[0].dataType);

17 }

18 }

Mas info:

Page 84: Apuntes Javascript Avanzado

Unit Testings en Javascript 79

..

→ Test Spy | xunitpatterns.com

Test Stubs

Un test stub es una función “spy” con un comportamiento pre-programado. Disponen de los mis-mos metodos/propiedades que los spy junto con otros utilizados para alterar el comportamientodel stub.

Un test stub puede ser una función anónima o puede “envolver” una función existente. Cuandoenvuelve a una functión existente, la función original no se ejecuta.

Utilizaremos stubs cuando queramos:

• Controlar el comportamiento de un metodo para testear una casuística concreta del código(por ejemplo, forzar que un metodo devuelva un error para testear la gestión del error)

• Evitar que un metodo específico sea llamado directamente (porque provocaria un compor-tamiento no deseado para nuestro test: llamadas AJAX, envio de formularios, etc..)

1 "test should call all subscribers, even if there are exceptions" : function\

2 (){

3

4 var message = 'an example message';

5 var error = 'an example error message';

6

7 var stub = sinon.stub().throws();

8 var spy1 = sinon.spy();

9 var spy2 = sinon.spy();

10

11 PubSub.subscribe(message, stub);

12 PubSub.subscribe(message, spy1);

13 PubSub.subscribe(message, spy2);

14

15 PubSub.publishSync(message, undefined);

16

17 assert(spy1.called);

18 assert(spy2.called);

19 assert(stub.calledBefore(spy1));

20 }

Mocks

Un test mockup es una combinación entre un “stub” y un “spy” con un comportamientopre-programado. Un test mockup es una función/metodo fake (spy) con un comportamiento

Page 85: Apuntes Javascript Avanzado

Unit Testings en Javascript 80

pre-programado (stub) pero que ademas se define con unas expectativas pre-programadas. Unmockup fallará si no se utliza como se espera.

Los mockups:

• Disponen de todos los metodos de stubs y spies• Pueden ser anónimos• Sólo pueden aumentar objetos .

1 "test should call all subscribers when exceptions": function () {

2 var myAPI = { method: function () {} };

3

4 var spy = sinon.spy();

5 var mock = sinon.mock(myAPI);

6 mock.expects("method").once().throws();

7

8 PubSub.subscribe("message", myAPI.method);

9 PubSub.subscribe("message", spy);

10 PubSub.publishSync("message", undefined);

11

12 mock.verify();

13 assert(spy.calledOnce);

14 }