JavaScript para Titanium (II)
Continúo con la serie de artículos de introducción a JavaScript para Titanium. Empezaré a mezclar con algo de código Titanium, pero lo importante en este punto es adquirir una buena base de JavaScript.
Considera estos puntos como un guión de cuestiones en las que tendrás que profundizar, en ningún caso se trata de un manual completo de JavaScript. Si aún no lo has hecho, quizá quieras leer el primer artículo de la serie para asegurarte que lo tienes controlado.
Objetos
Un resumen rápido de los objetos en JavaScript:
Se crea un objeto vacío de JavaScript haciendo
1 | var o = {}; |
Esto es lo mismo que decir
1 | var o = new Object(); |
Esta dualidad en la declaración de algunos tipos se da varias veces en JavaScript. Las siguientes formas de crear un array son exactamente lo mismo y ambos objetos heredan los mismos métodos y propiedades:
1 2 3 | var arr = []; var arr2 = new Array(); |
Y como significan lo mismo, acostúmbrate a utiliza la forma reducida por defecto, facilita la lectura un montón (hay una extraña manía por creer que usando el operador new
el objeto es más “importante”, lo cual es falso e injustificado). En ambos casos heredan todas las propiedades y métodos propias de un Array.
Con los Strings ocurre lo mismo:
1 2 3 4 5 | var str1 = ''; var str2 = "";//comillas simples y dobles en JavaScript son lo mismo var str3 = new String(); //las tres formas son lo mismo |
Pero ojo, porque no ocurre lo mismo con booleanos y variables numéricas
1 2 3 4 5 6 7 | var num = 5; var objNum = new Number(5); //objNumber desciende de Object e implementa métodos que num no tiene var bool = true; var boolObj = new Boolean(true); //como antes, boolObj hereda de Object y es un objeto en toda regla, mientras que bool es un "tipo blando" |
En el ejemplo objNum hereda los métodos propios de Number
como toExponential()
o toPrecission()
, entre otros, mientras que num no tiene métodos ni propiedades, no hereda de Object
.
Números, booleanos, y los tipos especiales null
y undefined
se dice que son “unboxed”, por no ser objetos de primera clase que puedan contener propiedades o métodos. Así, siguiendo con el ejemplo anterior
1 2 3 4 5 6 7 | num.propiedad = 'lo que sea'; //esto falla en silencio, la propiedad no se crea objNum.propiedad = 'lo que sea'; //añadimos una propiedad a nuestro objeto numérico Ti.API.info(num.propiedad); //no existe Ti.API.info(objNum.propiedad); //se imprime en la consola de Titanium |
Volviendo a los objetos, estos pueden tener propiedades y métodos. Podemos declararlos en su creación o posteriormente:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | var o = { nombre:'Javier', edad:33, especialidades:['titanium', 'obj-c', 'preparar mojitos'] }; //o es un objeto declarado por nosotros. Ahora podemos seguir añadiendo propiedades o.apellidos ='Rayon Encinas'; //creamos una propiedad nueva sobre la marcha o.saluda = function(){ alert(this.nombre + ' te saluda'); } o.saluda(); //ejecuta el método saluda |
NOTA: Mucho ojo con el operador this
de JavaScript. Conviene usarlo con conocimiento, ya que tiene un comportamiento diferente al this
de otros lenguajes.
Esto hace muy flexible la declaración de objetos, pero hay que tener cuidado de no tener fallos al escribir los nombres las propiedades, o en vez de asignar un valor a una propiedad, podríamos estar creando una propiedad nueva.
Recuerda que JavaScript es sensible a mayúsculas, por lo que “apellidos” y “Apellidos” serían propiedades distintas.
Un último apunte sobre objetos y propiedades. Al intentar acceder a una propiedad de un objeto que no existe, el objeto devuelve undefined
. Esto es muy práctico para comprobar si la propiedad de un objeto existe sin que se genere una excepción
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | if(o.nombre){ //si nombre existe y no está vacía, será true ... } if(o.nombreCompleto){ //esta propiedad NO existe, devuelve undefined y no entra en el if, pero no da error ... } //ojo, si intentamos acceder a una propiedad de un objeto undefined, lanzará una excepción if(o.nombreCompleto.apellidos) { //nombreCompleto es undefined, al acceder a apellidos lanza una excepción de error } |
Uso de objetos en Titanium
Continuamente usamos objetos como argumento en la creación de objetos de Titanium:
1 2 3 4 5 6 7 | var win = Ti.UI.createWindow({ backgroundColor:'#fff', left:0, right:0, top:0, bottom:0 }); |
El argumento que pasamos en la creación del Window no es más que un objeto JavaScript. Por eso también lo podemos encontrar declarado a parte (muy útil cuando queremos reutilizar estilos)
1 2 3 4 5 6 7 8 9 | var winStyle = { backgroundColor:'#fff', left:0, right:0, top:0, bottom:0 }; var win = Ti.UI.createWindow(winStyle); |
También podemos añadir métodos y propiedades a objetos nativos creados desde Titanium, pero éstos nunca deben empezar con las palabras “get” o “set”. (Esto sólo se aplica a objetos Titanium, los objetos JavaScript no tienen esta limitación).
1 2 3 4 5 6 7 8 9 | var win = Ti.UI.createWindow(); win.saluda = function(){ alert('hola'); } win.saluda(); |
Funciones y objetos
Llegamos a la mejor parte del lenguaje y que más sorprende a los recién llegados a JavaScript. Créeme que acabarás amando y admirando esta característica de JavaScript, pero al principio suena, digamos, raro.
Y es que en JavaScript las funciones son objetos. Al ser objetos significa que las funciones se pueden instanciar, se pueden pasar como argumento, pueden tener métodos, propiedades y cualquier otra cosa propia de los objetos. Resumiendo, una función es un tipo especial de objeto que tiene la capacidad de ser ejecutado, pero un objeto al fin y al cabo.
Las posibilidades son ilimitadas y el uso de funciones para crear clases y objetos es una constante en la programación con JavaScript+Titanium.
Si tienes experiencia en otros frameworks de programación JavaScript, como JQuery, seguramente ya habrás visto cosas como esta
1 2 3 4 5 6 7 | (function (){ function foo(){ } })(); |
Aquí tenemos una función llamada ‘foo’ envuelta por una función anónima, envuelta además por unos paréntesis y termina con otros paréntesis para ejecutar todo el bloque. El bloque se ejecuta tras ser definido, pero no la función foo, que simplemente es definida al ejecutarse la función. ¿La ventaja? Que foo sólo existe dentro de la función anónima, sin contaminar el entorno global (o Global Scope)
Podemos tener funciones anónimas, funciones que se autoejecutan y funciones dentro de funciones. Todas estas características dan una serie de combinaciones tremendamente útiles. Recuerda siempre que las funciones delimitan el ámbito de las variables declaradas en su interior. En el ejemplo, la función foo() no existe fuera de la función anónima. Cuando trabajemos en Titanium esto nos ayudará a que objetos que ya no necesitemos (ventanas, controles, etc.) se liberen de memoria de manera natural cuando no sean necesarios.
De momento quédate con esa idea de que en JavaScript las funciones son objetos de primera clase y que delimitan el ámbito de las variables que son declaradas en su interior.
Veamos algunos patrones de uso de funciones:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | //Declara una función function f () { }; f(); //ejecuta la función //Asigna una función a una variable: var f = function() { }; f(); //también ejecuta la función con igual resultado que el anterior //Asigna el resultado de la función a f: var f = (function(a, b) { return a + b; })(); //la función se autoejecuta y f recibe el resultado de la función, no la función en sí misma |
Un ejemplo muy típico de función anónima y que además se pasa como argumento de otra función la encontramos en los eventListeners:
1 2 3 4 5 6 7 | var btn = Ti.UI.createButton(); btn.addEventListener('click', function(e){ alert('has pulsado el botón en ' + e.x + ', ' + e.y); }); |
La función addEventListener recibe dos argumentos, el primero es el nombre del evento a capturar (en este caso ‘click‘) y el segundo es una función, normalmente llamada “función callback”, que es ejecutada cuando se genera el evento. El parámetro ‘e‘ que recibe la función callback contiene algunos valores del evento. Se llama ‘e‘ porque nosotros lo hemos definido con este nombre, pero puedes llamarlo como quieras, su nombre no es propio del lenguaje. Cada evento genera una serie de valores que están definidos en la API de Titanium.
El mismo eventListener podría definirse así
1 2 3 4 5 6 7 8 9 | var btn = Ti.UI.createButton(); function captureEvent(e){ alert('has pulsado el botón en ' + e.x + ', ' + e.y); } btn.addEventListener('click', captureEvent); |
En este caso, en vez de definir una función anónima en el momento de pasar el argumento, se crea una función que podríamos reutilizar, por ejemplo, en distintos eventListeners (con la propiedad e.source sabríamos quién está disparando el evento)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | var btn1 = Ti.UI.createButton(); var btn2 = Ti.UI.createButton(); function captureEvent(e){ if(e.source === btn1){ alert('has pulsado el botón 1 en ' + e.x + ', ' + e.y); }else if (e.source === btn2){ alert('has pulsado el botón 2 en ' + e.x + ', ' + e.y); } } btn1.addEventListener('click', captureEvent); btn2.addEventListener('click', captureEvent); |
Por último, podemos usar las funciones como Constructores de objetos, buscando un símil con lo que se conoce como Clases en la programación orientada a objetos.
El siguiente ejemplo es una muestra de ello:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | //Declaramos el constructor para nuestro objeto Persona var Persona = function(nombre, edad){ var p = { nombre: nombre, edad: edad }; p.saluda = function(){ alert(nombre + ' te saluda'); } return p; } //creamos una instancia del objeto var persona = new Persona('Javier', 33); persona.saluda(); |
Con esto y un bizcocho, estamos listos para adentrarnos en materia más interesante. En la próxima entrega haré una introducción a commonJS como mecanismo para estructurar nuestra aplicación móvil con Titanium aplicando todo lo visto hasta ahora.
Lecturas recomendadas
Hay dos lecturas muy recomendadas para profundizar. La primera es “JavaScript. The Good Parts” de Douglas Crawford. Todo un clásico que deberías devorar de principio a fin. Contiene lo esencial para hacer un buen uso del lenguaje.
El segundo es una muestra de la gran flexibilidad de JavaScript y lo tremendamente expresivo que puede llegar a ser. Se trata de “JavaScript Patterns” de Stoyan Stefanov.
Como recursos online, los hay a miles. Algunos muy recomendados:
Referencia JavaScript W3CSchool. Una buena referencia del lenguaje
JSPatterns Avanzados patrones de programación JavaScript
wftjs.com El lado más freak y sorprendente del lenguaje.
¿Te interesa Titanium y JavaScript? Sígueme en @jrayon
Ulises Garcia says
Excelentes tus aportes amigo, estoy a la espera de más entregas como ésta, ya te sigo en twitter así que estamos al pendiente.
Chao y Felices códigos!
Alejandro Acuña says
Hola, soy demasiado nuevo en Javascript y mas en Titanium, y quisiera que me ayudara, al querer darle click en un elemento de una tabla, me habra otra ventana, pero no se como llamar el evento.
paso el siguiente codigo
++++creo la tabla y la muestra, los datos de la tabla si lo muetra
var table = Ti.UI.createTableView({
data: tableData
});
+++++ aqui es donde no me hace lo que quiero, al darle clic en una row de mi tabla no me carga una nueva ventana
table.addEventListener(‘click’, function(e){
var seleccion = e.rowData;
var newWindow = Titanium.UI.createWindow({
title: seleccion.title
});
});
win2.add(table);
donde podria estar mal
J. Rayon says
lo único que veo que puede faltar es abrir la nueva ventana, es decir:
newWindow.open();