2011/05/28

Bonito efecto CSS3

Algo ligero para hoy después de dos días "rompiendo" las normas. Un bonito efecto CSS3 que he encontrado por ahí útil para un título de web por ejemplo. Una capturita de cómo deberíais verlo.



Por descontado, os hará falta un navegador "de verdad" para verlo en su máximo esplendor.

Más detalles en el fiddle...

2011/05/27

Sesiones PHP a lo campestre II. Seamos civilizados

En contra de lo que el título puede indicar, a priori vamos a ver el método "correcto" de almacenar la información de nuestra aplicación en el pseudo-array $_SESSION de PHP.

A la hora de almacenar los datos, siempre podemos usar el método que aparece en todos los sitios, incluida la documentación de http://php.net/ donde de forma simplista dice
"Para almacenar una variable en la sesión, haz $_SESSION['variable'] = $valor;"
Muy bonito y sencillo, pero las cosas no se pueden reducir tanto. Imagináos el escenario de que en un mismo servidor hay varias aplicaciones web con PHP funcionando. Inicialmente creamos una sesión en una aplicación que alegremente nos mete en nuestra sesión un valor 'id' con valor 3 mediante $_SESSION['id'] = 3;

Hasta aquí, bien, todo funciona. Ahora abrimos una segunda aplicación dentro del mismo servidor que usa esa misma variable de sesión, pero ahora con valor 'nulo' mediante $_SESSION['id'] = 'nulo';

Volvemos a la primera aplicación y cuando ésta pregunta por el valor de 'id' en la sesión NO RECIBE el valor 3 que es lo que espera, si no 'nulo'. Ea!, ahora vuélvete loco depurando o directamente vete a por una pistola para pegarte un tiro, porque no lo vas a encontrar...

Esto se arregla usando la misma técnica que se usa en javascript para optimizar el ámbito de las variables. Lo que hay que hacer es crear un ámbito de cada aplicación dentro de la sesión. Para ello, en vez de asignar un valor directamente a $_SESSION, vamos a crear una nueva varible dentro PERO DE TIPO ARRAY con nombre, siendo originales, el de nuestra aplicación. Así, todo quedaría así:
// Inicia la sesión
session_start();
// Crea el contexto
if(!isset($_SESSION['aplicacion1'])) $_SESSION['aplicacion1'] = array(); 

$_SESSION['aplicacion1']['id'] = 3;
// Ahora sí que cuando se lea la segunda vez valdrá 3
Con ésto, nos aseguramos que todo va a funcionar como debe sin tener dolores de cabeza.

Visto lo visto, si queremos realizar la misma barbaridad que vimos ayer accediendo a los valores de la sesión usando el contexto que hemos creado, habría que cambiarlo por
// Coge los valores de la sesión para nuestro contexto
$aplicacion = 'aplicacion1';
$res   = preg_match('/' . $aplicacion . '\|a\:\d+\:\{(.*)\};?/i', $txt, $data);
if($res) $txt    = $data[1];
else die("No hay sesión iniciada");

// Lee el valor de la variable id del contexto aplicacion1
$var   = 'id';
$res   = preg_match('/"' . $var . '"\;s\:\d*\:\"(\d+)\";/i', $txt, $data);
if($res) $idt    = $data[1];
Los cambios son pocos pero sutiles. Ahora, nuestra variable estará encerrada entre comas (antes no lo estaba) y el separador entre el nombre de la variable y el valor pasa de ser dos puntos a ser punto y coma.

Cuidadín con lo que haceís ya que en ese fichero de sesión se almacenan todas las sesiones iniciadas por ese cliente, tanto para la "aplicacion1" como para la "aplicacion2" siguiendo el ejemplo del principio.

2011/05/26

Sesiones en PHP a lo campestre

Hola a todos, hoy toca ser un poco "bruto". En uno de mis desarrollos, me he encontrado con un bug de PHP de modo que al trabajar con ficheros en tiempo real, cada vez que abría la sesion son session_start() para leer los datos del usuario actual, se me bloqueaban los ficheros por lo que se el tempo real se me iba al garete y el tiempo re sespuesta al timeout.

En vez de desesperar, me las he ingeniado para realizar las operaciones que jace el session_start() a lo basto, leyendo directamente las sesiones desde disco y extrayendo los datos que necesitaba.

