Skip to content

Latest commit

 

History

History
993 lines (790 loc) · 40.5 KB

File metadata and controls

993 lines (790 loc) · 40.5 KB

shieldsIO shieldsIO shieldsIO

WideImg

Máster en Programación FullStack con JavaScript y Node.js

JS, Node.js, Frontend, Backend, Firebase, Express, Patrones, HTML5_APIs, Asincronía, Websockets, Testing

Clase 24

Los Navegadores

IMG

Los Navegadores: Historia

IMG

Evolución IMG

Recursos

Los Navegadores: Motores actuales

img

Browser Engine

Los Navegadores: Interpretes de JS

IMG

El Motor JavaScript (JavaScript Engine)

Recursos

Los Navegadores: Funcionamiento

Arquitectura a alto nivel img

Afinando los detalles img

Philip Robers es un genio! IMG

Recursos

Hablemos de JavaScript y más allá...

img

Recursos

Las funciones en JS: Funciones anónimas autoejecutadas (IIFE)

// Classic
(function(){})();
 
// Crockford's favorite
(function(){}());
 
// Unary versions
+function(){}();
 
// Facebook version
!function(){}();
 
// Crazy version
!1%-+~function(){}();

Recursos

Las funciones en JS: Funciones variádicas

  • La gestión de argumentos es muy pocos restrictiva
  • EL úmero de argumentos es variable, por defecto su valor es siempre undefined
  • arguments no es un array y tendremos que manejarlo con cuidado o convertirlo usando prototype
function func(arg1, arg2, arg3) { 
	console.log("arg1:", arg1)
	console.log("arg2:", arg2)
	console.log("arg3:", arg3)
	console.log("Arguments:" arguments)
	console.log("Arguments eres una Array?", Array.isArray(arguments))
}
func(2); //arg1: 2 
		 //arg2: undefined
		 //arg3: undefined

Recursos

Las funciones en JS: Ciudadanos de primera clase

function func() {}
var func = function (){}
var obj = {
	func(){}
}
var func = new Function('arg1', 'arg2', 'console.log(arg1, arg2)');

Las funciones en JS: Comportamiento de objetos

function func() {console.log("Soy una función", typeof(func))}
func.metodo = function() {console.log("Soy un método", typeof(func.metodo))}
func.propiedad = "Dato almacenado..."
console.log("Tengo propiedades", typeof(func.propiedad)) //Tengo propiedades string
func.metodo() //Soy un método function
func() //Soy una función function

Las funciones en JS: Entidades de orden superior

function funcAsincrona (dato, callback)  {
    return callback(dato+5);
}

funcAsincrona(5, data => console.log) //10
function funcEspecial () {
	console.log("Una sola vez...")
    return function () {
        console.log("Muchas veces...")
    }
}

var nuevaFunc = funcEspecial(); //Una sola vez...
nuevaFunc(); //Muchas veces...
nuevaFunc(); //Muchas veces...

Recursos

Las funciones en JS: Recursión

Partes

  • Caso base: es el caso más simple y determina la salida de la función
  • Caso recursivo: vamos reduciendo la causistica y llamando a la propia función

Ejemplo

// Sin recursión
let counter = 100;
while(counter > 0) {
    console.log(counter--);
}

// Con recursión
const cuentaAtras = function(value) {
    if (value > 0) {
        console.log("Valor actual:", value);
        cuentaAtras(value - 1);
    } else {
        console.log("Fin de la historia:", value);
    }
};
cuentaAtras(100);

Recursos

Las funciones en JS: Los ambitos

Global

const global = "Dato global";
function func() {
    console.log(global);
}
func(); // Dato global

Local clásico

function func() {
    const local = 5;
}
console.log(local); // undefined.

Local con ES6

  • Usando var el scope se define por la función donde se declaró, function-level scope
  • Usando let y const el scope también se define en if, while, for o switch, block-level scope.
var uno = 1;
var diez = 10;
 
if (uno === 1) {
  let uno = 3; // El alcance es dentro del bloque if
  var diez = 5; // El alcance es dentro de la función
 
  console.log(uno);  // 3
  console.log(diez);  // 5
} 
 
