Universidad de Costa Rica
Escuela de Ciencias de la Computación e Informática
CI-2413 Desarrollo de aplicaciones web
Profesor: Jeisson Hidalgo Céspedes
04-Octubre-2011

Laboratorio 06

Este laboratorio pretende introducir autodidácticamente al estudiante en la labor de agregar comportamiento a sus documentos web mediante el lenguaje JavaScript.

Comportamiento con JavaScript

JavaScript es un lenguaje interpretado por el navegador web que permite al autor manipular dinámicamente el documento, sus estilos y la ventana misma del navegador, para hacer la experiencia del lector más natural y amena.

JavaScript fue creado en 1995 por Brendan Eich cuando trabajaba para Netscape. Un año después Microsoft produjo su propia versión llamada JScript. También en 1996, Netscape sometió JavaScript a la asociación Ecma International para consideración como estándar de la industria, el resultado fue ECMAScript.

ECMAScript es un estándar internacional de un lenguaje genérico de "scripting" para extender la funcionalidad de un programa cualquiera, no sólo navegadores web. Hay un número creciente de implementaciones basadas en ECMAScript que además extienden su funcionalidad, como el inicial JavaScript de Netscape, JScript de Microsoft, ActionScript de Macromedia (adquirida por Adobe), SpiderMonkey y Rhino de Mozilla, etc. Sin embargo, el nombre ECMAScript no tomó popularidad, y cuando la mayoría de la gente dice "JavaScript" está haciendo referencia al lenguaje en forma general, no a la implementación de Netscape, y así se hará en este documento.

JavaScript es un lenguaje genérico que puede utilizar cualquier software que quiera permitir al usuario automatizar tareas propias; como ocurre en la actualidad para programación de dispositivos móviles, acceso a bases de datos orientadas a documentos, animación digital y otros. Pero su uso más difundido ha sido históricamente el web, en la programación en el lado del cliente y más recientemente en el servidor web. En este documento se presentará este lenguaje ligado al navegador web.

Generalidades

JavaScript es un lenguaje similar a C/C++/Java. Es sensitivo a mayúsculas y minúsculas, por lo que resulta más consistente con XHTML que con HTML. Aunque no es obligatorio, cada sentencia en JavaScript debe terminar en punto y coma, y se considera una mala práctica omitirlos. Los comentarios utilizan la notación de C++:

// comentario hasta el final de línea
/* comentario que puede
   extenderse varias líneas */

El código JavaScript puede aparecer en cuatro lugares: en el elemento script, en un archivo .js externo, en un evento intrínseco, y con el pseudoprotocolo javascript:.

Código JavaScript en el elemento script

Se puede escribir código JavaScript en el contenido del elemento script, el cual debe ser hijo directo de head o body. El siguiente ejemplo muestra los cuadrados de los primeros 20 números naturales:

<body>
   <h1>Cuadrados naturales 1</h1>
   <script type="text/javascript">
   <!--
      document.write('<ul>\n');
      for ( var n = 1; n <= 20; ++n )
         document.write('<li>' + n + '<sup>2</sup> = ' + (n * n) + '</li>\n');
      document.write('</ul>\n');
   -->
   </script>
</body>
Un elemento script en el cuerpo del documento. Correr este ejemplo.

Mientras el navegador está cargando un documento web, va mostrando sus elementos (títulos, párrafos, imágenes, tablas, etc.), a medida que los encuentra. Lo mismo pasa con los scripts. Inmediatamente que el navegador encuentra un elemento script, ejecuta su código.

En el ejemplo anterior, se invoca al método write() del objeto document que representa al documento ante JavaScript. La salida del método document.write() es insertada como código (X)HTML inmediatamente después del elemento script que lo invoca. Una vez que el script ha terminado su ejecución, el navegador continúa procesando el resto del documento como de costumbre, y procesará tanto el código insertado con document.write() así como el provisto por el autor en el documento original.

Como se puede deducir del ejemplo anterior, JavaScript interpreta el texto entre apóstrofes ('') como cadenas de caracteres. El operador de suma (+) cuando alguno de sus operandos es una cadena, hace concatenación. Las variables no se declaran precedidas por su tipo de datos, sino por la palabra reservada var. Y el ciclo for tiene la misma sintaxis de C.