Aquí tenéis un ejemplo. Sed buenos, que os estoy vigilando ;)

El primer paso es recibir el session_id() bueno de la sesión que tengamos iniciada (suponemos que en algún momento hemos hecho un session_start() con éxito. Si no, mal vamos).

El session_id se puede obtener llamando a la función session_id() después de un session_start() o mediante la lectura de la cookie PHPSESSID mediante $_COOKIE['PHPSESSID']. En el ejemplo se puede pasar el is mediante el parámetro "sid" de la página o mediante la cookie.
// Obtiene el session_id
$sid = $_REQUEST['sid'];
if(!$sid) $sid = $_COOKIE['PHPSESSID'];
if(!$sid) die "Necesito un identificador de sesión!!!";

Ahora calculamos el nombre del fichero donde se almacena la sesión
// Lee los IDs desde los ficheros de la sesión
$fname = session_save_path() . "/sess_" . $sid;
$txt   = file_get_contents($fname);

Los ficheros son ficheros de texto con cada variable expresada sefún el formato
variable|tipo:longitud:"valor";

variable es el nombre de la variable
tipo es un carácter que indica el tipo de datos. por ejemplo las cadenas son la letra 's'
longitud es el número de caracteres del valor
valor es el valor de la variable de sesión
Así, vamos a leer el valor de las variables usando expresiones regulares
$res = preg_match('/variable\|s\:\d*\:\"(\d+)\";/i', $txt, $data);
if($res) $valor = $data[1];

2011/05/25

BubbleUp un plugin con jQuery con inspiración en los macs

Hace tiempo, encontré este pequeño plugin que funciona bastante bien en éste sitio. Como tenía planteado usarlo en alguno de mis proyectos, nada mas abrirlo me encontré con unas cuantas cosas que no me gustaban así que después de limpiarlo y quitarle algunas telarañas, vi que con un mínimo esfuerzo se podía mejorar para simplificar el CSS usado, así como darle estilo a los tooltips y mejorar las animaciones. Así que me puse manos a la obra y aquí está la versión 0.0.2 y funciona en todo menos para IE6 (mecachis...).

Lo podéis ver en funcionamiento en mi jsfiddle:

2011/05/20

Detectando la rotación de pantalla en Android

Snippet para detectar cambios en la orientación de pantalla en dispositivos móviles. Está pensado para Android, pero supongo que serviría para cualquier plataforma móvil. Realmente no controla que se rote, si no que controla si existe el evento de "onorientationchange" y asigna una función a él. En el caso de que el evento no esté soportado, vincula el evento a resize.

// Detect whether device supports orientationchange event,
// otherwise fall back to the resize event.
var supportsOrientationChange = "onorientationchange" in window,
    orientationEvent = supportsOrientationChange ? "orientationchange" : "resize";

window.addEventListener(orientationEvent, function() {
    alert('HOLY ROTATING SCREENS BATMAN:' + window.orientation + " " + screen.width);
}, false);

Como guinda del pastel, en el caso de que se vincule al evento "resize", se podrían definir 2 variables globales (al contexto) llamadas por ejemplo "screenW" y "screenH" que se controlan en el evento de modo que si están intercambiadas, se ha cambiado la orientación. Con ello, nuestro código quedaría así.

var _screenW = screen.width;
var _screenH = screen.height;

// Detect whether device supports orientationchange event,
// otherwise fall back to the resize event.
var supportsOrientationChange = "onorientationchange" in window,
    orientationEvent = supportsOrientationChange ? "orientationchange" : "resize";

window.addEventListener(orientationEvent, function() {
    if(supportsOrientationChange ||
      (!supportsOrientationChange && _screenW == screen.height && _screenH == screen.width))
   {
       alert('HOLY ROTATING SCREENS BATMAN:' + window.orientation + " " + screen.width);
   }
}, false);

2011/05/19

Contadores CSS

Cuántas veces hemos necesitado autonombrar elementos HTML y para ello hemos hecho un bucle en PHP que lo que hace es añadirle un número consecutivo a cada elemento?. Vale, es la solución más portable, pero CSS desde 2.1 soportaría realizar esa función de forma nativa lo caul puede ser útil cuando por ejemplo no se disponga de soporte de lenguajes de script o no se quieran usar.