console.log(uno); // 1
console.log(diez); // 5

Las funciones en JS: Closures

IMG

Un closure o clausura es la combinación de una función y el ámbito léxico en el que se declaró dicha función. Es decir los closures o clausuras son funciones que manejan variables independientes. En otras palabras, la función definida en el closure "recuerda" el ámbito en el que se ha creado. MDN

Claves

  • Simplemente: la función definida en el closure "recuerda" el ámbito en el que se ha creado
  • En resumen nos permite encapsular código pero con acceso a su contexto
  • Todo ese contexto que recuerda ataca a la memoria RAM
  • No debemos abusar de este hack por temas de rendimiento
  • Es muy útil para emular métodos privados entre otras cosas

Ejemplo clásico

function creaSumador(x) {
  return function(y) {
    return x + y;
  };
}

var suma5 = creaSumador(5);
var suma10 = creaSumador(10);

console.log(suma5(2));  // muestra 7
console.log(suma10(2)); // muestra 12 

Ejemplo aplicado

function makeSizer(size) {
  return function() {
    document.body.style.fontSize = size + 'px';
  };
}

var size12 = makeSizer(12);
var size14 = makeSizer(14);
var size16 = makeSizer(16);

Ejemplo ES5.1 vs ES6

//ES5
(function () {
	if(true) {
		var ifScope = "data"
	}
    for(var forScope = 0; forScope < 3; forScope += 1) {
        console.log(forScope);
    }
    console.log("forScope ?", forScope);	// 3
    console.log("ifScope ?", ifScope);		// data
}());

// ES6
(function () {
	if(true) {
		let ifScope = "data"
	}
    for(let forScope = 0; forScope < 3; forScope += 1) {
        console.log(forScope);
    }
    console.log("forScope ?", forScope);	// Uncaught ReferenceError: forScope is not defined
    console.log("ifScope ?", ifScope);		// Uncaught ReferenceError: ifScope is not defined
}());

Recursos

Conceptos: Hoisting (elevamiento)

  • Antes de ejecutar el código, las variables se mueven al inicio del contexto de ejecución
  • Declarar una variable en cualquier parte es lo mismo que declararla al inicio
  • Las variables pueden usarse antes de ser declaradas
console.log("fn1", fn1()); // Uncaught TypeError: fn1 is not a function
console.log("fn2", fn2()); // 2
 
//function expression
var fn1 = function() { return 1; };
 
//function declaration
function fn2() { return 2; }

console.log("fn1", fn1()); // 1
console.log("fn2", fn2()); // 2

Recursos

Conceptos: Programación defensiva

IMG

La programación defensiva (defensive programming en inglés) es una forma de diseño defensivo aplicada al diseño de software que busca garantizar el comportamiento de todo elemento de una aplicación ante cualquier situación de uso por incorrecta o imprevisible que ésta pueda parecer. En general, esto supone multiplicar las comprobaciones que se realizan en todos los módulos programados, con la consiguiente penalización en carga de procesador, tiempo y aumento en la complejidad del código. Las técnicas de programación defensiva se utilizan especialmente en componentes críticos cuyo mal funcionamiento, ya sea por descuido o por ataque malicioso, podría acarrear consecuencias graves o daños catastróficos. Wikipedia

Recursos

Conceptos: Lenguaje de programación dinámico

Un lenguaje de programación dinámico es un lenguaje de programación en el que las operaciones realizadas en tiempo de compilación pueden realizarse en tiempo de ejecución. Por ejemplo, en JavaScript es posible cambiar el tipo de una variable o agregar nuevas propiedades o métodos a un objeto mientras el programa está en ejecución. MDN

Recursos

Conceptos: duck typing

IMG

Si parece un pato, nada como un pato, y grazna como un pato, entonces probablemente sea un pato.

Conceptos

  • Podemos saber si un objeto es una instancia de otra por las propiedades que hereda o no...
  • typeof es muy limitado para saber que es algo, especialmento con objetos
  • Instanceof es útil cuando evaluamos nuestras propias construcciones