Ejercicio 0. ¿Por qué el código JavaScript en el ejemplo anterior se escribió dentro de un comentario (X)HTML? ¿Por qué el navegador ejecuta este código en lugar de ignorarlo como cualquier otro comentario? ¿Qué pasa si se retira este comentario del código?

Ejercicio 1. En un documento aparte, modifique el ejemplo anterior para desplegar en una tabla HTML, las tablas de multiplicar del 1 al 20. Asegúrese de que su documento sea válido ante los estándares.

Ejercicio 2. En el mismo documento, agregue después una tabla HTML con las primeras 10 potencias naturales de los números 1 a 20. Recuerde validar su documento.

Código JavaScript en un archivo externo

El elemento script permite escribir código JavaScript en su contenido, pero el Consorcio Web recomienda sacar el comportamiento del documento web a un recurso reutilizable, por convención, un archivo con extensión .js. Este es el segundo lugar donde se puede escribir código JavaScript. El mismo elemento script permite hacer la inclusión del archivo externo con el atributo src. El siguiente ejemplo tiene el mismo efecto que el ejemplo anterior:

<body>
   <h1>Cuadrados naturales 2</h1>
   <script type="text/javascript" src="squares_list2.js"></script>
</body>
El código JavaScript puede estar en un recurso externo y ser importado con el atributo src del elemento script. Correr este ejemplo.
document.write('<ul>\n');
for ( var n = 1; n <= 20; ++n )
   document.write('<li>' + n + '<sup>2</sup> = ' + (n * n) + '</li>\n');
document.write('</ul>\n');
El archivo JavaScript squares_list2.js referido en el ejemplo anterior.

La especificación (X)HTML dice que si un elemento script tiene tanto código JavaScript en el contenido como un archivo referido en el atributo src, el contenido será ignorado por completo en favor del archivo externo.

Ejercicio 3. Extraiga el código JavaScript que escribió en los ejercicios anteriores a uno o varios archivos .js externos.

Ejercicio 4. Escriba algunas instrucciones JavaScript en el contenido de un elemento script que también referencia un archivo externo con el atributo src. Verifique cuáles instrucciones son ejecutadas.

Ejercicio 5. Al retirar el código JavaScript del contenido del elemento script para moverlo a un archivo externo, el elemento script queda vacío, y según el estándar XML es válido emplear una etiqueta vacía de la forma <script src="path/to/file.js"/>. Pruebe esto en su documento web. Valídelo contra el estándar. ¿Cómo reacciona el navegador ante este cambio?

Código JavaScript en los eventos intrínsecos

El tercer lugar donde se puede especificar código JavaScript es en los eventos intrínsecos de ciertos elementos, tales como onload, onmouseover y onclick. Por ejemplo:

<body>
   <h1>Eventos en JavaScript</h1>
   <button onclick="alert('No me toque');">Un botón!</button>
</body>
El autor puede proveer código JavaScript se que ejecuta cuando un evento ha ocurrido, como presionar un botón en este caso. Correr este ejemplo.

Código JavaScript en el pseudoprotocolo javascript:

El cuarto lugar donde se puede ejecutar código JavaScript es en la barra de direcciones del navegador, con el pseudoprotocolo javascript:, que es seguido por una o varias instrucciones JavaScript separadas por punto y coma. El resultado de estas instrucciones, si lo hay, se toma como un string, y es desplegado en la ventana del navegador. Ejemplos:

javascript:5%2
javascript:x = 3; (x < 5) ? 'x is less' : 'x is greater'
javascript:d = new Date(); typeof d
javascript:for(i=0,j=1,k=0,fib=1; i>5; i++,fib=j+k,k=j,j=fib) alert(fib);
javascript:s=''; for(i in navigator) s+=i+':'+navigator[i]+'\n'; alert(s);

Tipos de datos y variables

JavaScript define siete tipos de datos: booleanos, números, cadenas de caracteres (strings), funciones, objetos, arreglos, y valores especiales. En las siguientes secciones se explorará cada uno de ellos.

Números

JavaScript no hace diferencia entre números enteros y de punto flotante. Todos son representados internamente como punto flotante de 64bits (IEEE 754). Las constantes numéricas de JavaScript siguen las mismas convenciones de C, excepto los números octales que no son parte de ECMAScript.