Estaría soportado desde IE7 y superiores (y por supuesto por el resto de los navegadores de verdad) y la técnica se basa en utilizar las propiedades CSS counter-reset y counter-increment junto con la propiedad content dentro del pseudoelemento :before para autogenerar ids. Se puede hasta anidar contadores!

Los contadores se inicializan con
counter-reset: nombre_del_contador;

Y se incrementarían con
counter-increment: nombre_del_contador;

Se puede referenciar al valor como si fuera una "función" con la palabra reservada
counters

Vamos a ello.

En el primer ejemplo, se va a numerar atomáticamente cada sección de una página de modo que para cada sección, indicada por <h1>s tenga automáticamente la palabra "Sección <n>" con <n> el número de sección.

Primero, reseteamos el contador para que empiece por 1:
body {counter-reset: thecounter} /* el nombre del contador sería "thecounter" */

Cada <h1> cogería el prefijo "Sección " añadiéndole el contador que automáticamente se incrementaría para cada sección (que es por defecto y se puede obviar), donde "thecounter" es el nombre del contador que se va a usar para nombrar las secciones:
.counter h1:before {
    counter-increment: thecounter 1;
    content:"Sección "counter(thecounter)":";
}

Para una lista numerada anidada el contador se resetea y la numeración automática de los <ol> se desactiva porque no soporta anidamiento
ol {
    counter-reset: section;
    list-style-type: none;
}

Ahora, cada <li> recive indentación automática y sel sepearador se fija al punto "." seguido por un espacio en blanco
li:before {
    counter-increment: section;
    content: counters(section,".")"";
}
El ejemplo completo sería:
<ol>
    <li>item</li>           <!-- 1 -->
    <li>item                <!-- 2 -->
        <ol>
            <li>item</li>   <!-- 1.1 -->
            <li>item</li>   <!-- 1.2 -->
        </ol>
    </li>
    <li>item</li>           <!-- 3 -->
<ol>

2011/05/18

"Tú no sabes javascript"

He encontrado la transcripción de un artículo que mete el dedo en la llaga sobre la gente que hace con el javascript igual que con el "nivel alto de inglés" y que si fuera al extranjero se moriría de hambre :P

Lo mejor de todo son los enlaces a cada uno de los apartados apra ampliar información sobre el tema (y también algo de alivio porque la mayoría de los temas ya los he tratado yo en entradas anteriores jejeje)

Bueno, sin más dilación os dejo el artículo.

Tú no sabes Javascript
Con este llamativo título, Michael Woloszynowicz escribía recientemente una entrada en su blog a modo de llamada de atención para los desarrolladores Javascript.

Al estilo del artículo Los programadores que no programan, Michael denunciaba que muchos desarrolladores están engordando sus currículums afirmando ser expertos en lenguajes que realmente no conocen. Esta preocupante tendencia parece agudizarse cuando nos referimos a la familia de lenguajes web: HTML, CSS y, especialmente, Javascript.

La falsa sensación de dominio total
El motivo de todo esto es doble: por un lado, los lenguajes web están de moda y todo desarrollador debe ser capaz de programar sin dificultad en cualquiera de ellos; por otro, suelen ser consideradoslenguajes fáciles y muy asequibles: en realidad, tanto HTML como CSS podrían no considerarse estrictamente como lenguajes ya que apenas participan de una lógica computacional seria. Con tan solo un puñado de palabras reservadas y estructuras repetidas hasta la saciedad, es relativamente sencillo construir un sitio.

Pero nada más lejos de la realidad. Conocer el marcado HTML y la semántica que hay detrás no resulta tan trivial como le puede parecer a quien se acerca por primera vez: con solo ojear el código fuente de cualquier proyecto web, podremos  ver cómo las normas más básicas o recomendaciones del W3C son pasadas por alto. En cuanto a las CSS, el argumento es similar: aquellos que realmente las dominen sabrán la complejidad que algunos maquetados (desgraciadamente) encierran. En definitiva, es extremadamente raro encontrar a un desarrollador web que haya reparado en leerse las especificaciones de los lenguajes que utiliza a diario…

En el caso de Javascript, el error no puede ser mayor: la mayoría de los programadores necesitan recurrir a este lenguaje en uno u otro momento. Cuando apenas se conocen sus conceptos básicos, la tendencia más común es lanzarse a buscar ejemplos de códigos (snippets) que puedan ser implementados ràpidamente a través de un sencillo copy/paste. Dada la gran flexibilidad de algunosscripts, esta técnica suele funcionar, especialmente cuando se trata de plugins para bibliotecas tipo jQuery o Mootools. El principal problema de esta metodología de aprendizaje es que realmente no se llega a comprender el comportamiento que hay detrás de cada uno de esos bloques de código dando una falsa sensación sobre nuestro dominio del lenguaje.