El problema de typeof

//@see: http://www.etnassoft.com/2011/02/07/duck-typing-en-javascript-chequeando-los-tipos-de-datos/
console.log( typeof new String( 'Fictizia' ) );				//object
console.log( typeof new Number( 1 ) );							//object
console.log( typeof [ "platano", true, "coche", 4 ] );			//object
console.log( typeof new Array( "platano", true, "coche", 4 ) ); //object

El problema de instanceof

// @see: http://www.etnassoft.com/2011/02/07/duck-typing-en-javascript-chequeando-los-tipos-de-datos/
// construciones propias:
function constructor(){}
function otro_constructor(){}
constructor.prototype = otro_constructor;
console.log( new constructor() instanceof constructor );			// true
console.log( new otro_constructor() instanceof otro_constructor );	// true
console.log( new otro_constructor() instanceof constructor );		// false

// construciones del lenguaje:
console.log( new String( 'Fictizia' ) instanceof String );			// true
console.log( new String( 'Fictizia' ) instanceof Object );			// true
console.log( 'Fictizia' instanceof String );						// false
console.log( 'Fictizia' instanceof Object );						// false
console.log( new Array( 1, true, "banana" ) instanceof Array ); 	// true
console.log( new Array( 1, true, "banana" ) instanceof Object );	// true
console.log( [ 1, true, "banana" ] instanceof Array );				// true
console.log( [ 1, true, "banana" ] instanceof Object ); 			// true

Entendiendo la validación

// https://github.com/jashkenas/underscore/blob/master/underscore.js#L1315
function isArray( obj ){
  return toString.call( obj ) === '[object Array]';
}

// Aproximación como Duck Typing
function isArray( obj ){
  return obj && typeof obj === "object" && "push" in obj;
}

//@see: http://www.etnassoft.com/2011/02/07/duck-typing-en-javascript-chequeando-los-tipos-de-datos/
// Aproximación como Duck Typing en modo paranoico
function isArray( obj ){
  return obj &&
  typeof obj === "object" &&
  "splice" in obj &&
  "join" in obj &&
  "push" in obj;
}

Recursos

Conceptos: weak typing (Tipado Blando)

  • JavaScript permite cambiar los tipos de datos de las variables de forma mas o menos automatica (sumar cadena a un numero..)
  • Ofrece mucha flexibilidad pero también hace el codigo potencialmente inestable
  • Es importnate tener controlado esto cuando manejamos datos externos (Ajax/JSON)

Ejemplo

var txt = "Cadena";
var num = 100;
var bol = false;
var otroNum = "10"

console.log(typeof(txt))	 //string
console.log(typeof(num))	 //number
console.log(typeof(bol))	 //boolean
console.log(typeof(otroNum)) //string

Problema Típico: Ajax/Json

  • Inconsistencia de datos
  • Inconsistencia en el tipo de datos
[
    {
        'id': 1,
        'name': 'Dr. Evil',
        'role': 'Evil Boss',
        'external_agency': false,
        'security_level': 5
        'started_at': 'unknown'
    }, {
        'id': "N1001",
        'name': 'Mr. Hacker',
        'role': 501,
        'security_level': "TOP SECRET"
        'external_agency': "Evil Mega Corp",
        'started_at': 125869547
    }
];

Solución (Programación defensiva) con tipos primarios

// @see: http://www.etnassoft.com/2016/10/17/tipado-seguro-en-javascript/
//Closure
var typeOf = ( type ) => ( x ) => {
    if ( typeof x === type ) return x;
    throw new TypeError( "Error: " + type + " expected, " + typeof x + " given." );
};
 
var str = typeOf( 'string' );

// Correct
console.info( str( 'Hello World' ) ); // Hello World
// Error / Exception
console.info( str( 123 ) ); // Error: string expected, number given

/* --- Ejemplo Real ---*/
function sum ( x, y ) {
    return num( x ) + num( y );
}
 
console.info( sum( 2, 4 ) ); // 6
console.info( sum( 2, '3' ) ); // Error: number expected, string given

