JavaScript
Contenido
- Declaración de variables
- Alcance de una variable
- Estructuras de control
- Plantillas de cadena de texto (Template Literals)
- JSON( JavaScript Object Notation.)
- El objeto this
- This y el método bind
- Objetos
- Arrow functions
- Arrays (solo se describen los más importantes)
- Asincronismo (callbacks, promesas, async-await)
- Eventos
Declaración de variables
Las formas de escribir variables son las siguientes.
VAR
Utilizado para declarar variables que pueden ser redeclaradas y que conservan su valor en el contexto donde fueron declaradas.
var foo = 'bar'
CONST
Utilizado para declaraciones constantes no reasignables pero no entra dentro del concepto de inmutabilidad ya sea locales o globales de acuerdo al contexto donde fueron declaradas
const foo=’’
LET
Utilizado para declaraciones variables reasignables pero no redeclarable con alcance donde fueron declaradas
let foo=’bar’
Redeclaración
//Redeclaration
var a=1
const b = 2
let c = 3
var a="A" //-- The statement is correct
//const b = "B" //-- SyntaxError: Identifier 'b' has already been declared
let c = "C" //-- SyntaxError: Identifier 'c' has already been declared
Redefinición
//Redeclaration
var a=1
const b = 2
let c = 3
a="A" //-- The statement is correct
//b = "B" //-- TypeError: Assignment to constant variable.
c = "C" //-- The statement is correct
Alcance de una variable
Ejemplo 1
var a=1
const b = 2
let c = 3
function demo(){
a++;
//b++; //--TypeError: Assignment to constant variable.
c++;
console.info('a: '+a) //-- print: a: 2
console.info('b: '+b) //-- print: a: 2
console.info('c: '+c) //-- print: a: 4
}
demo()
console.info('a: '+a)//-- print: a: 2
console.info('b: '+b)//-- print: a: 2
console.info('c: '+c)//-- print: a: 4
Ejemplo 2
var a=1
const b = 2
let c = 3
function demo(){
var a='A'
const b = "B"
let c = "C"
console.info('a: '+a) //-- print: a: A
console.info('b: '+b) //-- print: a: B
console.info('c: '+c) //-- print: a: C
}
demo()
console.info('a: '+a)//-- print: a: 1
console.info('b: '+b)//-- print: a: 2
console.info('c: '+c)//-- print: a: 3
Estructuras de control
Las estructuras de control son similares a otros lenguajes.
IF
const a = true
if(a){
//TODO
}else{
//TODO
}
FOR
for(let i=0; i<=10;i++){
//TODO
}
FOREACH
const months = ['January','February','March','April','May','June','July','August','September','October','November','December']
months.forEach(function(item, index){
console.info(`Tu estas en el indice ${index} mes ${item}`)
})
//--with arrow function
months.forEach((item, index)=>console.info(`Tu estas en el indice ${index} mes ${item}`))
SWITCH
let month='March'
switch(month){
case "January": console.info('Is winter'); break;
case "February": console.info('Is winter'); break;
case "March": console.info('Is Spring'); break;
case "April": console.info('Is Spring'); break;
case "May": console.info('Is Spring'); break;
case "June": console.info('Is Summer'); break;
case "July": console.info('Is Summer'); break;
case "August": console.info('Is Summer'); break;
case "September":console.info('Is Fall'); break;
case "October": console.info('Is Fall'); break;
case "November": console.info('Is Fall'); break;
case "December": console.info('Is winter'); break;
default: console.info(`Error`);
}
Plantillas de cadena de texto (Template Literals)
Plantillas de cadena de texto o Template Literals es una características que nos permite en una cadena de texto insertar expresiones, olvidándonos así de la concatenación con el signo +.
Para hacer un Template Literal en necesario que estén en comillas tipo ` content `
y las expresiones deben estar dentro de ${number}
const number=25;
console.debug(`My lucky number is ${number} `)
//-- "My lucky number is 25 "
console.debug(`If multiply my lucky number ${number*2} `)
//-- "If multiply my lucky number 50 "
JSON( JavaScript Object Notation.)
Es un formato de texto para intercambio de datos muy utilizado en JavaScript que funciona como un reemplazo a XML gracias a su gran facilidad de manejo; y un gran número de APIs en los diferentes lenguajes para poderlos manejar. Tipos de datos que acepta
- String
- Number
- Object (JSON object)
- Array
- Boolean
- null
Estructuras permitidas
Los json se escriben en dos estructuras Nombre Valor
{
var1:1,
var2:true,
var3:null,
var4:{}
}
Lista de valores
{
var_a:[1,2,3,4,5],
var_b:[{},{},{}]
}
Convertir JSON a String
JSON.stringify({
var1:1,
var2:true,
var3:null,
var4:{}
})
//>"{"var1":1,"var2":true,"var3":null,"var4":{}}"
Convirtiendo texto a JSON
JSON.parse(`{"var1":1,"var2":true,"var3":null,"var4":{}}`)
//>
{
var1:1,
var2:true,
var3:null,
var4:{}
}
Cómo acceder a los valores de un json
const luke = {
"name": "Luke Skywalker",
"height": "172",
"mass": "77",
"films": [
{
"title": "The Empire Strikes Back",
"episode_id": 5
},
{
"title": "Revenge of the Sith",
"episode_id": 3
}
]
}
luke.name
//"Luke Skywalker"
luke.films[1].title
//"Revenge of the Sith"
El objeto this
El this es el objeto para referirnos al contexto donde estamos pero en algunos casos es necesario; saber administrarlo ya que cambia su contexto ya estemos en un evento, dentro de un foreach.
Por ejemplo en el siguiente ejemplo:
en método demo.print()
imprime el objeto Demo, pero el método demo.actionForEach()
hace referencia al objeto Window
class Demo{
constructor(){
this.months=['January','February']
}
actionForEach(){
this.months.forEach(function(item){
console.info(this)
})
}
print(){
console.info(this)
}
}
let demo = new Demo();
demo.print()
demo.actionForEach()
Para solucionar lo anterior debemos hacer referencia al this de la siguiente forma, para poder acceder a las propiedades y métodos de la clase Demo
class Demo{
constructor(){
this.months=['January','February']
}
actionForEach(){
this.months.forEach(function(item){
console.info(this)
},this)
}
print(){
console.info(this)
}
}
let demo = new Demo();
demo.print()
demo.actionForEach()
This y el método bind
Otro momento que analizar es cuando trabajamos con eventos, en este script se crea un Objeto Button y se le agrega un evento click y se le asigna el método nextMonth()
para que incremente el valor de la variable currentMonth
en 1, pero en la línea this.currentMonth++;
no puede acceder al objeto base porque este obedece al objeto que generó el evento en este caso el Button, por lo tanto el currentMonth
siempre será January
.
const Demo = function(){
this.months=['January','February','March','April','May','June','July','August','September','October','November','December']
this.currentMonth=0
this.btnNext = document.createElement('button')
this.btnNext.addEventListener('click', this.nextMonth);
}
Demo.prototype.nextMonth = function(){
this.currentMonth++;
}
Demo.prototype.printCurrentMonth = function(){
console.info(this.months[this.currentMonth])
}
const demo = new Demo();
demo.printCurrentMonth(); //--print January
demo.btnNext.dispatchEvent(new Event('click'));
demo.printCurrentMonth(); //--print January
demo.btnNext.dispatchEvent(new Event('click'));
demo.printCurrentMonth(); //--print January
If you copy and paste the script into Chrome Browser you will see the result
Para eso es necesario utilizar el método bind que lo que hace es enviar el this al método del evento para poder uso de sus propiedades de la forma this.nextMonth.bind(this).
const Demo = function(){
this.months=['January','February','March','April','May','June','July','August','September','October','November','December']
this.currentMonth=0
this.btnNext = document.createElement('button')
this.btnNext.addEventListener('click', this.nextMonth.bind(this));
}
Demo.prototype.nextMonth = function(){
this.currentMonth++;
}
Demo.prototype.printCurrentMonth = function(){
console.info(this.months[this.currentMonth])
}
const demo = new Demo();
demo.printCurrentMonth(); //--print January
demo.btnNext.dispatchEvent(new Event('click'));
demo.printCurrentMonth(); //--print February
demo.btnNext.dispatchEvent(new Event('click'));
demo.printCurrentMonth(); //--print March
Objetos
Las tres formas de escribir Clases
Prototype
var Dog = function(){
console.log('Dog:Constructor()')
}
Dog.prototype.talk = function(){
console.info('auuuuuu');
}
var dog = new Dog();
dog.talk();
//--auuuuuu
Object
const Dog = {
init:function init(){
console.log('Dog:Constructor()');
},
talk : function talk(){
console.info('auuuuuu');
}
}
const dog = Object.create(Dog);
dog.talk();
//--"auuuuuu"
Class
class Dog {
constructor(){
}
talk(){
console.info('auuuuuu');
}
}
const dog = new Dog();
dog.talk();
//--"auuuuuu"
Arrow functions
Las arrow functions son una forma más reducida de escribir funciones, esta es una forma de escribir funciones anónimas además que provee una particularidad de desvincular el this.
La siguiente función calcula el área de un círculo y veras la forma de cómo los Array Functions te ayudan a reducir el código.
Versión 1
var radio = 2;
var areaCircle =function(radio){
return Math.PI * Math.pow(radio,2)
}
var area=areaCircle(radio);
console.log('El area de un circulo con radio '+radio+' es: '+ area);//--El area de un circulo con radio 2 es: 12.566370614359172
__Versión 2: con reducción de codigo gracias __
let radio = 2
const areaCircle = (radio) => Math.PI * Math.pow(radio,2)
console.log(`El area de un circulo con radio ${radio} es: ${areaCircle(radio)}`);//--El area de un circulo con radio 2 es: 12.566370614359172
Pero qué pasa con el concepto de desvincular el this. Cantes cuando se utilizaba un foreach y se quería acceder al this del objeto que lo contenía era necesario reasignar el this o pasarlo como parámetro de esta forma
const Sum = function(){
this.numbers =[1,2,3,4,5,6,7,8,9];
this.total=0
this.sumElements = function(){
this.numbers.forEach(function(item){
this.total= this.total+item;//en este punto this corresponde al objeto Window
});
}
}
let sum = new Sum();
sum.sumElements();
sum.total
const Sum = function(){
this.numbers =[1,2,3,4,5,6,7,8,9];
this.total=0
let self = this;
this.sumElements = function(){
this.numbers.forEach(function(item){
self.total= self.total+item;
})
}
}
let sum = new Sum();
sum.sumElements();
sum.total
Ahora si utilizamos Array Functions no tenemos que asignar a otra variable el objeto this
const Sum = function(){
this.numbers =[1,2,3,4,5,6,7,8,9];
this.total=0
this.sumElements = function(){
this.numbers.forEach( (item) => {this.total= this.total+item;});
}
}
let sum = new Sum();
sum.sumElements();
sum.total
Arrays (solo se describen los más importantes)
filter() : regresa todos los elementos que cumplan con el filtro aplicado
let days=['Monday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday','Sunday']
console.info(days.filter(item => item.length>6))
//-- ["Tuesday", "Wednesday", "Thursday", "Saturday"]
map(): regresa un nuevo arreglo con las operaciones aplicadas a cada elemento
let days=['Monday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday','Sunday']
console.info(days.map(item => item.toUpperCase()) )
//-- ["MONDAY", "MONDAY", "TUESDAY", "WEDNESDAY", "THURSDAY", "FRIDAY", "SATURDAY", "SUNDAY"]
push(): agrega un nuevo elemento al array
let days=['Monday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday']
days.push('Sunday')
console.info(days)
//--["Monday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]
pop(): elimina el último elemento
let days=['Monday','Tuesday','Wednesday','Thursday','Friday','Saturday','Sunday']
days.pop()
console.info(days)
//--["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]
shift(): elimina el primer elemento de un array
let days=['Monday','Tuesday','Wednesday','Thursday','Friday','Saturday','Sunday']
var day = days.shift()
console.info(days)
//--["Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]
console.info(day)
//--"Monday"
slice:([posición inicial], [posición final]): hace una copia de los elementos seleccionados y el array principal queda intacto let days=[‘Monday’,’Tuesday’,’Wednesday’,’Thursday’,’Friday’,’Saturday’,’Sunday’]
var day = days.slice(2,4);
console.info(days);
//--["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]
console.info(day);
//--["Wednesday", "Thursday"]
splice([posición inicial], [número de elementos a extraer]): extrae elementos seleccionados del array principal.
let days=['Monday','Tuesday','Wednesday','Thursday','Friday','Saturday','Sunday']
var day = days.splice(2,2);
console.info(days);
//-- ["Monday", "Tuesday", "Friday", "Saturday", "Sunday"]
console.info(day);
//-- ["Wednesday", "Thursday"]
reduce(): El método reduce hace la funcion de acululador teniendo en cada ciclo el valor anterior con el valor nuevo.
const numbers=[1,2,3,4,5,6,7,8]
const total = numbers.reduce((a,b) => a+b)
console.info(numbers)
//--[1, 2, 3, 4, 5, 6, 7, 8]
console.info(`total: ${total}`)
//--"total: 36"
Asincronismo (callbacks, promesas, async-await)
En algunas ocasiones necesitamos llamar peticiones HTTP, y sabemos que para que la experiencia de usuario no sea desfavorable estas peticiones deben ser asíncronas, pero qué pasa si estas peticiones dependen una de otra, lo que significa anidar estas peticiones; para este caso necesitamos promesas. Una promesa no es más que una respuesta que puede o no estar disponible; esto significa que si lanzamos una petición la respuesta puede ser devuelta al instante o tardar 1,2,3,… minutos o no responder nunca. Para los ejemplos posteriores estaremos utilizando la API REST JSON PLACEHOLDER
jQuery Callbacks
Si analizamos el método ajax ajaxCallback()
podemos ver que existe la línea var deferred = $.Deferred();
el método $.Deferred()
es el que nos ayuda a generar una cola de llamadas y retransmite una petición resuelta o una petición rechazada como lo podemos ver en las líneas deferred.resolve(data ); deferred.reject(data);
.
Al analizar la llamada ajaxCallback().then(resolvedAnswer, rejectedAnwer)
podremos ver que llama al método then que es un manejador para ser llamado cuando un Objeto Deferred es resuelto o rechazado, este está aceptando dos funciones la primera es para cuando hay una petición resuelta(resolvedAnswer)
y la segunda cuando hay una petición rechazada(rejectedAnwer)
.
var ajaxCallback = function(){
var deferred = $.Deferred();
$.ajax ({
type: "GET",
url: 'https://jsonplaceholder.typicode.com/users/1',
dataType: 'json',
success: function (data,textStatus,jqXHR ){
deferred.resolve(data );
},
fail: function (data,textStatus,jqXHR){
deferred.reject(data);
},
error: function (data,textStatus,jqXHR){
deferred.reject(data);
}
});
return deferred.promise();
}
var resolvedAnswer = function(data){
console.debug('--resolvedAnswer--')
console.debug(data)
}
var rejectedAnwer = function(data){
console.debug('--rejectedAnwer--')
console.debug(data)
}
ajaxCallback().then(resolvedAnswer, rejectedAnwer)
Resolved Answer
"--resolvedAnswer--"
Object {
address: Object {
city: "Gwenborough",
geo: Object {},
street: "Kulas Light",
suite: "Apt. 556",
zipcode: "92998-3874"
},
company: Object {
bs: "harness real-time e-markets",
catchPhrase: "Multi-layered client-server neural-net",
name: "Romaguera-Crona"
},
email: "Sincere@april.biz",
id: 1,
name: "Leanne Graham",
phone: "1-770-736-8031 x56442",
username: "Bret",
website: "hildegard.org"
}
Rejected Answer
"--rejectedAnwer--"
Object{
...
readyState: 4,
responseJSON: Object{},
responsetext:"{}"
status:404,
statusText:'error',
...
}
Promise Callbacks
Si no queremos utilizar jQuery la opción es utilizar el Objeto Promise que funciona masomenos como un proxy para encontrar un valor no disponible en el momento pero puede o no estar en el futuro.
Tenemos el mismo ejemplo anterior, si analizamos promiseCallback
veremos que regresa una promesa(return new Promise(function(resolve, reject) )
y mediante el objeto XMLHttpRequest()
hace una petición asíncrona para obtener un usuario y una vez responda se valida que tipo de status regresa si es un 200 entonces la promesa envía un resolver (resolve( req.response );
) y en caso contrario un reject reject( Error(req.statusText));
.
Para hacer la petición se hace de la siguiente forma promiseCallback().then( resolvedAnswer, rejectedAnwer)
y si observamos se utiliza también el then con los mismos parámetros el primero para una ejecución exitosa () y la segunda en caso de que exista algún error.
const promiseCallback = function () {
return new Promise(function(resolve, reject) {
var req = new XMLHttpRequest();
req.open('GET', 'https://jsonplaceholder.typicode.com/users/1');
req.onload = function() {
if (req.status == 200) {
resolve(req.response);
}
else {
reject(Error(req.statusText));
}
};
req.onerror = function() {
reject(Error("Network Error"));
};
req.send();
});
}
var resolvedAnswer = function(data){
console.debug('--resolvedAnswer--')
console.debug(data)
}
var rejectedAnwer = function(data){
console.debug('--rejectedAnwer--')
console.debug(data)
}
promiseCallback().then(resolvedAnswer, rejectedAnwer)
Resolved Answer
"--resolvedAnswer--"
{
'id': 1,
'name': 'Leanne Graham',
'username': 'Bret',
'email': 'Sincere@april.biz',
'address': {
'street': 'Kulas Light',
'suite': 'Apt. 556',
'city': 'Gwenborough',
'zipcode': '92998-3874',
'geo': {
'lat': '-37.3159',
'lng': '81.1496'
}
},
'phone': '1-770-736-8031 x56442',
'website': 'hildegard.org',
'company': {
'name': 'Romaguera-Crona',
'catchPhrase': 'Multi-layered client-server neural-net',
'bs': 'harness real-time e-markets'
}
}
Fetch
La siguiente implementación son promesas con API Fetch que es una API para recuperar recursos, es muy similar a la anterior. Lo que podemos decir de fetch es que tiene una particularidad de que todas las respuestas las responde de forma correcta lo que significa que todas caerán dentro del then y es ahí donde tenemos que evaluar el status para resolver la petición o rechazarla.
const promiseCallback = function () {
return new Promise((resolve, reject) => {
fetch('https://jsonplaceholder.typicode.com/users/1')
.then((response) => {
if(response.status==200){
resolve(response.json())
}else{
reject(response);
}
})
.catch((error) => console.info(error) )
});
}
var resolvedAnswer = function(data){
console.debug('--resolvedAnswer--')
console.debug('->',data)
}
var rejectedAnwer = function(data){
console.debug('--rejectedAnwer--')
console.debug('-->',data)
}
promiseCallback().then(resolvedAnswer, rejectedAnwer)
Async Await
Async Await apareció en el ES2017 y es una nueva definición para implementar promesas, lo que hace es definir un método de tipo async de la forma async function getData() {}
este utiliza el await para hacer esperar a la función hasta que esta responda; de forma de una promesa resuelta y sólo si entra dentro de la estructura catch devolverá una promesa rechazada.
async function asyncAwaitCallback() {
try {
const response1 = await fetch('https://jsonplaceholder.typicode.com/users/1');
return await response1.json();
} catch (error) {
throw error;
}
}
var resolvedAnswer = function(data){
console.debug('--resolvedAnswer--')
console.debug('->',data)
}
var rejectedAnwer = function(data){
console.debug('--rejectedAnwer--')
console.debug('-->',data)
}
asyncAwaitCallback().then(resolvedAnswer) .catch(rejectedAnwer);
Eventos
Los eventos son interacciones de un usuario dentro del DOM(Document Object Model) como desarrollador usamos los eventos para generar tareas como por ejemplo cambiar el color de un botón cuando es presionado. Existen tres modos de generar eventos Evento dentro de las etiquetas HTML En este ejemplo se asocia el evento onclick a un botón para que despliegue un mensaje de alerta a ser presionado.
<button onclick="alert('Hello world!')"></button>
Evento como propiedad de un elemento DOM
En este caso se referenci el objeto DOM y a la propiedad onclick se le asocia una función anónimo.
const myButton = document.getElementsByTagName('button ')[0]
myButton.onclick = function(event){alert('Hello world');};
Evento con EventTarget.addEventListener
Esta forma es la más utilizada en la actualidad, y lo que hace es registrar un evento específico y asociarse un método.
const myButton = document.getElementsByTagName('button ')[0]
myButton.addEventListener('onclick',function(event){alert('Hello world')});