Cuando llegan los encargos para construir aplicaciones complejas, los tutoriales en la web comienzan a mostrar conceptos exóticos como módulos, herencia prototípica, patrones de diseño, delegación, constructores o clausuras y con ellos, nuestra falsa idea inicial de dominio del lenguaje comienza a difuminarse.

Comienzan así las pruebas a ciegas donde echamos mano del patrón copy/paste; pero ahora las piezas no encajan: los errores se multiplican, nuestros conocimientos en otros lenguajes como por ejemplo Java o PHP no sirven pues la naturaleza de Javascript es diferente y la aplicación no funciona como se espera. Terminamos maldiciendo al lenguaje y comentándole al cliente que su proyecto no es consistente, que Javascript no ofrece la potencia, flexibilidad o seguridad necesaria para llevarlo a cabo y tratamos de sugerir alternativas en las que nos sintamos más cómodos… Y es que, por supuesto, es difícil terminar admitiendo que no conocemos el lenguaje: Javascript es un lenguaje web y, como tal, tiene que ser fácil y debemos dominarlo sólo por el hecho de haber implementado alguna vez un plugin jQuery con un bonito slideshow…

Niveles de conocimiento
Para poder crear un rasero con el que medir nuestro conocimiento real del lenguaje, podemos dividir sus conceptos en tres niveles diferentes: básico, intermedio y avanzado.

Cada una de estas categorías recogería las siguientes habilidades:
 
Un nivel básico de Javascript requiere:
  • Conocer la sintaxis básica de programación: bucles, condicionales, try/catch, etc…
  • Entender cómo se declaran las funciones (incluyendo las diferentes formas posibles para hacerlo así como las llamadas funciones anónimas).
  • Entender los principios básicos de los distintos ámbitos (scopes): ámbito global vs ámbito de objeto. Excluímos aquí las clausuras.
  • Entender el rol de los distintos contextos y el uso de la palabra reservada this en cada uno.
  • Entender las diferentes maneras de declarar o instanciar un objeto así como las formas de convertir las funciones en éstos.
  • Entender los operadores de comparación Javascript como ‘<’, ‘>’, ‘==’, ‘===’; valores falsy y cómo funciona la comparación entre objetos y cadenas así como el cambio de tipos (casting).
Un nivel intermedio debería requerir:
  • Entender los tiempos de ejecución, cómo funcionan, y cuándo/cómo pueden ser útiles. También incluimos los métodos de ejecución asíncronos.
  • Conocimiento profundo de los callbacks y de los métodos call y apply para controlar el contecto y los argumentos que se envían a las funciones.
  • Comprender en profundidad la notación JSON y el funcionamiento de eval.
  • Entender las clausuras, cómo afectan al rendimiento del código, y cómo pueden ser usadas para crear variables privadas mediante el patrón módulo.
  • AJAX y serialización de objetos.
Un nivel avanzado de conocimiento incluye:
  • Conocer el valor arguments y cómo puede ser utilizado para sobreescribir funciones: recursividad.
  • Clausuras avanzadas como los patrones memoization, curry y aplicaciones parciales.
  • Prototipos, cadena prototípica y cómo usar estos conceptos para minimizar el tamaño de nuestro código.
  • Tipos de objetos y el uso de instanceof y typeof.
  • Expresiones regulares.
  • Qué expresiones no deben utilizarse (bad patterns)
  • Y, finalmente, cómo combinar todo lo anterior para construir aplicaciones sólidas, limpias, rápidas, mantenibles y compatibles.
El punto final, es especialmente importante porque puede darse el caso de que conozcamos los anteriores en mayor o menor medida pero no la forma de integrarlos en un todo. Este aspecto es el que finalmente denominaríamos arquitectura Javascript y corresponde a los cimientos en los que tiene que asentarse todo proyecto serio. Es el camino opuesto a un código tipo spaghetti realizado sin ninguna planificación previa.