Cuando un valor flotante llega a ser más grande que el más grande de los representables, se almacena con el valor especial Infinity o su opuesto negativo. Cuando se hace una operación indefinida, se genera un número especial NaN (not-a-number), el cual nunca es igual a nada, incluso ni a él mismo, por eso debe usarse la función especial isNaN(). Otra función práctica es isFinite(), que prueba si un número no es Infinite y no es NaN. A continuación una lista de constantes numéricas especiales:

Ejercicio 6. Escriba un script que genere una tabla con las siguientes columnas: el nombre de la constante, el valor real de dicha constante, el valor que precede a la constante, el valor sucesor de la constante, el resultado de invocar isNaN() con la constante, y el resultado de invocar isFinite() con la constante. Llene las filas de la tabla con cada una de las constantes listadas anteriormente.

Ejercicio 7. Agregue dos filas más a la tabla del ejercicio 6. La primera con una expresión aritmética que genera un valor real, y la segunda que genere un valor indefinido.

Cadenas de caracteres (strings)

Las cadenas literales en JavaScript se encierran entre comillas dobles o simples y se pueden anidar éstos. Ya que es común escribir código HTML dentro de JavaScript y viceversa, es conveniente uniformar las comillas para cada cual. Por ejemplo:

<a href="" onclick="alert('You\'re welcome')">Thanks</a>

A diferencia de otros lenguajes de programación, Las cadenas literales tienen que ser escritas en una única línea, no pueden romperse en dos o más de ellas.

Igual que en C/C++, JavaScript emplea secuencias de escape iniciadas en backslash (\) para representar caracteres especiales, tales como \n para el cambio de línea, \r para el retorno de carro, \" para comillas dobles, \' para apóstrofe o comilla simple, \0 para el carácter nulo y \\ para la barra invertida (backslash).

No se debe utilizar el backslash frente a un cambio de línea para tratar de continuar un string en varias líneas; JavaScript probablemente lo ignorará. En JavaScript las cadenas de caracteres o strings son un tipo de datos atómico, no un arreglo de caracteres. De hecho JavaScript no tiene el concepto de carácter (char) como sí ocurre en otros lenguajes de programación. Un carácter se representa como un string de longitud 1. El método str.charAt(i) permite obtener un string con el carácter que está en la posición i de str, donde el índice i está basado en cero, es decir, 0 representa el primer carácter de la cadena. El siguiente código muestra varias operaciones con strings:

var str = 'Welcome ' + visitorName; // concatenación
var len = str.length; // longitud de cadena
var lastChar = str.charAt(str.length - 1); // obtener un caracter de la cadena
var sub = str.substring(3, 4); // obtiene 'come'
var i = str.indexOf('e'); // Encuentra la posición de la primera letra 'e' en str

Aunque algunas implementaciones permiten el uso del operador [] para acceder a un carácter específico del string, es recomendable evitar su uso, ya que no forma parte del estándar ECMAScript.

Ejercicio 8. Escriba un poema de su agrado (o cualquier otro texto con varios párrafos) en una variable string de JavaScript. Imprima las estrofas (no las líneas que lo componen) en orden inverso en el documento.

Ejercicio 9. Para hacer notorio al lector que las estrofas están en orden inverso, imprima el número de la estrofa y con estilos CSS haga a este número visiblemente grande y ubicado a la izquierda o derecha de la estrofa.

Conversiones entre números y strings

Cuando un número aparece en un contexto donde se requiere un string, JavaScript lo convierte automáticamente. Por ejemplo, si uno de los operandos de + es una cadena y el otro es un número, el operador + actuará como el operador de concatenación y convertirá el número en una cadena.

var hdd_gb = 500; // 500 GB
var hdd_gib = 500 * Math.pow(10, 9) / Math.pow(2, 30); // 465.661287 GiB
var text = 'Un disco duro de ' + hdd_gb + 'GB equivale a ' + hdd_gib + 'GiB';
document.write('<p>', text, '</p>\n');
El operador de concatenación convierte números a cadenas automáticamente. Correr este ejemplo.

Las conversiones explícitas se pueden hacer con el constructor String(numero), y varios métodos de la clase Number: numero.toString(base), numero.toFixed(decimales), numero.toExponential(decimales) y numero.toPrecision(decimales). Ejemplos:

hdd_gib + "";              // "465.66128730773926"
String(hdd_gib);           // "465.66128730773926"

hdd_gib.toString();        // "465.66128730773926"
hdd_gib.toString(2);       // "111010001.1010100101001010001"
hdd_gib.toString(16);      // "1d1.a94a2"

