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
Este laboratorio pretende introducir autodidácticamente al estudiante en la labor de agregar comportamiento a sus documentos web mediante el lenguaje 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.
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:
.
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>
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.
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>
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');
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?
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>
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);
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.
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:
Infinity
. Valor especial usado en lugar de un número gigante que no cabe en un double
de 64 bits, por ejemplo el resultado de la expresión 17/0
.NaN
. Acrónimo de "not-a-number". Es un valor especial usado cuando una expresión no genera un número válido, como la división 0/0
o tratar de convertir un string no apto a un número como parseInt("cinco")
.Number.MAX_VALUE
. El número más grande representable en un double
de 64 bits.Number.MIN_VALUE
. El número decimal más pequeño (cercano a cero) representable en un double
de 64 bits.Number.NaN
. Igual a NaN
.Number.POSITIVE_INFINITY
. Igual a +Infinity
.Number.NEGATIVE_INFINITY
. Igual a -Infinity
.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.
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.
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');
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
.
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);
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?
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):
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.
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.
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.
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');
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).
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");
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.
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
.