3. 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?
7. Índice de contenidos
• Introducción y entorno de desarrollo
• Conceptos de programación básicos
• Conceptos de programación avanzados
8. 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?
11. 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
18. Estructura léxica
• Juego de caracteres:
Unicode case
sensitive
• Comentarios C
• Literales
• Sentencias
/*
* Literales
*/
12
1.2
“Hello World!”
/*
* Sentencias
*/
var a = 1;
19. 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
20. Estructura léxica
; es opcional!!
/*
* Comportamiento esperado
*/
1 + 2
“hola mundo!”
/*
* Comportamiento inesperado!
*/
var f = 2
1 + f
(2 + 3) * 2
25. undefined y null
• undefined => no inicializado
• null => no hay objeto o valor
26. Booleanos
• true y false
• Tabla de verdad:
– false => undefined, null, 0, -0, NaN y “”
– true => Todo lo demás
27. Detectando tipos
Operador typeof
/*
* Detección de tipos
*/
typeof 2
typeof 2.33
typeof “Hola Mundo!”
typeof undefined
typeof true
typeof null // Es un objeto!!
28. Tipos proporcionados por JS
• Fechas y horas
• El objeto global
• Wrapper objects
• Conversión de tipos (Casting)
29. 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;
30. 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
31. 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
32. 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”);
33. 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;
34. 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’
35. 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
39. Variables: Declaración
/*
* Declaración e inicialización
*/
var i; // Declaración simple
var i, j, k; // Declaración múltiple
var i = 0; // Inicialización simple
var i = 0, j = 1, k = 2; // Inicialización múltiple
var a, b = “hola”; // Declaración + inicialización
40. 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
41. 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.
42. 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!!
43. 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); // 1
console.log(k); // 2
}
checkscope();
44. 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); // undefined
var scope = “local”;
console.log(scope); // local
}
checkscope();
45. Objetos
• Tipo de dato fundamental de JS
• Colección no ordenada de propiedades
con nombre y valor
• Dinámicos
46. 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.
47. 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();
48. 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.
49. Objetos: Propiedades
• Añadiendo propiedades
var o = {};
o.first_prop = “first”;
o[“second_prop”] = “second”;
• Accediendo a propiedades
o[“first_prop”]; // first
o.second_prop; // second
51. 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.
52. 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];
}
53. 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]);
}
55. 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];
}
56. 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, {}, []);
57. Arrays
• Crecen dinámicamente
var a = [];
a[3] = “hola”;
a;
• Son objetos, por tanto
admiten propiedades!!
var a = [];
a.hola = “hola”;
a.hola;
58. 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.
59. Funciones
Definición // Como expresión
function f(x) {
return x + 1;
}
// Como sentencia
var f = function(x) {
return x + 1;
}
// Anidada
function hypotenuse(a, b) {
function square(x) { return x * x; };
return Math.sqrt(square(a) + square(b));
}
60. 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”);
61. 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);
}
}
62. Métodos con funciones anidadas
Fuente común de errores debido al contexto (this).
var o = {
m: function() {
console.log(this === o); // true
function f() {
console.log(this === o); // false
console.log(this); // Es el objeto global!!
}
f();
}
}
o.m();
63. 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();
65. 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);
66. 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 menos
a(1, 2, 3); // Con un argumento mas
68. 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”);
}
// ...
}
69. 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
70. 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));
71. 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());
72. 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();
73. 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();
74. 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).
75. 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?
76. 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.
77. 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
78. Clases: Herencia
function 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
79. 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
80. 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.
81. 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
82. 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; // true
child instanceOf Object; // true
child instanceOf String; // false
83. 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()
84. 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;
85. Modificación dinámica de clases
JS 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();
86. 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
87. 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();
88. 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();
90. 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.
91. 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.
92. Scope Chain
var a = 1;
function f(c) {
var b = 2;
// Scope chain 2
}
// Scope chain 1
f(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.
93. 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 }
94. 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.)
95. 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?
96. 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
97. 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?
100. 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; // undefined
t.read_private_var(); // “private”
101. 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
102. 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;
};
}
103. 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();
104. 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));
105. Subclases
La solución es ‘aplicar’ el constructor del
padre a nuestro contexto.
function Child() {
Parent.apply(this, arguments);
}
106. 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();
107. 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");
};
108. 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();
109. 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
110. 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
111. 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 ==.
112. 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));
113. 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.
114. Módulos
Paso 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-import
var Set = collections.Set;
var s2 = new Set(...);
115. 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;
})();
116. Módulos
Paso 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;
})();
117. 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.
118. 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;
})();
119. 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.
120. 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
121. 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.
122. 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
123. Modo Strict: Más información
https://developer.mozilla.org/en-US/docs/Web/JavaScript/
Reference/Functions_and_function_scope/Strict_mode
124. Preguntas de entrevistas
• ¿Cuál es el motivo de rodear todo el código de un archivo
JS en una función?
125. 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.
127. 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()
128. Preguntas de entrevistas
• ¿Cuál es el significado de incluir “use strict” en un script?
¿Cuáles son los beneficios de hacerlo?
129. 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.
• …
130. 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)
131. 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; }
133. Programación: Extensión de contenido
• Detección avanzada de propiedades
• Accessor properties
• Atributos de las propiedades
134. 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”); // true
o.hasOwnProperty(“y”); // false
o.hasOwnProperty(“toString”); // false
135. Propiedades: Detección
• Método propertyIsEnumerable(): Detecta propiedades
propias y enumerables
var o = { x: 1 };
o.propertyIsEnumerable(“x”); // true
o.propertyIsEnumerable(“y”); // false
o.propertyIsEnumerable(“toString”); // false
136. 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
137. Propiedades: Comparativa
Mé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.getOwnProperty
Names()
Sí No Sí Sí
138. 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;
139. 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.
142. 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; // 1
Object.keys(o); // []
143. 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 });
144. 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