hdd_gib.toFixed(0);        // "466"
hdd_gib.toFixed(2);        // "465.66"

hdd_gib.toExponential(0);  // "5e+2"
hdd_gib.toExponential(3);  // "4.657e+2"

hdd_gib.toPrecision(4);    // "465.7"
hdd_gib.toPrecision(7);    // "465.6613"

Cuando un string se utiliza en un contexto donde se requiere un número, será traducido automáticamente por JavaScript, por ejemplo:

var product = "21" * "2";  // product == 42.

Con la notación anterior no se podrá sumar un string a un número con el operador +, ya que será interpretado como concatenación. El constructor Number(str) convierte el string str en un número, siempre que str tenga formato de número en base 10 y no inicie con espacios en blanco. Las funciones parseInt(str,base) y parseFloat(str,base) asumen que str está en base base (10, si se omite) y lo convierten en un número entero o real respectivamente. Ejemplos:

parseInt("3 blind mice");  // Returns 3
parseFloat("3.14 meters"); // Returns 3.14
parseInt("12.34");         // Returns 12
parseInt("0xFF");          // Returns 255

parseInt("11", 2);         // Returns 3 (1*2 + 1)
parseInt("ff", 16);        // Returns 255 (15*16 + 15)
parseInt("zz", 36);        // Returns 1295 (35*36 + 35)
parseInt("077", 8);        // Returns 63 (7*8 + 7)
parseInt("077", 10);       // Returns 77 (7*10 + 7)

parseInt("eleven");        // Returns NaN
parseFloat("$72.47");      // Returns NaN

Si la cadena a convertir inicia con "0x" se interpreta que está en hexadecimal. Si inicia con 0 su resultado es indefinido, ya que algunas implementaciones podría interpretar octal o decimal, por lo que es conveniente siempre especificar la base. Si no se puede convertir a un número, estas funciones retornan NaN.

Booleanos

Los valores booleanos sólo pueden contener los valores literales false o true. Cuando se usa un booleano en un contexto numérico, se convierten automáticamente a los valores 0 y 1. Si se usan en un contexto string, JavaScript los convierte automáticamente a "false" y "true" respectivamente. El recíproco también es válido. Los valores especiales NaN, null, undefined y la cadena vacía ("") siempre se convierten a false; todos los demás a true (como Infinity). Para hacer explícita la conversión, es recomendable emplear la función Boolean():

var x_as_boolean = Boolean(x);

Funciones

El programador puede declarar sus propias funciones con la palabra reservada function, el nombre opcional de la función, los parámetros sin tipos entre paréntesis ( ), y el cuerpo de la función entre llaves { }. Las funciones en JavaScript son un tipo de datos más, por ende, una función es un valor; así, las funciones se pueden almacenar en variables, miembros de objetos, arreglos, y pasarse por parámetros en una forma más natural que en C/C++. Cuando una función se asigna a un objeto como un miembro, recibe el nombre especial de método. En caso de asignarse a una variable, puede omitirse el nombre de la función, lo que en JavaScript se llama función literal o función lambda en homenaje al lenguaje Lisp que fue uno de los primeros en permitir funciones sin nombre:

// una función nombrada
function square1(x) { return x*x; }

// una función literal o "lambda"
var square2 = function(x) { return x*x; }

// una función construida a partir de strings
var square3 = new Function("x", "return x*x;");

// invoca la función a través de la variable square2
square2(2.5);

Una tercera forma de definir una función es pasar sus argumentos y cuerpo como strings a la función constructora Function(), lo cual es poco usado e incluso, menos eficiente. Cuando el valor de una variable es una función, se puede invocar ésta usando el operador () tras el nombre de la variable, como se hizo en el ejemplo anterior con square2.

Ejercicio 10. Programe la función factorial recursivamente. Imprima en una lista no ordenada el factorial de los primeros 20 naturales. ¿Qué sucede si llama su función con un número muy grande o con parámetro no numérico?

Objetos

Un objeto es una colección de valores nombrados, que usualmente se les refiere como propiedades, campos o miembros del objeto, y se les accede utilizando el operador punto. Por ejemplo:

image.width
image.height
document.myform.button
document.write("write es un método: una propiedad cuyo tipo de datos es una función");