Solución (Programación defensiva) con Objetos

//@see: http://www.etnassoft.com/2016/10/17/tipado-seguro-en-javascript/
const objectTypeOf = name => obj => {
    let toType = ( {} ).toString.call( obj ).match( /\s([a-z|A-Z]+)/ )[ 1 ].toLowerCase();
    if ( toType === name ) return obj;
 
    throw new TypeError( "Error: " + name + " expected, " + toType + " given." );
}
 
const obj = objectTypeOf( 'object' ),
    arr = objectTypeOf( 'array' ),
    date = objectTypeOf( 'date' );
    
// Correct
console.info( obj( {} ) ); // Object {}
console.info( arr( [] ) ); // []
console.info( date( new Date() ) ); // Date { ... }
 
// Error / Exception
console.info( obj( [] ) ); // Error: object expected, array given.
console.info( arr( {} ) ); // Error: array expected, object given.
console.info( date( '13/11/2016' ) ); // Error: date expected, string given.

/* --- Ejemplo Real ---*/

const map = function( fn, a ) {
    return arr( a ).map( func( fn ) );
}
 
map( String.toUpperCase, [ 'foo', 'bar' ] ); // [ 'FOO', 'BAR' ]
map( String.toUpperCase, 'Hello World' ); // Error: array expected, string given

Recursos

Conceptos: Coercion (Coerción/Imposición/Condicionamiento a la fuerza)

Importante

  • Se fuerza a que un elemento cambie su comportamiento y se comporte como otro de forma temporal
  • Es dificil recordar todas las reglas
  • Jugar con esto es complejo y peligroso, deberias de evitarlo de forma consciente
  • Javascript hace mucho juego de coerción por debajo y es necesario controlarlo
  • La coercion SOLO aplica a los Tipos primitivos (Primitive Types): Boolean, Number, String, Null, Undefined, Symbol.
  • La coercion JAMAS aplica en Objetos ni funciones, a excepción de null porque object == object siempre es false

Coerción implicita vs. Explicita

  • Explicita es cuando lo gestionamos directaente Boolean(), Number(), String(), parseloat(), etc...
  • Implicita es cuando delegamos esa conversion con operadores como +, ==, !=, -, etc...
  • === NO implica una coerción de tipos ya que valida por tipo primero (comparación estricta)

Solo existen tres tipos de coerciones

  • +dato y -dato fuerza la coercion a número
  • algo + string o algo - string fuerza a coerción de cadena
  • !value, if(value) fuerzan a la coerción boolena, Boolean(x)

Ámbitos del Juego

  • cualquier cosa -> boolean
  • numbero -> string
  • string -> number
  • boolean -> number
  • null or undefined -> number
  • null or undefined -> string

Lógica aceptable

  • Una cadena que no pueda representar un número será NaN
  • Todo es True, a excepción de las cosas sin valor real (0, -0, false, null, undefined, "")
  • Undefined es NaN, isNaN(undefined)
//Un clasico
Array(16).join( 'hero'-1) + "Batman";
// NaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNBatman

//@see: https://wtfjs.com/
//@see: https://github.com/getify/You-Dont-Know-JS/blob/master/types%20%26%20grammar/ch4.md#edge-cases
console.log('+true:', +true);									//1
console.log('-true:', -true);									//-1
console.log('true + false:', true + false); 					// 1
console.log('12 / "6":', 12 / "6"); 							// 2
console.log('"number" + 15 + 3:', "number" + 15 + 3);			//number153
console.log('15 + 3 + "number":', 15 + 3 + "number");			//18number
console.log('[1] > null:', [1] > null); 						//true
console.log('"foo" + + "bar":', "foo" + + "bar");				//fooNaN
console.log('"true" == true:', "true" == true); 				//false
console.log('false == "false":', false == "false"); 			//false
console.log('null == "":', null == ""); 						//false
console.log('!!"false" == !!"true":', !!"false" == !!"true");	//true
console.log('["x"] == "x":', ["x"] == "x");						//true
console.log('[] + null + 1:', [] + null + 1);					//null1
console.log('[1,2,3] == [1,2,3]:', [1,2,3] == [1,2,3]);			//false
console.log('{}+[]+{}+[1]:', {}+[]+{}+[1]);						//[object Object][object Object]1
console.log('!+[]+[]+![]:', !+[]+[]+![]);						//truefalse
console.log('new Date(0) - 0:', new Date(0) - 0);				//0
console.log('new Date(0) + 0:',new Date(0) + 0);				//Thu Jan 01 1970 01:00:00 GMT+0100 (hora estándar de Europa central)0

