Javascript - 2014
-
Upload
carlos-alonso-perez -
Category
Engineering
-
view
187 -
download
1
Transcript of Javascript - 2014
#FormaciónEBusiness
Mobile Apps Desarrollo en JavaScript Carlos Alonso Pérez
#FormaciónEBusiness
COMPARTE Y DEBATE #DIPLOMADOAPPS
Antes de empezar... Presentaciones
• Nombre • Edad • Estudios • ¿A qué te dedicas? • ¿Por qué estás aquí? • ¿Qué esperas de este curso? • ¿Qué experiencia tienes con JS, HTML y CSS?
Metodología
Metodología Práctica
• En la explicación • En labs • En ejercicios
Sobre las dudas
Índice de contenidos
• Introducción y entorno de desarrollo • Conceptos de programación básicos • Conceptos de programación avanzados
Introducción• ¿Qué sabemos de Javascript?
– Historia – ¿Es un lenguaje de programación? ¿de scripting? ¿un subconjunto de Java? – ¿Estructurado u OO? – ¿Compilado o interpretado? – ¿Imperativo, funcional o híbrido? – ¿Fuerte o débilmente tipado? – ¿Dinámico? – ¿Cuáles son sus entornos de ejecución? – ¿Sencillo o difícil? – ¿Potente? – ¿Tiene errores? – ¿Tiene buena o mala reputación?
¡¡No conocemos Javascript!!
Re-introducción• ¿Quién es Javascript?
– Historia – Lenguaje de scripting más usado del mundo
Re-introducción• ¿Quién es Javascript?
– Sintaxis C – Completamente Orientado a Objetos – Interpretado – Funcional Híbrido – Débilmente tipado – Dinámico – Fácil de aprender, difícil de dominar – Muy potente – Sí, tiene errores
La reputación de Javascript
Entorno de desarrollo• Obligatorio
– Editor de textos – Navegador + debugger – Documentación: https://developer.mozilla.org/en-‐US
• Opcional – IDE – JSLint: http://www.jslint.com – JSfiddle: http://js:iddle.net
Mi elección
++
+ +
Programación: Conceptos básicos
• Estructura léxica • Tipos, valores y
variables • Funciones • Clases
Pero antes... Debugging
Pero antes... Debugging
Lab 1
Estructura léxica
• Juego de caracteres: Unicode case sensitive
• Comentarios C • Literales • Sentencias
/* * Literales */121.2“Hello World!”
/* * Sentencias */var a = 1;
Estructura léxica
• Identificadores – i, my_variable, _dummy, $str, sí, π
• Palabras reservadas – break delete case do catch else continue false debugger finally default
for function return typeof if switch var in this void instanceof throw while new true with null try
Estructura léxica
; es opcional!!
/* * Comportamiento esperado */1 + 2 “hola mundo!”
/* * Comportamiento inesperado! */var f = 21 + f(2 + 3) * 2
Tipos primitivos
• Numéricos • Cadenas • undefined • null • Booleanos
Number
• Sintaxis: [dígitos][.dígitos][(E|e)(+|-)dígitos] • Enteros:
– 0, 3, 10000000
• Floats: – 3.14, 2345.678, .333333, 6.02e23, 1.45632E-32
Number: Valores especiales.
console.log(1E309);console.log(-1E309);console.log(1/0);
Infinityconsole.log(1/-0);console.log(0 == -0);
-0
console.log(1/”hola”);console.log(-1E309);console.log(1/0);
NaN"hola" == NaN;isNaN("hola");(1/-0) == Infinity;isFinite(1/-0);
isFinite() isNaN()
String
• Definición => “Hola Mundo!” • Concatenación => “Hola” + “Mundo!” • Longitud => “Hola Mundo!”.length • Array de caracteres => “Hola Mundo!”[0]
undefined y null
• undefined => no inicializado
• null => no hay objeto o valor
Booleanos
• true y false
• Tabla de verdad: – false => undefined, null, 0, -0, NaN y “” – true => Todo lo demás
Detectando tipos
Operador typeof
/* * Detección de tipos */typeof 2typeof 2.33typeof “Hola Mundo!”typeof undefinedtypeof truetypeof null // Es un objeto!!
Tipos proporcionados por JS
• Fechas y horas • El objeto global • Wrapper objects • Conversión de tipos (Casting)
Fechas y horas
• Clase Date • Documentación:
– https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date
/* Ejemplo uso de fechas */var now = new Date();var future = new Date(now.getTime() + 300000);future - now;
Fechas y horas: Ejercicio!• Calcular el número de días que han pasado desde
mi nacimiento el 19 - Marzo - 1985. • Documentación:
– https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/round
Fechas y horas: Ejercicio!• Calcular el número de días que han pasado desde
mi nacimiento el 19 - Marzo - 1985. • Documentación:
– https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date
/** Solución: 10700.6108…*/
var now = new Date();var carlos = new Date(1985, 2, 19);var millis = now - carlos;millis / 1000 / 60 / 60 / 24;
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/round
El objeto global
• Creado al inicializar el intérprete JS • Es nuestro contexto de ejecución (this) • Proporciona métodos
– isNaN(), isFinite(), ...
/* * El objeto global nos permite usar sus métodos * directamente, sin usar this */isNaN(“hola”);this.isNaN(“hola”);
El objeto global
• Proporciona Constructores: – Date, Object, ...
• Almacena nuestras variables globales
/* * Constructores */var now = new this.Date();now;
/* * Almacena variables * globales */var now = new Date();this.now;
Wrapper Objects
/* * Ejemplo 1 */var s = “Hola Mundo!”;console.log(typeof s);s.charAt(0);
/* * Ejemplo 2 */var s = “Hola Mundo!”;s.my_dummy_var = 56;s.my_dummy_var;
Conversión automática entre tipos primitivos y su correspondiente ‘wrapper object’
Wrapper Objects
/* * Ejemplo 1 */var s = new String(“Hola!”);console.log(typeof s);s.my_dummy_var = 56;s.my_dummy_var;
/* * Ejemplo 2 */var n = new Number(5);console.log(5 == n);5 === n;
Conversión automática entre ‘wrapper objects’ y su tipo primitivo
Conversión de tipos (Casting)
/* * Ejemplos */10 + “objects” // “10 objects”“7” * “4” // 28var n = 1 - “x” // NaNn + “objects” // “NaN objects”10 + “” // “10”+”1” // 1“1” - 0 // 1!!”0” //true (doble negación)
Casting implícito
Conversión de tipos (Casting)
/* * Ejemplos */Number(“2”) // 2String(false) // “false”Boolean([]) // trueObject(3) // new Number(3)parseInt(“3 min”) // 3parseInt(“0.1”) // 0parseFloat(“0.1”) // 0.1
Casting explícito
Variables
• Declaración • Scope
Variables: Declaración
/* * Declaración e inicialización */var i; // Declaración simplevar i, j, k; // Declaración múltiplevar i = 0; // Inicialización simplevar i = 0, j = 1, k = 2; // Inicialización múltiplevar a, b = “hola”; // Declaración + inicialización
Variables: Redeclaración y omisión
/* * Redeclaración */var a = 1;var a = 2;a; // 2
/* * Redeclaración */var a = 1;var a;a; // 1
/* * Omisión */a = 1;a // 1
Variables: Scope
var scope = “global”;function checkscope() {
scope = “local”;}checkscope();scope; // local
• Global: Variables disponibles en cualquier sección del código.
var scope = “global”;function checkscope() {
var scope = “local”;}checkscope();scope; // global
• Local: Variables definidas dentro de una función.
• Eclipsan variables globales con el mismo nombre.
Variables: Scope
El problema de definir variables omitiendo ‘var’.
scope = “global”;function checkscope() {
scope = “local”;localvar = “local”;
}checkscope();scope; // local => ¡¡Ha sido modificada!!localvar; // local => ¡¡Está definida!!
Variables: Scope
Función: Característica de JS. La variable está disponible en todo el cuerpo de la función. No sólo en el bloque donde se define.
function checkscope() {if (true) {
var j = 1;if (true) {
var k = 2;}
}console.log(j); // 1console.log(k); // 2
}checkscope();
Variables: Scope
Variable hoisting: Otra característica de JS. ¡¡Las variables definidas en una función están disponibles antes incluso de ser definidas!!
function checkscope() {console.log(scope); // undefinedvar scope = “local”;console.log(scope); // local
}checkscope();
Objetos
• Tipo de dato fundamental de JS • Colección no ordenada de propiedades
con nombre y valor • Dinámicos
Objetos: El ‘prototype’
• Todas las clases JS tienen un prototype asociado que define sus atributos y métodos.
• Todos los objetos de una clase tendrán todos los atributos y métodos de su prototype y pueden, además, tener otros atributos y métodos propios.
Objetos: Creación
• Literal
var empty = {}; var point = {x:1, y:3}; var book = { title: "The title", author: { name: "author name", surname: "author surname" }};
• Operador ‘new’
var o = new Object();var d = new Date();
Objetos: Propiedades
• Tienen un nombre, un valor y atributos. • Son accesibles con notación de punto (.) o
corchetes ([]) • Se pueden añadir o eliminar en tiempo de
ejecución. • Pueden ser propias o heredadas.
Objetos: Propiedades
• Añadiendo propiedades
var o = {};o.first_prop = “first”;o[“second_prop”] = “second”;
• Accediendo a propiedades
o[“first_prop”]; // firsto.second_prop; // second
Objetos: Propiedades
Borrando propiedades
delete o.first_prop;
¡JS es dinámico!
Propiedades: Ejercicio!• Crear una función que reciba tres parámetros, un objeto, el nombre
de una propiedad y un valor. • La función debe comprobar si el objeto tiene una propiedad con ese
nombre y si la tiene retornar el valor y sino definirla, asignar el valor recibido en el tercer parámetro y retornarlo.
Propiedades: Ejercicio!• Crear una función que reciba tres parámetros, un objeto, el nombre
de una propiedad y un valor. • La función debe comprobar si el objeto tiene una propiedad con ese
nombre y si la tiene retornar el valor y sino definirla, asignar el valor recibido en el tercer parámetro y retornarlo.
function f(obj, property, value){
if (!obj[property]) {obj[property] = value;
}return obj[property];
}
Propiedades: Detección y enumeración
• Operador ‘in’:
var o = { x: 1 };“x” in o; // true“y” in o; // false“toString” in o; // true
• Bucle for/in:
var o = { x: 1, y: 2, z: 3 };for(p in o) {
console.log(p);console.log(o[p]);
}
Propiedades: Ejercicio!Reescribir la función del ejercicio anterior utilizando alguno de los enumeradores vistos.
Propiedades: Ejercicio!Reescribir la función del ejercicio anterior utilizando alguno de los enumeradores vistos.
function f(obj, property, value){
if (!(property in obj)) {obj[property] = value;
}return obj[property];
}
Arrays
• Creación
var empty = [];var fill = [1, 2, 3, 4];var empty2 = new Array();var withSize = new Array(10);var fill2 = new Array(1, 2, 3, 4);
• Pueden contener cualquier tipo de objeto
var misc = [1, “hola”, true, , {}, []];var misc2 = new Array(1, “hola”, true, {}, []);
Arrays
• Crecen dinámicamente
var a = [];a[3] = “hola”;a;
• Son objetos, por tanto admiten propiedades!!
var a = [];a.hola = “hola”;a.hola;
Funciones
• Son tipos primitivos: • Se pueden usar como variables • Se pueden pasar como argumentos a otras funciones • Pueden ser el retorno de otra función • Se les pueden asignar propiedades e incluso invocar
métodos en ellas.
Funciones
Definición // Como expresiónfunction f(x) {
return x + 1;}
// Como sentenciavar f = function(x) {
return x + 1;}
// Anidadafunction hypotenuse(a, b) {
function square(x) { return x * x; };return Math.sqrt(square(a) + square(b));
}
Funciones: Scope léxico
Tienen acceso a todas las variables definidas en el bloque donde son definidas.
function a(x) {var y = “y”;function b() { console.log(x + “ “ + y + “ “ + z); // x y z}var z = “z”;b();
}a(“x”);
Métodos
• Son funciones que son propiedades de algún objeto.
• Se ejecutan en el contexto del objeto.
var o = {m: function() {
console.log(this === o);}
}
Métodos con funciones anidadas
Fuente común de errores debido al contexto (this).
var o = {m: function() {
console.log(this === o); // truefunction f() { console.log(this === o); // false console.log(this); // Es el objeto global!!}f();
}}o.m();
Métodos con funciones anidadas
La solución es el uso del scope léxico
var o = {m: function() {
var self = this;function f() { console.log(self === o); // true}f();
}}o.m();
Funciones: Ejercicio!¿Los argumentos en JS se pasan por valor o por referencia? ¿Y sin son objetos?
Funciones: Ejercicio!¿Los argumentos en JS se pasan por valor o por referencia? ¿Y sin son objetos?
/** Solución*/
function f (value, object) {value ++;object.x ++;
}
var o = { x: 1 };var v = 1;f(v, o);console.log(o);console.log(v);
Funciones: Argumentos
No se comprueba ni el tipo ni el número
function a(x, y) {console.log(“Función ejecutada correctamente”);
}a(1); // Con un argumento menosa(1, 2, 3); // Con un argumento mas
Funciones: Argumentos
Argumentos opcionales = undefined
function saySomething(/*optional*/something) {
something = something || “hello”;console.log(something);
}
saySomething();saySomething(“goodbye”);
Funciones: Argumentos• Argumentos extra: ‘varargs functions’. • JS proporciona a todas las funciones un array ‘arguments’
• Nos permite comprobar si una función se ha invocado con el número correcto de argumentos.
• Nos permite definir funciones que trabajen sobre un número variable de argumentos.
function f(x, y, z) {if (arguments.length != 3) {
throw new Error(“Wrong arguments number”);}// ...
}
Funciones: Ejercicio!• Definir una función que retorne el número máximo de todos los que recibe
como argumentos. • La función no debe definir ningún argumento:
• function max() { ... } • Pista: Podéis usar Number.INFINITY y Number.NEGATIVE_INFINITY
Funciones: Ejercicio!• Definir una función que retorne el número máximo de todos los que recibe
como argumentos. • La función no debe definir ningún argumento:
• function max() { ... } • Pista: Podéis usar Number.INFINITY y Number.NEGATIVE_INFINITY
function max(/* ... */) { var max = Number.NEGATIVE_INFINITY; for (var i = 0 ; i < arguments.length ; ++i) { if (arguments[i] > max) { max = arguments[i] } } return max; } console.log(max()); console.log(max(1, 2, 1000, 3));
Funciones: Propiedades
Las funciones son objetos, por tanto pueden tener propiedades.
function uniqueValue() {return uniqueValue.c ++;
}uniqueValue.c = 1;
console.log(uniqueValue());console.log(uniqueValue());console.log(uniqueValue());
Clases
Conjunto de objetos que heredan propiedades del mismo prototipo.
function range(from, to) {var r = Object.create(range.prototype);r.from = from;r.to = to;return r;
}
range.prototype = {includes: function(x) {
return this.from <= x && x <= this.to;},toString: function() {
return “(“ + this.from + “..” + this.to + “)”;}
};
var r = range(1, 3);console.log(r.includes(2));r.toString();
Clases: Constructores
En JS se define una clase mediante una función constructor y su prototype.
function Range(from, to) {this.from = from;this.to = to;
}
Range.prototype = {includes: function(x) {
return this.from <= x && x <= this.to;},toString: function() {
return “(“ + this.from + “..” + this.to + “)”;}
};
var r = new Range(1, 3);console.log(r.includes(2));r.toString();
Clases: operador ‘new’
• Convierte una función simple en un constructor: • El intérprete creará un nuevo objeto. • Lo convertirá en el contexto actual (this). • Le asignará la propiedad prototype al prototype de la
función • Lo usará como retorno de la función (constructor).
Operador ‘new’: Ejercicio!• ¿Qué ocurriría si, por error, invocamos un constructor sin el
operador new? • ¿Qué objeto se creará? • ¿Qué efecto tendrá el código del constructor? • ¿Cuál será el valor retornado?
Operador ‘new’: Ejercicio!• ¿Qué ocurriría si, por error, invocamos un constructor sin el
operador new? • ¿Qué objeto se creará? • ¿Qué efecto tendrá el código del constructor? • ¿Cuál será el valor retornado?
• No se creará ningún objeto nuevo. • El contexto desde el que se invoque la función sufrirá las
modificaciones resultantes del código del constructor. • El valor de retorno será undefined.
Clases: Herencia
• Heredar en JS significa que el prototipo de la función constructor de la clase hija es un objeto de la clase padre.
• La función constructor de la clase padre tiene como prototype un objeto de su clase padre.
• La función constructor de Object es la única del lenguaje cuyo prototype es null.
• Así se constituye la cadena de prototipos (prototype chain)
Object()
proto = null
Parent()
proto = Object
Child()
proto = Parent
Clases: Herenciafunction Parent() {}
Parent.prototype = { sayHello: function() { console.log("Hello"); }};
function Child() {}
Child.prototype = new Parent(); Child.prototype.sayGoodbye = function() { console.log("Goodbye");};
var child = new Child();child.sayHello();child.sayGoodbye();child.toString();
Object()
proto = null
Parent()
proto = Object
Child()
proto = Parent
Detectando clases• Mediante el prototype: Un objeto es de una clase si su prototype y el de
su constructor son el mismo.
• Los objetos de una clase no tienen propiedad prototype definida. La propiedad prototype es de la función constructora.
// En el ejemplo anterior...var child = new Child();child.prototype; // undefined!!
var child = new Child();Object.getPrototypeOf(child); // Retorna el objeto Parent que asignamos como// prototype a la función Child y extendimos
Detectando clases: Ejercicio!Escribir una función que reciba dos argumentos, un objeto y un constructor e indique si el objeto es de ese tipo o de algún ancestro de ese tipo.
Detectando clases: Ejercicio!Escribir una función que reciba dos argumentos, un objeto y un constructor e indique si el objeto es de ese tipo o de algún ancestro de ese tipo.
function isKindOfClass(object, klazz) { var ret = false; while (!ret && object) { ret = Object.getPrototypeOf(object) === klazz.prototype; object = Object.getPrototypeOf(object); } return ret; }function Test() {}
var t = new Test(); isKindOfClass(t, Test); // true isKindOfClass(t, Object); // true isKindOfClass(t, String); // false
Detectando clases
Operador instanceof: Detecta si un objeto es instancia de una clase o ancestro.
// En el ejemplo anterior...var child = new Child();child instanceOf Parent; // truechild instanceOf Object; // truechild instanceOf String; // false
Detectando clases
Propiedad constructor: Propiedad que tienen todos los prototipos, aunque JS no siempre puede asignarla.
Parent()prototype
Constructor Prototype
constructor
sayHello: ...
Instancias
new Parent()hereda
hereda new Parent()
Detectando clases
JS no puede asignarla automáticamente.
function Parent() {}Parent.prototype = { constructor: Parent, sayHello: function() { console.log("Hello"); }};
JS sí puede asignarla automáticamente.
function Parent() {}Parent.prototype.sayHello = function() { console.log("Hello");};(new Parent()).constructor;
Modificación dinámica de clasesJS permite modificar los objetos prototype de cualquier constructor en tiempo de ejecución y la modificación afectará incluso a los objetos ya existentes.
function Parent() {}
Parent.prototype = {sayHello: function() {
console.log("Hello");}
};
var p = new Parent();
Parent.prototype.sayHi = function() {console.log(“Hi”);
};p.sayHi();
Modificación dinámica de clases: Ejercicio!
• Modificar la clase Number y añadirle un método que nos indique si su valor es par o no.
• Mirad la documentación de Number y su prototype
Modificación dinámica de clases: Ejercicio!
• Modificar la clase Number y añadirle un método que nos indique si su valor es par o no.
• Mirad la documentación de Number y su prototype
/** Solución*/
Number.prototype.even = function() {return this.valueOf() % 2 == 0;
}
var n = 4;n.even();
Métodos estáticos
No existen en JS, pero es tan flexible que podemos simularlos como métodos de la función constructora.
Parent.buildNew = function() {return new Parent();
}
var p = Parent.buildNew();
Programación: Conceptos avanzados
• Scope Chain • Closures • Subclases • Clases Abstractas • Comparación de objetos • Módulos • Strict Mode
Scope Chain: Conceptos
• Scope: Conjunto de líneas de código en las que una variable está definida. Puede ser: – Global: Variables globales, que están definidas en
todo el programa. – Local: Variables definidas en la función que son
declaradas y en las funciones anidadas dentro de esa función.
Scope Chain• JS almacena las variables con scope local para cada fragmento
de código como propiedades de un objeto interno. • El scope chain es una cadena (lista) de objetos que definen las
variables que están en scope. • Cada fragmento de código tiene un scope chain asociado. • Cuando JS necesita buscar una variable recorre el scope chain
de forma ordenada hasta que encuentra algún objeto de la cadena que define la variable o la cadena termina y lanzaría un ReferenceError.
• Cuando una función es invocada, un nuevo objeto se añade al scope chain con las variables locales de la función.
Scope Chain
var a = 1;function f(c) {
var b = 2;// Scope chain 2
}// Scope chain 1f(3);
Ejemplo:
{ a: 1,f: function() }
Scope Chain 1{ a: 1,f: function() }
{ c: 3, b: 2 }
Scope Chain 2
El proceso de resolución de variables sigue la cadena en el sentido de las flechas.
Scope Chain
var a = 1;function f(c) {
var b = 2;function s(d) {
// Scope chain(i)return b + c;
}return s(4);
}for (var i = 0 ; i < 3 ; ++i) {
f(i);}
Ejemplo con funciones anidadas:{ a: 1,f: function(),i: i }
{ c: i,b: 2,s: func }
Scope Chain(i)
{ d: 4 }
Scope chain: Ejercicio!
var a = 1;function f(c) {
var b = 2;function s(d) {
// Scope chain(i, j)return b + c + d;
}return s(j);
}for (var i = 0 ; i < 3 ; ++i) {
for (var j = 0 ; j < 3 ; ++j) {f(i);
}}
• Dibujar el Scope Chain que existirá en el punto del comentario.
• Recordad:
• Que ese Scope Chain dependerá de las variables i y j.
• Que cada vez que una función es invocada, sus funciones anidadas son definidas nuevamente
• Que solo se añade un nuevo objeto al Scope Chain cuando ocurre una invocación de una función, pero el estado del resto de objetos del Scope Chain puede variar en cualquier momento (nuevas variables o cambio de valor de variables ya existentes.)
Scope chain: Solución!
{ a: 1,f: function(),i: i,j: j }
{ c: i,b: 2,s: func }
Scope Chain(i, j)
{ d: j }
var a = 1;function f(c) {
var b = 2;function s(d) {
// Scope chain(i, j)return b + c + d;
}return s(j);
}for (var i = 0 ; i < 3 ; ++i) {
for (var j = 0 ; j < 3 ; ++j) {f(i);
}}
¿Dudas? ¿Preguntas? ¿Repetimos?
Closures
• JS usa Scope Léxico, es decir, las funciones son ejecutadas usando el scope existente cuando fueron definidas, no cuando son invocadas.
• Para implementar Scope Léxico, cada función guarda internamente el código a ejecutar y una copia del scope chain existente cuando se define.
• Objeto función + Scope Chain = Closure
Closures• Mismo Scope Chain
var scope = “global scope”;function checkscope() {
var scope = “local scope”;function f() {
return scope;}return f();
}checkscope(); // “local scope”
• Diferente Scope Chain
var scope = “global scope”;function checkscope() {
var scope = “local scope”;function f() {
return scope;}return f;
}a = checkscope();a(); // “local scope”
Sorprendidos, ¿verdad?
Closures
Mismo Scope Chain
var scope = “global scope”;function checkscope() {
var scope = “local scope”;function f() {
// Scope chainreturn scope;
}return f();
}checkscope(); // “local scope”
{ scope: “global”,checkscope: func}
{ scope: “local”,f: func }
{}
Scope chain
{ scope: “global”,checkscope: func}
{ scope: “local”,f: func }
{}
Scope chain f
Scope chain f()
Closures
Distinto Scope Chain
var scope = “global scope”;function checkscope() {
var scope = “local scope”;function f() {
// Scope chainreturn scope;
}return f;
}var fn = checkscope();fn(); // “local scope”
{ scope: “global”,checkscope: funcfn: func}
{}
Scope chain { scope: “global”,checkscope: func}
{ scope: “local”,f: func }
{}
Scope chain f
Scope chain f()
Closures: Private properties
Un posible uso de las closures es definir propiedades privadas en objetos.
function Test() {var my_private = “private”;this.read_private_var = function() {
return my_private;}
}var t = new Test();t.my_private; // undefinedt.read_private_var(); // “private”
Closures: Ejercicio!
• Definir una clase Counter que tenga únicamente dos métodos públicos y ninguna propiedad: • count(): Devuelve el número de llamadas recibidas a esta
función. • reset(): Pone a 0 el contador
Closures: Ejercicio!
• Definir una clase Counter que tenga únicamente dos métodos públicos y ninguna propiedad: • count(): Devuelve el número de llamadas recibidas a esta
función. • reset(): Pone a 0 el contador function Counter() {
var counter = 0;this.count = function() {
counter ++;return counter;
};this.reset = function() {
counter = 0;};
}
Subclases
• Hasta ahora, cuando necesitamos extender una clase, simplemente encadenamos sus prototipos.
• ¿Qué ocurre cuando necesitamos inicializar el padre?
function Parent() {this.initialized = true;
}
Parent.prototype = {initialized: false,
sayHello: function() {if (this.initialized) {
console.log("Hello");}
}};
function Child() {}
Child.prototype = new Parent(); Child.prototype.sayGoodbye = function() { console.log("Goodbye");};
var c = new Child();child.sayHello();
Subclases: funciones call y apply• call y apply son métodos de las funciones que nos
permiten invocarlas indirectamente, ¡como si fueran métodos de otros objetos!.
• Se diferencian en la forma de recibir los argumentos.
var num = { value: 1 };
function sum(value) {return this.value + value;
}
console.log(sum.apply(num, [1]));console.log(sum.call(num, 2));
Subclases
La solución es ‘aplicar’ el constructor del padre a nuestro contexto.
function Child() {Parent.apply(this, arguments);
}
Subclases: Ejercicio!
En el código de ejemplo, ¿cómo haríais para que la llamada a sayHello en el hijo ejecute también sayHello en el padre?
function Parent() {}
Parent.prototype = { sayHello: function() {
console.log("Hello"); }};
function Child() {}
Child.prototype = new Parent(); Child.prototype.sayHello = function() { console.log(" from child");};
var c = new Child();child.sayHello();
Subclases: Solución!
Igual que invocamos al constructor de la función padre con apply, hacemos lo mismo con la función sayHello, referenciándola a través de su prototype.
Child.prototype.sayHello = function() {Parent.prototype.sayHello.apply(this, arguments);
console.log(" from child");};
Clases Abstractas function Parent() {}
Parent.prototype = { sayHello: function() {
throw new Error(“Abstract method!”); }};
function Child() {}
Child.prototype = new Parent(); Child.prototype.sayHello = function() { console.log("Hello");};
var c = new Child();child.sayHello();
Comparación de objetos• Igualdad (==). True si son…
• …cadenas idénticas • …equivalentes numéricamente • …el mismo objeto • …valores booleanos idénticos • …expresiones de distintos
tipos, pero pueden reducirse a alguno de estos casos anteriores
“abc” == “abc”;
2 + 3 == 5;
var a = {};a == a;
true == !false
new Number(3) == 3
Comparación de objetos• Identidad (===). True si son…
• …cadenas idénticas • …equivalentes numéricamente • …el mismo objeto • …valores booleanos idénticos
“abc” === “abc”;
2 + 3 === 5;
var a = {};a === a;
true === !false
Mismas reglas que en el caso de la igualdad, excepto que no se hace conversión de tipos
Comparación de objetos: Ejercicio!• new Number(3) == new Number(3) // False!• ¿Cómo podemos entonces comparar el valor de dos objetos
Number? Implementar una solución • Pista: En otros lenguajes como Java, todas las clases definen un
método equals() para comparar, en vez de ==.
Comparación de objetos: Ejercicio!• new Number(3) == new Number(3) // False!• ¿Cómo podemos entonces comparar el valor de dos objetos
Number? Implementar una solución • Pista: En otros lenguajes como Java, todas las clases definen un
método equals() para comparar, en vez de ==.
Number.prototype.equals = function(obj) {if (!obj instanceof Number) {
return false;}return this.valueOf() == obj.valueOf();
};(new Number(3)).equals(new Number(3));
Módulos• Necesitamos definir módulos si queremos:
• Mantener el código organizado. • Poder mantenerlo. • Reutilizarlo • ...
• Pero JS no proporciona una forma estándar, por tanto tenemos que diseñar nuestra propia manera de hacerlo.
• Objetivo: Definir un módulo que se incluya en otros programas JS y no altere el entorno. • Minimizar el número de variables globales definidas.
MódulosPaso 1: Utilizar un solo objeto como namespace
/* * Definición */
var collections = {};collections.Set = function() {
//Constructor};collections.Set.prototype ...collections.OrderedMap = function() {
//Constructor}collections.OrderedMap.prototype ...
/* * Uso */
var s = new collections.Set(...);
// pseudo-importvar Set = collections.Set;var s2 = new Set(...);
Módulos
Paso 2: Utilizar funciones como namespace privado
/* * Definición */
var collections = {};(function(){
function Set() {//Constructor
};Set.prototype ...function OrderedMap() {
//Constructor}OrderedMap.prototype ...
var api_private_var;function api_private_func(){};
collections.Set = Set;collections.OrderedMap = OrderedMap;
})();
MódulosPaso 3: Separar cada clase en un archivo
/* * collections/set.js */
if (!collections) {collections = {};
}(function() {
function Set() {//Constructor
};Set.prototype ...var set_private_var;
collections.Set = Set;})();
/* * collections/ordered_map.js */
var collections = collections || {};(function() {
function OrderedMap() {//Constructor
}OrderedMap.prototype ...var orderedMap_private_var;
collections.OrderedMap = OrderedMap;})();
Módulos: Ejercicio!
• Escribir el contador con count() y reset() como componente de un módulo llamado utils. • Nota: El constructor no
debe contener código.
Módulos: Ejercicio!
• Escribir el contador con count() y reset() como componente de un módulo llamado utils. • Nota: El constructor no
debe contener código.
/* * utils/counter.js */var utils;if (!utils) {
utils = {};}(function() {
function Counter() {//Constructor
}var value = 0;Counter.prototype.count = function() {
value ++;return value;
};Counter.prototype.reset = function() {
value = 0;};utils.Counter = Counter;
})();
Modo Strict
• Modo de operación de JS que restringe algunas acciones y lanza más errores • Detecta errores de código comunes y lanza errores. • Prohibe o lanza errores cuando se realizan acciones
potencialmente peligrosas • Deshabilita características no recomendadas.
Modo Strict: Activación
<script type=“text/javascript”>“use strict”;
</script>
function strict_func(){ "use strict"; // ...}
• Para todo el script
• Sólo una función
// Código no strict (function(){ "use strict"; // Código strict...})(); // Código no strict
• Módulo
Modo Strict: Variables y propiedades
var myvar = 1;…myvr = 2; // ReferenceError!
• Prohibe declarar variables sin la palabra reservada var.
var o = { a: 1, a: 2 }; // SyntaxError
• Prohibe declarar dos propiedades con el mismo nombre.
Modo Strict: Funciones
function f() { arguments = ‘abc’; // SyntaxError}
• Prohibe sobreescribir el array arguments
function f(arg, arg) { // SyntaxError}
• Prohibe nombrar dos argumentos de una función iguales
Modo Strict: Más información
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions_and_function_scope/Strict_mode
Preguntas de entrevistas
• ¿Cuál es el motivo de rodear todo el código de un archivo JS en una función?
Preguntas de entrevistas
• ¿Cuál es el motivo de rodear todo el código de un archivo JS en una función?
Mediante esta técnica creamos una closure alrededor del contenido del archivo y un namespace privado controlando así
qué variables/métodos serán visibles desde el exterior y evitando posibles conflictos de nombres.
Preguntas de entrevistas
• ¿Qué es NaN? ¿Cómo podemos saber si un valor es NaN?
Preguntas de entrevistas
• ¿Qué es NaN? ¿Cómo podemos saber si un valor es NaN?
El valor NaN representa un valor no numérico. Éste valor resulta de operaciones que no pueden ser completadas,
normalmente, porque alguno de sus operandos no es numérico (‘abc’ / 2).
Debido a que NaN == NaN es falso, la única forma de comprobar si un valor es NaN es mediante la función isNaN()
Preguntas de entrevistas
• ¿Cuál es el significado de incluir “use strict” en un script? ¿Cuáles son los beneficios de hacerlo?
Preguntas de entrevistas
• ¿Cuál es el significado de incluir “use strict” en un script? ¿Cuáles son los beneficios de hacerlo?
“use strict” es una forma de habilitar modo estricto JS durante un script o una función. Éste modo nos ayuda a evitar errores comunes.
Las ventajas de hacerlo son:• Evitar definir variables globales por error.• Evitar definir dos propiedades con mismo nombre en un objeto.• Evitar nombrar dos argumentos de una función iguales.• Evitar sobreescribir el array arguments de una función.• …
Preguntas de entrevistas
• Escribir una función que retorne un booleano que indique si una cadena es un palíndromo o no.
• Puntos extra por hacerlo en una sola línea (< 80 caracteres)
Preguntas de entrevistas
• Escribir una función que retorne un booleano que indique si una cadena es un palíndromo o no.
• Puntos extra por hacerlo en una sola línea (< 80 caracteres)
function isPalindrome(str) { return str.split('').reverse().join('') === str; }
Preguntas de entrevistas
http://www.toptal.com/javascript/interview-questions#.
Programación: Extensión de contenido
• Detección avanzada de propiedades • Accessor properties • Atributos de las propiedades
Propiedades: Detección• Operador ‘in’: Detecta propiedades propias o heredadas.
var o = { x: 1 };“x” in o; // true“y” in o; // false“toString” in o; // true
• Método hasOwnProperty(): Detecta propiedades propias, no heredadas, sean enumerables o no.
var o = { x: 1 };o.hasOwnProperty(“x”); // trueo.hasOwnProperty(“y”); // falseo.hasOwnProperty(“toString”); // false
Propiedades: Detección
• Método propertyIsEnumerable(): Detecta propiedades propias y enumerables
var o = { x: 1 };o.propertyIsEnumerable(“x”); // trueo.propertyIsEnumerable(“y”); // falseo.propertyIsEnumerable(“toString”); // false
Propiedades: Enumeración• Bucle for/in: Propiedades propias o heredadas enumerables
var o = { x: 1, y: 2, z: 3 };for(p in o) {
console.log(p);console.log(o[p]);
}
• Método Object.keys(objeto): Retorna un Array con las propiedades propias y enumerables
• Método Object.getOwnPropertyNames(objeto): Retorna un Array con las propiedades propias, sean enumerables o no
Propiedades: ComparativaMétodo de Acceso Propias Heredadas Enumerables No enumerables
Acceso simple (. ó []) Sí Sí Sí Sí
Operador in Sí Sí Sí Sí
Método hasOwnProperty()
Sí No Sí Sí
Método propertyIsEnumerable() Sí No Sí No
Bucle for/in Sí Sí Sí No
Object.keys() Sí No Sí No
Object.getOwnPropertyNames()
Sí No Sí Sí
Accessor properties
Consultar o asignar una ‘accessor property’ significa la ejecución del método get/set correspondiente
var o = {_a: 3,get a() {
return this._a;},set a(value) {
this._a = value;}
};o.a = 4;o.a;
Propiedades: Atributos
• Value: El propio valor de la propiedad. • Writable: Determina si es modificable o no. • Get: Función getter • Set: Función setter. • Enumerable: Determina si la propiedad es enumerable o
no. • Configurable: Determina si los estos atributos pueden ser
modificados o no.
Atributos: AplicabilidadAtributo Data Property Accessor property
Value Sí No
Writable Sí No
Get No Sí
Set No Sí
Enumerable Sí Sí
Configurable Sí Sí
Propiedades: Manejando atributos
Método Object.getOwnPropertyDescriptor(): Retorna un objeto que describe los atributos de la propiedad.
/* * Obtener Property Descriptor * para una propiedad */var o = { x: 1, y: 2 };Object.getOwnPropertyDescriptor(o, “x”);
Propiedades: Manejando atributos
Método Object.defineProperty(): Crea o modifica los atributos de una propiedad si ésta es propia y configurable.
var descriptor = { value: 1, writable: true, enumerable: false, configurable: true};var o = {};Object.defineProperty(0, “x”, descriptor);o.x; // 1Object.keys(o); // []
Manejando atributos: Ejercicio!
• Dada esta inicialización, ¿se podría cambiar el valor de o.x? ¿Y el de o1.x? ¿Cómo?
• Pista: ¿Cuál es el valor por defecto del atributo configurable?
var o = {};Object.defineProperty(o, “x”, { value: 1, writable: false });
var o1 = { x: 1 };Object.defineProperty(o1, “x”, { writable: false });
Manejando atributos: Solución• La solución está en el atributo configurable de la propiedad y los valores
por defecto, que dependen del método que usemos para definir la propiedad. • Definición literal: Todos los atributos = true. • Definición usando Object.defineProperty: Todos los atributos no
especificados = false.
Object.defineProperty(o, “x”, { value: 2 }); // Error!! x no es configurable
Object.defineProperty(o1, “x”, { value: 2 }); // OK
#FormaciónEBusiness
GRACIAS. Carlos Alonso Pérez @calonso