El operador punto permite acceder a las propiedades utilizando identificadores. Pero JavaScript permite también usar cadenas de caracteres para acceder a las propiedades, con el operador corchetes []. Esta segunda notación permite ver a los objetos como arreglos asociativos. Ejemplos:

image["width"]
image["height"]
document["myform"]["button"]
document["write"]("write es un método: una propiedad cuyo tipo de datos es una función");

Los objetos se crean llamando funciones constructoras con el operador new, después de lo cual se usan como de costumbre. Estos objetos son almacenados en memoria dinámica por el navegador, el cual incorpora un recolector de basura (garbage collector), de tal forma que ahorra al programador la responsabilidad de liberar la memoria de cada objeto creado.

var now = new Date();
var pattern = new RegExp("\\sjava\\s", "i");

var point = new Object();
point.x = 2.3;
point.y = -1.2;

En el ejemplo anterior el objeto point se creó como un objeto vacío, y sus propiedades se fueron agregando luego con el operador de asignación. Existe una notación para definir objetos literales, útil para inicializaciones:

{ property1: value1; "property2": value2; ...; propertyN: valueN }

Las llaves {} en JavaScript indican la creación de un objeto literal. Los nombres de las propiedades pueden declararse como identificadores o como strings. Los valores de cada propiedad pueden ser de cualquier tipo de datos de JavaScript (booleano, numérico, string, función, arreglo, u objeto), sea como valores literales o como resultado de una expresión aritmética. Así los objetos se pueden anidar como es de esperar:

var point = { x:2.3, y:-1.2 };

var activo1 =
{
   "tipo": "disco_duro",
   "precio": 30000, // colones
   'tamanno': 500 * Math.pow(10, 9) / Math.pow(2, 30) // GiB
};

var rectangle =
{
   color : "#a4f0ca",

   background:
   {
      color: "lightgray",
      image: "img/bricks.png",
      repeat: "repeat"
   },

   geometry:
   {
      topLeft: { x: 45, y: 10 },
      extend: { width: 21.93, height: 38.34 }
   }
};

Si un objeto se emplea en un contexto Boolean, se traduce a true si no es null. Si el contexto es string se llamará al metodo toString() y si es numérico a valueOf().

Ejercicio 11. Escriba un objeto Círculo, Triángulo y Rectángulo. En cada uno de ellos almacene su posición y dimensiones. Provea dos métodos en cada objeto, uno para calcular el perímetro y otro para el área. Haga un programa en JavaScript que imprima cada figura: su tipo, posición, dimensiones, perímetro y área. Nota: si A, B y C son los vértices de un triángulo, su área y perímetro se pueden obtener mediante (requiere Firefox para visualizar las fórmulas):

área = Ax ( By Cy ) + Bx ( Cy Ay ) + Cx ( Ay By ) 2

perímetro = ( Ax Bx ) 2 + ( Ay By ) 2 + ( Ax Cx ) 2 + ( Ay Cy ) 2 + ( Bx Cx ) 2 + ( By Cy ) 2

Ejercicio 12. Agregue otro triángulo a la colección de figuras hechas en el ejercicio anterior. ¿Puede reutilizar el código de los métodos del primer triángulo? Haga que su nuevo triángulo aparezca en la salida del programa. Sugerencia: puede verificar el área de sus triángulos contra esta aplicación.

Arreglos

En JavaScript un arreglo es una colección de datos enumerados. Se acceden con un índice entero, basado en 0, escrito entre corchetes tras el nombre del arreglo. El siguiente ejemplo obtiene el ancho en pixeles de la segunda imagen en el documento:

document.images[1].width

Los arreglos pueden tener datos heterogéneos. Así un elemento del arreglo puede contener otro arreglo, pero no hay arreglos multidimensionales. Los arreglos se crean con el constructor Array() y sus elementos se pueden agregar simplemente asignándolos a sus índices o bien, como parámetros del constructor Array(); pero si se pasa un único entero a este constructor, de la forma Array(N) se creará un arreglo con N elementos indefinidos. Ejemplos:

var a = new Array();                // Arreglo vacío, lo mismo que: var a = [];
a[0] = 1.2;                         // Un elemento es insertado en la posición 0
a[1] = "JavaScript";
a[2] = true;
a[4] = { x:1, y:3 };                // La posición 3 tiene un elemento con el valor undefined
a[5] = function(x) { return x*x; }; // a[5](7) retornará 49
a[6] = new Array();                 // Elemento a[6] almacena un arreglo vacío
a[6][0] = -Infinity;                // Inserta un elemento en el arreglo que está en a[6]