Un buen esquema estructural img

Recursos

This: Lo básico

IMG

Importante

  • this es uno de los conceptos peor entendidos y más odiados
  • this se establece en tiempo de ejecución
  • this es dinámico y varia constantemente
  • this es una de las herramientas más potentes del lenguaje
  • this es extensamente usando en la Programación Orientada a Objetos (POO) en JavaScript

Dominando los Contextos de this

  • Window:
console.log( this === window );
 
function prueba(){
  console.log( this === window);
}
 
prueba();
  • Otro Contexto:
var usuario = {
  nombre : 'Yo',
  apellido : 'Mismo',
  nombreCompleto : this.nombre + this.apellido,
  metodoNombre: function(){
    return this.nombre + " " + this.apellido
  },
  valorThis: function (){
    console.log(this);
  }
}

console.log(usuario.nombreCompleto); // ERROR - Undefined  -> this=window
console.log(usuario.metodoNombre()); // this=usuario
usuario.valorThis(); // this=usuario

Recursos

This: _self, un salvavidas

Usando this correctamente

var objeto = {
    valor: 0,
    incrementar: function(incremento){
       this.valor += incremento;
    }
};

objeto.incrementar(6);
console.log("objeto.valor:", objeto.valor) //6

Empiezan los problemas con this por el scope

var objeto = {
    valor: 0,
    incrementar: function(incremento){
       function otraFuncion(unValor){
           this.valor += unValor;
       }
       otraFuncion(incremento);
    }
};

objeto.incrementar(6);
console.log("objeto.valor:", objeto.valor) //0!!

_self al rescate

var objeto = {
    valor: 0,
    incrementar: function(incremento){
       var _self = this;
       function otraFuncion(unValor){
           _self.valor += unValor;
       }
       otraFuncion(incremento);
    }
};

objeto.incrementar(6);
console.log("objeto.valor:", objeto.valor) //6

Recursos

This: Modificadores de contexto

  • Nos permite hacer un código muy modular
  • Nos permite crear mucho código reutilizable
  • Existen tres formas de modificar el contexto
    • call() Nos devuelve la ejecución con el contexto cambiado (this), documentación
    • apply() Nos devuelve la ejecución con el contexto cambiado (this), documentación
    • bind() Nos devuelve una nueva funcion con el contexto modificado (this), documentación
    • call() y apply() se diferencian por como se pasan los argumentos a la función que modificamos

Ejemplo Sencillo

var objeto = {
  multiplicador: 2,
  sumatorio: function(num1, num2){
     return (num1 + num2) * this.multiplicador;
  }
};

var objeto_de_cambio = {
  multiplicador: 5
};

var resultado = objeto.sumatorio(2,2);
console.log(resultado); // 8


/* -- Tres formas de lograr lo mismo -- */

var resultado_call = objeto.sumatorio.call(objeto_de_cambio, 5, 5);
var resultado_apply = objeto.sumatorio.apply(cambio, [5,5]);
var cambiandoFuncion = objeto.sumatorio.bind(cambio);

console.log(resultado_call); // 50
console.log(resultado_apply); // 50
console.log(cambiandoFuncion(5, 5)); // 50

Ejemplo más real

var usuarios = [{
  nombre: "Yo",
  apellido: "Mismo",
  user: "me"
}, {
  nombre: "Doctor",
  apellido: "Maligno",
  user: "MrEvil"
}];
 