Conclusión
Como puede comprobarse a través de todos los aspectos mencionados anteriormente, Javascript es algo más que manejar eventos con nuestra biblioteca favorita o validar un formulario. Es cierto que podemos solventar muchos de estos puntos utilizando nuestro tradicional copy/paste; sin embargo, con esta técnica, podemos llegar muy fácilmente a un callejón sin salida donde continuar avanzando resulte simplemente imposible.

La mejor garantía para afrontar un proyecto de envergadura es, como con cualquier otro lenguaje, conocer el comportamiento de las herramientas que utilizamos lo mejor posible. De este modo, si no encontramos el snippet adecuado o tenemos que modificar el comportamiento de uno concreto, estaremos en disposición de hacerlo nosotros mismos sin mayores problemas.

2011/05/17

Mapeando "this" a "self"

Continuando con la serie de entradas dedicadas a programación orientada a objetos con javascript, me he encontrado con algo que ya había visto en varios sitios, pero que nadie se había molestado en explicar, la asignación a self de la instanciación de la clase (this).


El problema principal viene de que "this" puede variar a lo largo de la ejecución del javascript al usarse frameworks como jquey, de modo que de lugar a errores muy difíciles de encontrar ya que "debería funcionar", pero no lo hace debido al ámbito de this.


Un ejemplo de este comportamiento es el siguiente.

Bob.findFriend("Barry");

Person.prototype.findFriend = function(toFind) {
    // this = Bob
    $(this.friends).each(function() {
        // this = Bob.friends[i]
        if (this.name === toFind) {
            // this = Barry
            return this;
        }
    });
}

En el ejemplo, inicialmente this vale "Bob", pero en cuanto entra en el bucle de jQuery each, this es igual a cada uno de las iteraciones del bucle y ya la hemos armado buena...

Esta situación se puede dar my fácilmente así que la mejor solución es usar una variable local al contexto del objeto para guardar la instancia del objeto (y posiblemente mejorar el rendimiento) mediante

var self = this

Así, el ejemplo anterior quedaría así corregido de modo que haga lo que tiene que hacer y no perdamos el valor de "this" (ahora "self")

Bob.findFriend("Barry");

Person.prototype.findFriend = function(toFind) {
    // the only time "this" is used
    var self = this;
     
    $(self.friends).each(function(i,item) {
        if (item.name === toFind) {
            return item;
        }
    });    
}

2011/05/07

Bucle de alfabeto en PHP

Hoy algo ligero para sobrellevar los efectos perniciosos de la feria.

Me ha hecho falta crear una paginación mediante letras y quería hacer algo elegante en PHP. La primera solución no me gustó demasiado, pero puede servir para ciertas situaciones.
<?php for ($i=65; $i<=90; $i++) echo "Mi código que usa " . chr($i); ?>
La solución más "bonita", sin embargo sería ésta
<?php foreach(range('A','Z') as $i) echo "Mi código que usa " . $i; ?>

2011/05/05

Optimización de javascript (XI). Ámbitos de ejecución

Para no hacer muy densa la entrada de ayer, la he dividido en dos partes.

Ésta sí que se refiere a optimización de javascript, ya que posiblemente, la creación de un ámbito de ejecución para nuestra aplicación sea la característica que más va a influir en la ejecución de nuestro javascript si éste es medianamente pesado. Además de las ventajas de tiempo de ejecución por reducción del ámbito, de regalo se tiene una aproximación a programación orientada a objetos con javascript que permite encapsular funcionalidades.

Un auténtico experto en el uso de ámbito para aplicaciones en tiempo real (demos gráficas) es el creador del sitio dhteumeuleu, que mediante una base de aplicación sencilla y un timer, hacer arte.