var b = new Array(1.2, "JavaScript", true, { x:1, y:3 }); // 4 elementos

var c = new Array(10); // Arrreglo de 10 elementos indefinidos

JavaScript permite crear arreglos literales, preferiblemente utilizados para inicialización, y son una lista de valores separados por comas dentro de corchetes, que se asignan secuencialmente empezando en 0. Los elementos también pueden ser indefinidos lo cual se logra omitiendo el valor entre comas:

var b = [ 1.2, "JavaScript", true, { x:1, y:3 } ];

var matrix = [[1,2,3], [4,5,6], [7,8,9]];           // matrix[2][1] == 8

var base = 1024;
var table = [base, base+1, base+2, base+3];

var sparseArray = [1,,,,5];

Ejercicio 13. Almacene las figuras que haya creado en los ejercicios 11 y 12 en un arreglo. Con un ciclo imprima cada figura, su perímetro y su área.

Ejercicio 14. Escriba una función que crea y retorna una figura geométrica aleatoriamente cada vez que se invoca. Llene un arreglo de 20 figuras aleatorias e imprímalas como hizo en el ejercicio anterior. Puede apoyarse en los métodos Math.random() y Math.floor().

Ejercicio 15. Escriba un inventario compuesto de una jerarquía de activos en JavaScript. Programe métodos para imprimir cada activo e imprima el inventario en un documento XHTML. Haga que cada activo genere un div en el documento, de tal forma que los div mantengan el mismo anidamiento que los objetos. Con estilos CSS gregue bordes a los div para que sea visualmente clara la relación entre estos.

Valores especiales

El valor especial null se utiliza para indicar que un objeto no tiene valor. El valor especial undefined indica que una variable u objeto nunca ha sido declarado o nunca se le ha asignado un valor.

Objetos especiales

JavaScript provee varios objetos útiles: Date, RegExp, Boolean, Number y String. El objeto Date sirve para obtener y manipular fechas u horas, como se aprecia en el siguiente ejemplo.

var text = '';
var now = new Date();

// Los meses inician en 0. Diciembre es el 11
if ( now.getMonth() == 11 && now.getDate() == 25 )
{
   text = 'Hoy es navidad!';
}
else
{
   // Fijar en un objeto Date la proxima navidad
   var christmas = new Date( now.getFullYear(), 11, 25, 00, 00, 000 );
   if ( now.getMonth() == 11 && now.getDate() > 25 )
   {
      christmas.setFullYear( now.getFullYear() + 1 );
      text = 'la próxima ';
   }

   // Convertir la cantidad de milisegundos que faltan para navidad en dias
   var dias = (christmas.getTime() - now.getTime()) / (1000 * 60 * 60 * 24);
   text = 'Faltan ' + dias.toFixed(0) + ' días para ' + text + 'navidad';
}

document.write('<p>', text, '</p>\n');
Ejemplo de uso del objeto Date. Correr este ejemplo.

El objeto especial RegExp permite manipular expresiones regulares en JavaScript, siguiendo la misma notación de Perl.

Ningún valor primitivo en JavaScript tiene métodos, sin embargo, en algunos ejemplos anteriores, se han invocado algunos a través de valores primitivos. Esto es posible ya que JavaScript define tres clases correspondientes a cada uno de los tipos de datos primitivos, llamadas wrapper objects: Number, String y Boolean. Cuando a un valor primitivo se le invoca un método, JavaScript automáticamente construye un objeto temporal de la clase correspondiente y lo inicializa con el dato primitivo y luego invoca el método. Es decir, cuando se escribe algo como

var s = "Hola mundo!";
var len = s.length;

La propiedad length no proviene del string s, sino de un objeto temporal String inicializado con s, una copia, que después de usarse, se desecha. Este comportamiento también aplica para los datos primitivos de Number y Boolean.

Ejercicio 16. Calcule el tiempo que tarda en ejecutarse todos los scripts en conjunto, que haya programado en este laboratorio. Presente el resultado al usuario, en milisegundos o segundos (si se tarda más de uno).

Por valor o por referencia