function nombreCompleto (){
	console.log( this.nombre + ' ' + this.apellido + ' (@'+this.user+')' );
}

function premium (isPremium, until) {
	var user = '@'+this.user;
	console.log( isPremium ? user + " es premium hasta "+ until : user + " NO es premium");
}



nombreCompleto() //Error (window...)		// undefined undefined (@undefined)
nombreCompleto.apply(usuarios[1])			// Doctor Maligno (@MrEvil)

premium.apply(usuarios[1], [true, 'Mayo'])	// @MrEvil es premium hasta Mayo
premium.call(usuarios[0], true, 'Enero')	// @me es premium hasta Enero
premium.call(usuarios[0], false)			// @me NO es premium

Protype:

IMG

Las claves

  • Puede ser complejo de entender si vienes de lenguajes Orientados a Objetos
  • Todo es prototype de algo, incluido el propio core de JavaScript
  • Nunca debes modificar metodos de objetos nativos
  • La modificación del prototipo se extiende a todo el sistema

Uso de prototype

var elementosDom = document.querySelectorAll('div');
var elementos = Array.prototype.slice.call(elementosDom);

Extensión de objetos nativos

var amigosOriginales = ["Charlie", "Marco", "Oscar"];
amigosOriginales.coincidencias("Jose"); //amigosOriginales.coincidencias is not a function

Array.prototype.coincidencias = function(palabra) {
    var coincidencias = 0;
    //
    for (var i=0; i<this.length; i++) {
        if (this[i] == palabra) {
            coincidencias++;
        }
    }
    console.warn("Se encontraron "+coincidencias+" coincidencia(s) de la palabra");
};

var amigos = ["Charlie", "Marco", "Luis", "Jose", "Miguel", "Jose", "Luis", "Oscar"];
amigos.coincidencias("Jose");			// Se encontraron 2 coincidencia(s) de la palabra
amigosOriginales.coincidencias("Jose"); // Se encontraron 0 coincidencia(s) de la palabra

Recursos

Objetos: ¿Los punteros son amor?

Importante saber

  • La variables primitivas (Undefined, Null, Boolean, Number y String) se copian por valor.
  • Los objetos se copian por referencia (shallow copy), no es una copia real.. solo otra forma de llamarlo
  • Para clonar objetos, necesitaremos hacer una recreación completa (deep copy) desde cero.

img

El problema de los punteros

var primitivo1 = "BANANA";
var primitivo2 = primitivo1;
primitivo2 = "MANDARINA";
console.log(primitivo1); //"BANANA"
console.log(primitivo2); //"MANDARINA"

// ¯\_(ツ)_/¯
var array1 = [1, 2, 3];
var array2 = array1;
array2.push("MANDARINA");
console.log(array1); //[1, 2, 3, "MANDARINA"]
console.log(array2); //[1, 2, 3, "MANDARINA"]

El problema de las estructuras circulares

var kom256 = {
    nombre: "Ulises",
    rol: "profesor",
    curso: "Master de JS y Node",
    amigos: []
};
 
var codingCarlos = {
    nombre: "Carlos",
    rol: "profesor",
    curso: "Master de JS y Node",
    amigos: []
};
var josheriff = {
	nombre: "José Manuel",
    rol: "profesor",
    curso: "Curso de JS",
    amigos: []
}
 
codingCarlos.amigos = [kom256, josheriff];
kom256.amigos = [codingCarlos, josheriff];
josheriff.amigos = [kom256, codingCarlos];

Posibles Soluciones

  • Iterando propiedades con bucles, las propiedades de tipo objeto sera copiadas por referencia
  • JSON.parse ignora funciones y objetos con estructuras circulares
  • En ES6 con Object.assign(), spread operator(...), etc...
  • Librerias: $.extend, _.CloneDeep, _.Clone
var array1 = [1, 2, 3];
var array2 = JSON.parse(JSON.stringify(array1));
array2.push("MANDARINA");
console.log(array1); //[1, 2, 3]
console.log(array2); //[1, 2, 3, "MANDARINA"]

Recursos

Resumen de la clase 💪💪

img

IMG