Colección de propiedades (pares nombre-valor).
Todo son objetos (las funciones también) excepto los primitivos: strings, números, booleans, null o undefined
Para saber si es un objeto o un primitivo hacer typeof variable
objeto.nombre === objeto[nombre] // true
var objeto = {};
objeto.nuevaPropiedad = 1; // añadir
delete objeto.nuevaPropiedad; // eliminar
var objeto = {
nombre: "Adolfo",
twitter: "@asanzdiego"
};
function Persona(nombre, twitter) {
this.nombre = nombre;
this.twitter = twitter;
};
var objeto = new Persona("Adolfo", "@asanzdiego");
Las funciones son objetos y tienen una propiedad llamada prototype.
Cuando creamos un objeto con new, la referencia a esa propiedad prototype es almacenada en una propiedad interna.
El prototipo se utiliza para compartir propiedades.
// Falla en Opera o IE <= 8
Object.getPrototypeOf(objeto);
// No es estandar y falla en IE
objeto.__proto__;
function ConstructorA(p1) {
this.p1 = p1;
}
// los métodos los ponenmos en el prototipo
ConstructorA.prototype.metodo1 = function() {
console.log(this.p1);
};
function ConstructorA(p1) {
this.p1 = p1;
}
function ConstructorB(p1, p2) {
// llamamos al super para que no se pierda p1.
ConstructorA.call(this, p1);
this.p2 = p2;
}
// Hacemos que B herede de A
// Prototipo de Función Constructora B apunta al
// Prototipo de Función Constructora A
ConstructorB.prototype = Object.create(ConstructorA.prototype);
instanciaB.__proto__ == ConstructorB.prototype // true
instanciaB.__proto__.__proto__ == ConstructorA.prototype // true
instanciaB.__proto__.__proto__.__proto__ == Object.prototype // true
instanciaB.__proto__.__proto__.__proto__.__proto__ == null // true
expect(ConstructorB.__proto__).toEqual(Function.prototype);
expect(ConstructorB.__proto__.__proto__).toEqual(Object.prototype);
expect(ConstructorB.__proto__.__proto__.__proto__).toEqual(null);
La expresión instanciaB instanceof ConstructorA devolverá true, si el prototipo de la Función ConstructorA, se encuentra en la cadena de prototipos de la instanciaB.
En el ejemplo anterior:
instanciaB instanceof ConstructorB; // true
instanciaB instanceof ConstructorA; // true
instanciaB instanceof Object; // true
Con los prototipos podemos extender la funcionalidad del propio lenguaje.
Ejemplo:
String.prototype.hola = function() {
return "Hola "+this;
}
"Adolfo".hola(); // "Hola Adolfo"
Lo que se define dentro de la función constructora va a ser propio de la instancia.
Pero como hemos dicho, en JavaScript, una función es un objeto, al que podemos añadir tanto atributos como funciones.
Añadiendo atributos y funciones a la función constructora obtenemos propiedades y métodos estáticos.
function ConstructorA() {
ConstructorA.propiedadEstatica = "propiedad estática";
}
ConstructorA.metodoEstatico = function() {
console.log("método estático");
}
La visibilidad de objetos depende del contexto.
Los contextos en JavaScript son bloques de código entre dos {} y en general, desde uno de ellos, solo tienes acceso a lo que en él se defina y a lo que se defina en otros contextos que contengan al tuyo.
function ConstructorA(privada, publica) {
var propiedadPrivada = privada;
this.propiedadPublica = publica;
var metodoPrivado = function() {
console.log("-->propiedadPrivada", propiedadPrivada);
}
this.metodoPublico = function() {
console.log("-->propiedadPublica", this.propiedadPublica);
metodoPrivado();
}
}
Poder llamar a métodos sintácticamente iguales de objetos de tipos diferentes.
Esto se consigue mediante herencia.
Son objetos con sus propiedades.
Se pueden pasar como parámetros a otras funciones.
Pueden guardarse en variables.
Son mensajes cuyo receptor es this.
var nombre = "Laura";
var alba = {
nombre: "Alba",
saludo: function() {
return "Hola "+this.nombre;
}
}
alba.saludo(); // Hola Alba
var fn = alba.saludo;
fn(); // Hola Laura
fn.call(thisArg [, arg1 [, arg2 [...]]])
fn.apply(thisArg [, arglist])
function echoArgs() {
console.log(arguments[0]); // Adolfo
console.log(arguments[1]); // Sanz
}
echoArgs("Adolfo", "Sanz");
function holaMundo1() {
console.log("Hola Mundo 1");
}
holaMundo1();
var holaMundo2 = function() {
console.log("Hola Mundo 2");
}
holaMundo2();
function saluda() {
console.log("Hola")
}
function ejecuta(func) {
func()
}
ejecuta(saluda);
Hemos dicho que las funciones se pueden declarar.
Pero también podemos no declararlas y dejarlas como anónimas.
function(nombre) {
console.log("Hola "+nombre);
}
function saludador(nombre) {
return function() {
console.log("Hola "+nombre);
}
}
var saluda = saludador("mundo");
saluda(); // Hola mundo
(function(nombre) {
console.log("Hola "+nombre);
})("mundo")
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
miModulo = (function() {
var propiedadPrivada;
function metodoPrivado() { };
// API publica
return {
metodoPublico1: function () {
},
metodoPublico2: function () {
}
}
}());
Si se ejecuta desde el navegador, se suele pasar como parámetro el objeto window para mejorar el rendimiento. Así cada vez que lo necesitemos el intérprete lo utilizará directamete en lugar de buscarlo remontando niveles.
Y también se suele pasar el parámetro undefined, para evitar los errores que pueden darse si la palabra reservada ha sido reescrita en alguna parte del código y su valor no corresponda con el esperado.
miModulo = (function(window, undefined) {
// El código va aquí
})( window );
El problema del patrón Modulo es pasar un método de privado a público o viceversa.
Por ese motivo lo que que se suele hacer es definir todo en el cuerpo, y luego referenciar solo los públicos en el bloque return.
miModulo = (function() {
function metodoA() { };
function metodoB() { };
function metodoC() { };
// API publica
return {
metodoPublico1: metodoA,
metodoPublico2: metodoB
}
}());
miBiblioteca = miBiblioteca || {};
miBiblioteca.seccion1 = miBiblioteca.seccion1 || {};
miBiblioteca.seccion1 = {
priopiedad: p1,
metodo: function() { },
};
miBiblioteca.seccion2 = miBiblioteca.seccion2 || {};
miBiblioteca.seccion2 = {
priopiedad: p2,
metodo: function() { },
};
miBiblioteca = miBiblioteca || {};
(function(namespace) {
var propiedadPrivada = p1;
namespace.propiedadPublica = p2;
var metodoPrivado = function() { };
namespace.metodoPublico = function() { };
}(miBiblioteca));
getElementById(id)
getElementsByName(name)
getElementsByTagName(tagname)
getElementsByClassName(className)
getAttribute(attributeName)
querySelector(selector)
querySelectorAll(selector)
createElement(tagName)
createTextNode(text)
createAttribute(attributeName)
appendChild(node)
insertBefore(newElement, targetElement)
removeAttribute(attributename)
removeChild(childreference)
replaceChild(newChild, oldChild)
attributes[]
className
id
innerHTML
nodeName
nodeValue
style
tabIndex
tagName
title
childNodes[]
firstChild
lastChild
previousSibling
nextSibling
ownerDocument
parentNode
// Vanilla JavaScript
var elem = document.getElementById("miElemento");
//jQuery
var elem = $("#miElemento");
jQuery UI: diseño interfaces gráficas.
jQuery Mobile: versión adaptada para móviles (eventos y tamaño).
Fácil maquetación, sistema rejilla, clases CSS, temas, etc.
BackboneJS: ligero y flexible.
EmberJS: "Convention over Configuration", muy popular entre desarrolladores Ruby on Rails.
AngularJS extiende etiquetas HML (g-app, ng-controller, ng-model, ng-view), detrás está Google, tiene gran popularidad, abrupta curva de aprendizaje.
Bower: para el lado cliente. Puede trabajar con repositorios Git.
Browserify: permite escribir módulos como en NodeJS y compilarlos para que se puedan usar en el navegador.
RequeriJS: las dependencias se cargan de forma asíncrona y solo cuando se necesitam.
WebPack: es un empaquetador de módulos
AppJS, y su fork DeskShell: los más antiguos, un poco abandonados.
NW.js: opción más popular y madura hoy en día.
Electron: creada para el editor Atom de GitHub: está creciendo en popularidad.
cordova: una de los primeros. Hoy en día, otros frameworks se basan en él.
ionic: utiliza AngularJS, tiene una CLI, muy popular.
React Native: recién liberado por facebook.
WebComponents es una especificación de la W3C para permitir crear componentes y reutilizarlos.
polymer: proyecto de Google para poder empezar a usar los WebComponents en todos los navegadores.
var EventBus = {
topics: {},
subscribe: function(topic, listener) {
if (!this.topics[topic]) this.topics[topic] = [];
this.topics[topic].push(listener);
},
publish: function(topic, data) {
if (!this.topics[topic] || this.topics[topic].length < 1) return;
this.topics[topic].forEach(function(listener) {
listener(data || {});
});
}
};
EventBus.subscribe('foo', alert);
EventBus.publish('foo', 'Hello World!');
var Mailer = function() {
EventBus.subscribe('order/new', this.sendPurchaseEmail);
};
Mailer.prototype = {
sendPurchaseEmail: function(userEmail) {
console.log("Sent email to " + userEmail);
}
};
var Order = function(params) {
this.params = params;
};
Order.prototype = {
saveOrder: function() {
EventBus.publish('order/new', this.params.userEmail);
}
};
var mailer = new Mailer();
var order = new Order({userEmail: 'john@gmail.com'});
order.saveOrder();
"Sent email to john@gmail.com"
Evento | Descripción |
---|---|
onblur | Un elemento pierde el foco |
onchange | Un elemento ha sido modificado |
onclick | Pulsar y soltar el ratón |
ondblclick | Pulsar dos veces seguidas con el ratón |
Evento | Descripción |
---|---|
onfocus | Un elemento obtiene el foco |
onkeydown | Pulsar una tecla y no soltarla |
onkeypress | Pulsar una tecla |
onkeyup | Soltar una tecla pulsada |
onload | Página cargada completamente |
Evento | Descripción |
---|---|
onmousedown | Pulsar un botón del ratón y no soltarlo |
onmousemove | Mover el ratón |
onmouseout | El ratón "sale" del elemento |
onmouseover | El ratón "entra" en el elemento |
onmouseup | Soltar el botón del ratón |
Evento | Descripción |
---|---|
onreset | Inicializar el formulario |
onresize | Modificar el tamaño de la ventana |
onselect | Seleccionar un texto |
onsubmit | Enviar el formulario |
onunload | Se abandona la página |
var windowOnLoad = function(e) {
console.log('window:load', e);
};
window.addEventListener('load', windowOnLoad);
window.removeEventListener('load', windowOnLoad);
var event = new Event('build');
elem.addEventListener('build', function (e) { ... }, false);
var event = new CustomEvent('build', { 'detail': detail });
elem.addEventListener('build', function (e) {
log('The time is: ' + e.detail);
}, false);
function simulateClick() {
var event = new MouseEvent('click');
var element = document.getElementById('id');
element.dispatchEvent(event);
}
1 2
| | / \
+-------------| |------------| |-------------+
| DIV1 | | | | |
| +---------| |------------| |---------+ |
| | DIV2 | | | | | |
| | +-----| |------------| |-----+ | |
| | | A \ / | | | | |
| | +----------------------------+ | |
| +------------------------------------+ |
| FASE DE FASE DE |
| CAPTURA BURBUJA |
| DE EVENTOS DE EVENTOS |
+--------------------------------------------+
// en fase de CAPTURA
addEventListener("eventName",callback, true);
// en fase de BURBUJA
addEventListener("eventName",callback, false); // por defecto
// detiene la propagación del evento
event.stopPropagation();
// elimina las acciones por defecto (ejemplo: abrir enlace)
event.preventDefault();
Librería cliente y servidor (NodeJS) para utilizar WebSockets:
Permite utilizar navegadores sin soporte de WebSockets.
AJAX no es una tecnología en sí misma, en realidad, se trata de varias tecnologías independientes que se unen de formas nuevas y sorprendentes.
El intercambio de datos AJAX entre cliente y servidor se hace mediante el objeto XMLHttpRequest, disponible en los navegadores actuales.
No es necesario que el contenido esté formateado en XML.
Su manejo puede llegar a ser complejo, aunque librerías como jQuery facilitan enormemente su uso.
var http_request = new XMLHttpRequest();
var url = "http://example.net/jsondata.php";
// Descarga los datos JSON del servidor.
http_request.onreadystatechange = handle_json;
http_request.open("GET", url, true);
http_request.send(null);
function handle_json() {
if (http_request.status == 200) {
var json_data = http_request.responseText;
var the_object = eval("(" + json_data + ")");
} else {
alert("Ocurrio un problema con la URL.");
}
}
miObjeto = eval('(' + json_datos + ')');
Eval es muy rápido, pero como compila y ejecuta cualquier código JavaScript, las consideraciones de seguridad recomiendan no usarlo.
{
curso: "AJAX y jQuery",
profesor: "Adolfo",
participantes: [
{ nombre: "Isabel", edad: 35 },
{ nombre: "Alba", edad: 15 },
{ nombre: "Laura", edad: 10 }
]
}
Por seguridad XMLHttpRequest sólo puede realizar peticiones al mismo dominio.
JSONP envuelve el JSON en una función definida por el cliente.
Esto nos permite hacer peticiones GET (sólo GET) a dominios distintos.
Protocolo Cross-Origin Resource Sharing (Compartición de recursos de distintos orígenes).
Realizar peticiones a otros dominios siempre y cuando el dominio de destino esté de acuerdo en recibir peticiones del dominio de origen.
Tanto navegador como servidor tienen que implementar el protocolo.
Access-Control-Allow-Origin: http://dominio-permitido.com
REST (Representational State Transfer) es una técnica de arquitectura software para sistemas hipermedia distribuidos como la World Wide Web.
Es decir, una URL (Uniform Resource Locator) representa un recurso al que se puede acceder o modificar mediante los métodos del protocolo HTTP (POST, GET, PUT, DELETE).
Definición de Módulos Asíncronos (AMD)
La implementación más popular de este estándar es RequireJS.
Sintaxis un poco complicada.
Permite la carga de módulos de forma asíncrona.
Se usa principalmente en navegadores.
<!DOCTYPE html>
<html>
<head>
<title>Page 1</title>
<script data-main="js/index" src="js/lib/require.js"></script>
</head>
<body>
<h1>Hola Mundo</h1>
</body>
</html>
requirejs(['./common'], function (common) {
requirejs(['app/main']);
});
define(function (require) {
var $ = require('jquery');
var persona = require('./persona');
$('h1').html("Hola requery.js");
var p = new persona("Adolfo", 30);
p.saludar();
});
define(function () {
var Persona = function(nombre, edad) {
this.nombre = nombre;
Persona.prototype.saludar = function() {
alert("Hola, mi nombre es " + this.nombre);
};
}
return Persona;
});
La implementación usada en NodeJS y Browserify.
Sintaxis sencilla.
Carga los módulos de forma síncrona.
Se usa principalmente en el servidor.
npm install -g browserify
npm install
{
"name": "browserify-example",
"version": "1.0.0",
"dependencies": {
"jquery": "^2.1.3"
}
}
browserify js/main.js -o js/bundle.js
<!doctype html>
<html>
<head>
<title>Browserify Playground</title>
</head>
<body>
<h1>Hola Mundo</h1>
<script src="js/bundle.js"></script>
</body>
</html>
var $ = require('jquery');
var persona = require('./persona');
$('h1').html('Hola Browserify');
var p = new persona("Adolfo", 30);
p.saludar();
var Persona = function(nombre, edad) {
this.nombre = nombre;
Persona.prototype.saludar = function() {
alert("Hola, mi nombre es " + this.nombre);
};
}
module.exports = Persona;
Coje lo mejor de los 2 enfoques:
// ES5
var data = [{...}, {...}, {...}, ...];
data.forEach(function(elem){
console.log(elem)
});
//ES6
var data = [{...}, {...}, {...}, ...];
data.forEach(elem => {
console.log(elem);
});
// ES5
var miFuncion = function(num1, num2) {
return num1 + num2;
}
// ES6
var miFuncion = (num1, num2) => num1 + num2;
//ES5
var objEJ5 = {
data : ["Adolfo", "Isabel", "Alba"],
duplicar : function() {
var that = this;
this.data.forEach(function(elem){
that.data.push(elem);
});
return this.data;
}
}
//ES6
var objEJ6 = {
data : ["Adolfo", "Isabel", "Alba"],
duplicar : function() {
this.data.forEach((elem) => {
this.data.push(elem);
});
return this.data;
}
}
//ES5
var Shape = function (id, x, y) {
this.id = id;
this.move(x, y);
};
Shape.prototype.move = function (x, y) {
this.x = x;
this.y = y;
};
//ES6
class Shape {
constructor (id, x, y) {
this.id = id
this.move(x, y)
}
move (x, y) {
this.x = x
this.y = y
}
}
//ES5
var Rectangle = function (id, x, y, width, height) {
Shape.call(this, id, x, y);
this.width = width;
this.height = height;
};
Rectangle.prototype = Object.create(Shape.prototype);
Rectangle.prototype.constructor = Rectangle;
var Circle = function (id, x, y, radius) {
Shape.call(this, id, x, y);
this.radius = radius;
};
Circle.prototype = Object.create(Shape.prototype);
Circle.prototype.constructor = Circle;
//ES6
class Rectangle extends Shape {
constructor (id, x, y, width, height) {
super(id, x, y)
this.width = width
this.height = height
}
}
class Circle extends Shape {
constructor (id, x, y, radius) {
super(id, x, y)
this.radius = radius
}
}
//ES5
(function() {
console.log(x); // x no está definida aún.
if(true) {
var x = "hola mundo";
}
console.log(x);
// Imprime "hola mundo", porque "var"
// hace que sea global a la función;
})();
//ES6
(function() {
if(true) {
let x = "hola mundo";
}
console.log(x);
//Da error, porque "x" ha sido definida dentro del "if"
})();
//ES5
(function () {
var foo = function () { return 1; }
foo() === 1;
(function () {
var foo = function () { return 2; }
foo() === 2;
})();
foo() === 1;
})();
//ES6
{
function foo () { return 1 }
foo() === 1
{
function foo () { return 2 }
foo() === 2
}
foo() === 1
}
//ES6
(function() {
const PI;
PI = 3.15;
// ERROR, porque ha de asignarse un valor en la declaración
})();
//ES6
(function() {
const PI = 3.15;
PI = 3.14159;
// ERROR de nuevo, porque es de sólo-lectura
})();
//ES6
let nombre1 = "JavaScript";
let nombre2 = "awesome";
console.log(`Sólo quiero decir que ${nombre1} is ${nombre2}`);
// Solo quiero decir que JavaScript is awesome
//ES5
var saludo = "ola " +
"que " +
"ase ";
//ES6
var saludo = `ola
que
ase`;
//ES6
var [a, b] = ["hola", "mundo"];
console.log(a); // "hola"
console.log(b); // "mundo"
//ES6
var obj = { nombre: "Adolfo", apellido: "Sanz" };
var { nombre, apellido } = obj;
console.log(nombre); // "Adolfo"
console.log(apellido); // "Sanz"
//ES6
var foo = function() {
return ["180", "78"];
};
var [estatura, peso] = foo();
console.log(estatura); //180
console.log(peso); //78
//ES5
function f (arg) {
var name = arg[0];
var val = arg[1];
console.log(name, val);
};
function g (arg) {
var n = arg.name;
var v = arg.val;
console.log(n, v);
};
function h (arg) {
var name = arg.name;
var val = arg.val;
console.log(name, val);
};
f([ "bar", 42 ]);
g({ name: "foo", val: 7 });
h({ name: "bar", val: 42 });
//ES6
function f ([ name, val ]) {
console.log(name, val)
}
function g ({ name: n, val: v }) {
console.log(n, v)
}
function h ({ name, val }) {
console.log(name, val)
}
f([ "bar", 42 ])
g({ name: "foo", val: 7 })
h({ name: "bar", val: 42 })
//ES5
function f (x, y) {
var a = Array.prototype.slice.call(arguments, 2);
return (x + y) * a.length;
};
f(1, 2, "hello", true, 7) === 9;
//ES6
function f (x, y, ...a) {
return (x + y) * a.length
}
f(1, 2, "hello", true, 7) === 9
//ES5
function(valor) {
valor = valor || "foo";
}
//ES6
function(valor = "foo") {...};
//ES6
// lib/math.js
export function sum (x, y) { return x + y }
export function div (x, y) { return x / y }
export var pi = 3.141593
//ES6
// someApp.js
import * as math from "lib/math"
console.log("2π = " + math.sum(math.pi, math.pi))
// otherApp.js
import { sum, pi } from "lib/math"
console.log("2π = " + sum(pi, pi))
//ES6
function *soyUnGenerador(i) {
yield i + 1;
yield i + 2;
yield i + 3;
}
var gen = soyUnGenerador(1);
console.log(gen.next());
// Object {value: 2, done: false}
console.log(gen.next());
// Object {value: 3, done: false}
console.log(gen.next());
// Object {value: 4, done: false}
console.log(gen.next());
// Object {value: undefined, done: true}
//ES6
let s = new Set()
s.add("hello").add("goodbye").add("hello")
s.size === 2
s.has("hello") === true
for (let key of s.values()) { // insertion order
console.log(key)
}
//ES6
let m = new Map()
m.set("hello", 42)
m.set(s, 34)
m.get(s) === 34
m.size === 2
for (let [ key, val ] of m.entries()) {
console.log(key + " = " + val)
}
//ES6
"hello".startsWith("ello", 1) // true
"hello".endsWith("hell", 4) // true
"hello".includes("ell") // true
"hello".includes("ell", 1) // true
"hello".includes("ell", 2) // false
//ES6
Number.isNaN(42) === false
Number.isNaN(NaN) === true
Number.isSafeInteger(42) === true
Number.isSafeInteger(9007199254740992) === false
//ES6
let target = {
foo: "Welcome, foo"
}
let proxy = new Proxy(target, {
get (receiver, name) {
return name in receiver ? receiver[name] : `Hello, ${name}`
}
})
proxy.foo === "Welcome, foo"
proxy.world === "Hello, world"
//ES6
var i10nUSD = new Intl.NumberFormat("en-US", { style: "currency", currency: "USD" })
var i10nGBP = new Intl.NumberFormat("en-GB", { style: "currency", currency: "GBP" })
i10nUSD.format(100200300.40) === "$100,200,300.40"
i10nGBP.format(100200300.40) === "£100,200,300.40"
//ES6
var i10nEN = new Intl.DateTimeFormat("en-US")
var i10nDE = new Intl.DateTimeFormat("de-DE")
i10nEN.format(new Date("2015-01-02")) === "1/2/2015"
i10nDE.format(new Date("2015-01-02")) === "2.1.2015"
//ES6
var promise = new Promise(function(resolve, reject) {
var todoCorrecto = true; // o false dependiendo de como ha ido
if (todoCorrecto) {
resolve("Promesa Resuelta!");
} else {
reject("Promesa Rechazada!");
}
});
//ES6
// llamamos el metodo 'then' de la promesa
// con 2 callbacks (resolve y reject)
promise.then(function(result) {
console.log(result); // "Promesa Resuelta!"
}, function(err) {
console.log(err); // Error: "Promesa Rechazada!"
});
//ES6
// podemos también llamar al 'then' con el callback 'resolve'
// y luego al 'catch' con el callback 'reject'
promise.then(function(result) {
console.log(result); // "Promesa Resuelta!"
}).catch(function(err) {
console.log(err); // Error: "Promesa Rechazada!"
});
//ES6
Promise.all([promesa1,promesa2]).then(function(results) {
console.log(results); // cuando todas las promesas terminen
}).catch(function(err) {
console.log(err); // Error: "Error en alguna promesa!"
});
//ES6
Promise.race([promesa1,promesa2]).then(function(firstResult) {
console.log(firstResult); // cuando termine la primera
}).catch(function(err) {
console.log(err); // Error: "Error en alguna promesa!"
});
/