En JavaScript los datos se manipulan por valor y por referencia. Automáticamente JavaScript sigue esta regla: valores primitivos (booleanos y números) son manipulados por valor; los objetos, arreglos y funciones, por referencia. No existe una notación especial para que el programador pueda decidir cuáles manipular por referencia y cuales por valor. Debe ajustarse a las reglas expuestas y tener los cuidados respectivos.

La manipulación tiene tres escenarios: cuando se copia un dato, cuando se pasa por parámetro a una función y cuando se compara. Cuando se invoca a una función, las copias y referencias simplemente se harán de acuerdo al tipo de dato enviado. Por ejemplo:

var n = 1;   // Es un número, el literal 1 se copia por valor a n
var t = n;   // t será un número, se copia también por valor

// Los parametros de la funcion no tienen tipo de datos, reciben por valor o referencia
// de acuerdo a los datos enviados en la invocacion
function addto(total, value) { total += value; }

// Se invoca con dos numeros, al llamar a addto, total y value tendrán copias por valor
// así que la funcion no tendra el efecto que quiere conseguir, y por ende, t se mantendra
// intalterado con su valor 1
addto(t, n);

// La comparacion se hace por valor, es decir, se comparan byte a byte variables distintas
if ( t == n ) document.write('Los números son copiados');   // Se evaluará como true

Cuando los datos se manipulan por referencia, se hace una copia de la dirección del objeto. Es decir, se copian, pasan por parámetro y comparan direcciones que apuntan a lo mismo, de forma similar a los punteros C. Por ejemplo:

// Crea un objeto y su direccion la copia a hoy
var hoy = new Date();

// Copia la direccion de hoy a navidad, ambas variables refieren al mismo objeto
var navidad = hoy;

// Modifica al objeto apuntado, por ende, 'hoy' tambien referirá al 25 de diciembre
navidad.setMonth(11);
navidad.setDate(25);

// Si se comparan, se hará una comparación de direcciones, y se evaluará como true
if ( hoy == navidad ) document.write('Hoy es navidad!');

// se crea otro objeto distinto pero con iguales datos
var navidad2 = new Date(navidad.getFullYear(), 11, 25);

// Esta comparacion evaluara como falsa, ya que tienen direcciones distintas
if ( navidad == navidad2 ) document.write('La navidad es universal!');


// Una funcion recibira por valor o referencia dependiendo de como se le invoque
function addDays(date, days)
{
   date.setDate( date.getDate() + days );
}

// En esta invocacion, date recibira una copia de direccion de navidad y days una copia del
// literal 6 logrando el efecto deseado para navidad, pero 'hoy' se vera afectado tambien.
addDays(navidad, 6);

// Esta funcion no provoca el efecto deseado pero ilustra como trabajan las direcciones
function addDaysB(dateB, daysB)
{
   var tmpDate = new Date(dateB);
   tmpDate.setDate( tmpDate.getDate() + daysB );
   dateB = tmpDate; // Esto solo modifica la direccion de dateB y no al objeto real
}

// Al hacer esta invocacion la funcion addDaysB crea otro objeto internamente y lo asigna a su
// parametro temporal dateB y no a navidad2. Al salir de la función, navidad2 permanece
// inalterada. Esto tambié explica cómo actuar cuando no se quiere alterar datos que son pasados
// por referencia: haciendo copias
addDaysB(navidad2, 6);

Los strings no caben en ninguna de esas dos categorías. Una vez creado un string, JavaScript no permite modificarlo, es decir, son inmutables. Asi que no tiene importancia saber si se pasa por copia o por referencia. Puede pensarse lo segundo por razones de eficiencia. Lo único que se puede averiguar es si se comparan por valor o por referencia, de esta forma:

// Determining whether strings are compared by value or reference is easy.
// We compare two clearly distinct strings that happen to contain the same
// characters. If they are compared by value they will be equal, but if they
// are compared by reference, they will not be equal:
var s1 = "hello";
var s2 = "hell" + "o";
document.write(s1 == s2 ? "Strings compared by value" : "Strings compared by reference");

Variables

Una de las primeras diferencias con otros lenguajes como C es que las variables de JavaScript no tienen un tipo de datos, por lo que es perfectamente válido asignarles un valor de un tipo y luego otro de diferente naturaleza:

var i = 16;
i = "dieciséis";

Antes de que usted pueda usar una variable, tiene que haberla declarado con la palabra reservada var, o JavaScript declarará una implícitamente por usted. Lo cual puede traer efectos secundarios. En la declaración se deben inicializar las variables, sino JavaScript lo hará con el valor undefined.