El esqueleto básico de nuestra aplicación 'App' debe ser el siguiente:
// Esqueleto de la clase App
var App = function() {
    /////////////////////////////////////////////////////////////
    // MIEMBROS PÚBLICOS DE LA CLASE
    /////////////////////////////////////////////////////////////
    // Objeto donde almacenaremos las propiedades públicas de la clase
    // Se accede a ellas mediante app.self.propx
    this.self = {
        prop1: valor,
        prop2: valor,
        // ...
    };

    // Otra manera de definir miembros públicos de la clase es mediante this
    this.prop = valor;
    this.funcionPublica = function (params) { /* código */ };

    /////////////////////////////////////////////////////////////
    // MIEMBROS PRIVADOS DE LA CLASE
    /////////////////////////////////////////////////////////////
    // ----- crossbrowsers addEvent ----- 
    function addEvent (o, e, f) { 
        if (window.addEventListener) o.addEventListener(e, f, false); 
        else if (window.attachEvent) r = o.attachEvent('on' + e, f); 
    } 

    // Variables privadas de la clase
    var _proppriv1 = valor;
    var _proppriv2 = valor;

    // Funciones privadas de la clase
    var func1 = function (params) { /* lo que sea */ };
    function func2(params) { /* lo que sea */ } // También se puede hacer así

    /////////////////////////////////////////////////////////////
    // CLASES
    /////////////////////////////////////////////////////////////
    // Además del resto de miembros privados y públicos, 
    // se pueden definir nuevas clases dentro de nuestra clase que se 
    // podrán usar dentro del objeto (y no fuera).
    // Evidentemente todos los miembros de nuestra clase son 
    // visibles para la clase "hija"
    var Coordenada = function (x, y) { 
        this.x = x; 
        this.y = y; 
    }; 

    /////////////////////////////////////////////////////////////
    // CONSTRUCTOR
    /////////////////////////////////////////////////////////////
    var init = function (params) {
        // Función interna del constructor
        // Llamada desde la función de instanciación
    };

    // Devuelve la función de instanciación de la clase
    // En este caso se vincula a un evento para que la ejecución no sea bloqueante
    return {
        init : function (params) { 
            addEvent(window, 'load', function () { 
                init(params); // Llama a la función de construcción
            }
        };
}();

/////////////////////////////////////////////////////////////
// FUNCIONES PÚBLICAS DE LA CLASE
// Se hace mediante el prototype por mayor velocidad de 
// ejecución a costa de algo más de memoria usada.
App.prototype.func1 = function( params ) { /* ... código ... */ };
App.prototype.func2 = function( params ) { /* ... código ... */ };
// ...
App.prototype.funcn = function( params ) { /* ... código ... */ };

Cuando se esté ejecutando código de una de las funciones de la clase, para acceder a la "instancia" (los valores de la instanciación del objeto) basta con hacer la llamada mediante
self.propx
ya que self está dentro del ámbito de la aplicación o mediante un simple
propy
en el caso de que se haya definido de la segunda manera, eso sí, no nos beneficiamos de la reducción del ámbito de búsqueda que nos da definirla dentro de "self".

Para crear la instancia de la aplicación se hace de la manera siguiente
<script>var miApp1 = app.init( { prop: valor, prop: valor, ... } );</script>
Si no quisiéramos usar una función "init" para instanciar la clase, se podría quitar toda la parte del constructor y se definiría la clase como
// Esqueleto de la clase app
var App2 = function( params ) {

    // ... 
    // El resto es igual, pero para acceder a los parámetros de instanciación
    // habría que hacer params.prop, con param en formato de objeto 
    // ...

    // ... Inicialización ...

    return self; // Dentro vamos a definir los miembros públicos además 
                 // de los definidos mediante this.xxx = yyy;
    return this; // Si no se quiere usar el objeto "self" siempre se 
                 // puede devolver this
}();

// Se llama mediante
var miApp2 = App2( { prop: valor, ... } );

En la llamada, se crea la instancia de la clase de la aplicación pasando como parámetro un objeto con los valores de la instanciación.

Si en algún momento ya no nos hacen falta propiedades que tenga definidas (ya sean variables o funciones incluso) se pueden borrar mediantedelete miApp.prop;
Os recuerdo que además, se pueden eleiminar elementos del DOM que ya no nos hagan falta para liberar memoria mediante
delete el.parentNode.removeChild(el);
Como extra, podemos aprovechar además para aplicar técnicas de "memoization" que no es más que cachear resultados de operaciones costosas para acelerar su ejecución
function miFunc(param) {
    if(!miFunc.cache) {
        miFunc.cache = {}; // Creamos un caché de resultados para la función
    }
    if(!miFunc.cache[param]) {
        var resultado = {};
        // ... realiza las operaciones y se calcula resultado ... 
        miFunc.cache[param] = result;
    }
    return miFunc.cache[param];
}
Obviamente, para tareas costosas que no necesiten ejecutarse en primer plano, tenemos los web workers de los que ya he hablado con antelación. Si no se dispone de web workers por la versión del navegador, se puede hacer ejecutar la función en segundo plano mediante setTimeout() como se vió en el capítulo VI de optimización de javascript.

2011/05/04

Optimización de javascript (X). OOP con javascript

Como no hay 2 sin 3, no pueden haber 9 sin 10... vamos a por la décima entrega de optimización de javascript.

Hoy, más que hablar de optimización, vamos a ver algo sobre orientación a objetos, que uno de los mejores inventos desde la cocacola.

El primer paso para la orientación a objetos, es saber cómo se crea un objeto de tipo Object en javascript.

El objeto más simple es el mapa (y no el de Dora la exploradora) o matriz asociativa dispersa que se define entre llaves (recuerdo que los corchetes es la notación simple para definir arrays) y que se indexa por cadenas.

Para definir propiedades del objeto, se puede hacer al definirlo (la clave es una cadena de texto pero no es obligatorio escribirla como cadena entre comillas) o a posteriori como si fuera un Array.
var obj      = {};               // Objeto vacío
var obj      = {'clave': valor}; // Objeto con propiedad
obj['clave'] = valor;            // Propiedad en tiempo de ejecución
El valor almacenado para una propiedad puede ser cualquier cosa, es decir, cualquier objeto de javascript, incluidos otros objetos.

Así se definen objetos, pero las funciones también son objetos y cuando se busca la orientación a objetos con javascript, son los que se usan para definir las clases.

Una clase sencilla definida con una función puede ser el siguiente
var Coordenada = function (x, y) { 
    this.x = x; 
    this.y = y; 
}; 
Los miembros de la clase se definen de dos maneras, o desde dentro de la definición de la clase, o desde fuera.

Si es desde dentro, se hace con la palabra reservada this
this.x = x;
Si es desde fuera, hay que hacer referencia al prototipo de la función clase
Coordenada.prototype.propiedad = valor;
Todas las variables/funciones definidas dentro dentro de nuestra función clase que no se definan mediante this, serán miembros privados de la clase mientras que todas las que se hagan con this (o accediento al prototype) serán miembros públicos.
var Coordenada = function (x, y) { 
    this.x = x;  // Miembro público
    this.y = y;  // Miembro público
    var c = 123; // Miembro privado
};
Para instanciar la clase, no hay más que usar la palabra reservada new
var c = new Coordenada(x, y);
Creo que es importante recordar algo de notación básica de objetos (heredada de Java) para diferenciar clases de objetos.
  • Las clases comienzan por mayúscula
  • Los objetos empiezan por minúscula
Aparte, de regalo, recomiendo usar una notación coherente luego para nombrar clases y objetos. Si te gusta usar '_' entre "palabras", usar siempre '_'. Si te gusta usar combinaciones mayúsculas/minúsculas, usarlas siempre.

2011/05/03

Optimización de Javascript (IX). Creando una cola de ejecución

Tal y como hemos visto en las 8 entradas anteriores, hay que optimizar todo lo referente a javascript y su carga ya que éste bloquea la ejecución durante la carga.

Lo hemos movido a otro sitio de la página, lo hemos minimizado, lo hemos cargado en diferido, pero aún no había tratado sobre el código javascript de tipo inline, es decir, el que ponemos en medio del códgido para hacer cualquier cosa. Bueno, pues ahora vamos a ver cómo optimizar éste código también para dar una mayor velocidad de carga de nuestra página.

1.- El secreto de todo está en crear un objeto para poner en una cola de ejecución todas nuestras llamadas de tipo inline. Así, lo primero que tendremos que hacer es
var miaplicacion = { queue: [] };
Con ésto creamos un contexto de ejecución para todas las llamadas además de crear un array para encolar las ejecuciones.

2.- Ahora tenemos que sustituir todas las llamadas inline como por ejemplo
<script>alert("Zas!");</script>
por
<script>
    miaplicacion.queue.push(function() { 
        alert("Zas!"); 
    }
</script>
3.- Ya hora la guinda. Una vez llegamos al final de la página, justo antes del </body> que es donde ya estamos seguros de que se ha cargado todo el DOM (así no es necesario enlazar con el evento de DOMReady con lo que arañamos aún más milésimas de tiempo de ejecución), ejecutamos la cola mediante el siguiente código:
<script>
    var l = miaplicacion.queue.length;
    for(var i = 0; i < l; i++) {
        miaplicacion.queue[i]();
    }
</script>