Si una variable se declara dos veces en secciones var distintas y la segunda declaración tiene inicialización actúa como una simple asignación. Todas las variables declaradas en una sección var son permanentes, es decir, no se pueden eliminar con el operador delete. La primera sección de la cláusula for o for/in, permite también declarar variables.

for(var i = 0; i < 10; ++i) document.write(i, '\n');
for(var i in obj) document.write(i, '\n');

Cuando se trata de leer una variable que no existe, se genera un error y el navegador detiene la ejecución del script. Si se le trata de asignar algo a una variable inexistente, se creará una variable global implícita. Las variables globales son visibles en cualquier lugar donde haya código JavaScript.

Las variables locales son aquellas creadas en secciones var en los cuerpos de las funciones, incluyendo a los parámetros. Las variables locales se superponen a las globales. A diferencia de C y Java, JavaScript no tiene diferente alcance (scope) en cada bloque. Mejor dicho, los bloques de JavaScript lo definen las mismas funciones y no las parejas { }, así todas las variables de una función comparten el mismo alcance, indiferentemente del nivel de anidamiento de llaves en que se declaren las variables. En el siguiente ejemplo; las variables o, i, j, k comparten el mismo scope.

function test(obj)
{
   var i = 0;                        // i is defined throughout function
   if (typeof obj == "object")
   {
      var j = 0;                     // j is defined everywhere, not just block
      for(var k=0; k < 10; ++k)   // k is defined everywhere, not just loop
      {
         document.write(k);
      }
      document.write(k);             // k is still defined: prints 10
   }
   document.write(j);                // j is defined, but may not be initialized
}

Las variables locales ocultan las globales, y las locales tienen alcance (scope) de toda la función. Esto puede generar resultados confusos o sorprendentes. Por ejemplo:

var str = "global";
function f( )
{
   alert(str);         // Displays "undefined", not "global"
   var str = "local";  // Variable initialized here, but defined everywhere
   alert(str);         // Displays "local"
}
f( );

En la función anterior no se imprime "global" en el primer alert de la línea 4, porque se está usando la variable str local de la línea 5, la cual no ha sido inicializada aún. Este comportamiento equivale al siguiente código:

var str = "global";
function f( )
{
   var str;
   alert(str);         // Displays "undefined", not "global"
   str = "local";      // Variable initialized here, but defined everywhere
   alert(str);         // Displays "local"
}
f( );

La moraleja es siempre declare sus variables juntas al inicio de una función JavaScript. Se puede distinguir dos tipos de variables: undefined y unassigned. Una variable sería indefinida si nunca se ha declarado. Al tratar de leerla se generará un error y el navegador detiene la ejecución del script. Una variable es unassigned si está declarada pero no se le ha asignado un valor aún. Al tratar de leerla, se evaluará con el valor especial undefined (posiblemente mejor hubiera sido llamado unassigned).

var x;     // Declare an unassigned variable. Its value is undefined.
alert(u);  // Using an undeclared variable causes an error.
j = 3;     // Assigning a value to an undeclared variable creates the variable.

Cuando se inicia un intérprete de JavaScript, lo primero que hace antes de ejecutar el código fuente, es crear el global object, el cual se encarga de almacenar todas las variables globales que se declaren en el código. Cuando usted declara una variable global, realmente está declararando una propiedad del global object.

El intérprete de JavaScript también define convenientemente otras propiedades (variables y métodos) en el global object que son de uso común, como Infinity, parseInt(), y Math. La palabra reservada this usada en un contexto global (fuera del cuerpo de una función), referencia específicamente al global object. En el caso del navegador, el global object es además la ventana del navegador, y se cumple:

this == this.window == window

Si las variables globales son propiedades de un global object ¿qué son las variables locales? También son propiedades de un objeto temporal llamado call object, el cual se construye en cada invocación de una función, y mantiene juntos los parámetros, variables declaradas en el cuerpo de la función y el código de la misma. Este objeto es el que impide que las variables locales sobrescriban las globales cuando tienen el mismo nombre. Dentro de una función, this hace referencia al call object en lugar del global object.

Evaluación

Comprima los archivos XHTML, CSS y JavaScript que haya creado durante el laboratorio. Súbalos a la plataforma educativa (Moodle) de la ECCI, en la asignación con título Tarea08.