Archivo para 2010

jQuery

No hace mucho, en una clase del curso J2EE Avanzado del Club de Programadores, el profesor hace referencia a la posibilidad de generar contenido dinámico con palabras del estilo:

[...] y cargan todo con ajax usando jQuery [...]

En el resto del curso hubo otras tantas referencias del mismo tipo. Para alguien que conoce un poco del ambiente del lenguaje semejantes dichos pasan por una suerte de desagravio (salvando las distancias obviamente).

El lenguaje está muy extendido, inclusive por lejos de las fronteras de los browsers*, y aún dentro de los mismos, lejos está de ser la única alternativa.

*Server Side Javascript (SSJ)Rhino (intérprete JS en Java), etc. Sin ir más lejos, el cliente de mensajero interno que uso en el trabajo está escrito parcialmente en JavaScript!

Aún dentro del ecosistema originario, existen unos cuantas librerías/frameworks similares a jQuery (sMooTools, ExtCore, Glow,  base2, Pulp, Rico, YUI, etc). Obviamente, cada una tiene sus características particulares que la hacen más o menos querible, pero definitivamente ninguna de ellas está muy alejada de lo que ofrece jQuery.

Y en lo personal puedo llevar la apuesta inclusive un poco más lejos, ¿cuántas veces se utiliza jQuery (u otra de las librerías similares) para acciones que no llevan más de unas líneas de código? ¿Qué tanto alejan al programador del lenguaje? Con estas reflexiones en la mano me gustaría saber cuantos de los usuarios fanáticos de esta librería pueden leer el código fuente con conocimiento de causa.

A no confundir el mensaje, jQuery es una EXCELENTE librería, con una GRAN y CAPAZ comunidad detrás, con el peso de un gigante que mueve montañas (y crea los peores browsers). A estas ventajas se le suma la ENORME cantidad de plugins que existen y más importante aún, que promueve buenas prácticas, como la encapsulación vía clousures además de tomarse el trabajo de evitar colisiones con otras librerías/frameworks (jQuery.noConflict()). Sólo que no deja de ser lo que es, una librería.

Finalizando, definitivamente ayer JavaScript no fue Prototype, hoy no es jQuery, ni mañana será ######.

Leer más:

Adjunto la presentación realizada por Nicholas C. Zakas* en la YUIConf 2010 sobre performance y javascript.

*Nicholas es uno de los ases de desarollo frontend de Yahoo, siendo el principal responsable del frontend de su home (www.yahoo.com). Además contribuye al desarrollo de YUI, siendo el autor de alguno de sus módulos (Cookie Utility, Profiler, and YUI Test).

Leer más:

Prácticamente todo el mundo con PC sabe lo que es un browser y como utilizarlo, pero como sucede con todo, existe un gran desconocimiento de las formas y comportamiento de los mismos.

Este post tiene como humilde objetivo introducir en los conceptos básicos de dos de los componentes más críticos de los browsers en cuanto a experiencia generada; estos son el motor gráfico y el interprete JavaScript.

A modo de disclaimer: No incluyo a Opera por dos motivos. El primero es la poca participación que tiene en el mercado (1.93% según wikipedia al día de la fecha), y el segundo es que no tengo suficiente conocimiento del mismo e implicaría bastante más investigación que el resto.

Motor gráfico (Layout engine)

Cualquier browser (de primera línea, por así decirlo), posee un motor gráfico encargado de renderizar los archivos html (entre otros, dependiendo del navegador en cuestión). La interpretación que hacen debería respetar la especificación correspondiente*, con agregados particulares como suelen ser la libre interpretación de casos no previstos por la especificación o inclusive errados sintácticamente.

*Los motores gráficos listados pueden no ser estrictamente “motores gráficos”, pudiendo cumplir roles adicionales a los detallados (como ser manejo de historial, soporte DOM, etc).

El renderizado de los browsers (y su velocidad al realizarlo) se puede percibir al:

  • Cargar un sitio, donde se produce un renderizado completo, lo que implica generar la estructura en memoria (render) y su posterior “dibujo” en pantalla (draw).
  • Modificar la estructura del árbol DOM o la geometría de uno o más nodos. En este caso se debe revalidar la estructura generada (parcial o totalmente, dependiendo de la modificación). Esta acción se denomina reflow.
  • Modificar el estilo o geometría de uno o más nodos. Se debe actualizar la visualización de la parte modificada. Esta acción se denomina repaint (o redraw).

Sabiendo esto, vamos a conocer a los sospechosos de siempre.

Microsoft: TridentMicrosoft Trident

Internet Explorer usa Trident como motor gráfico desde la versión 4, con obvios progresos desde ese entonces. De los browsers populares, este es sin duda el menos avanzado (sin contar que corre únicamente sobre Windows).

Los problemas de renderizado* y box model que tuvo y tiene lo ponen muy detrás de los competidores. Gran ejemplo de esto es la dificultad que genera desarrollar un sitio compatible con IE6 e inclusive IE7.

Obviamente, esperamos (y rogamos) que IE9 sea ciertamente un cambio de aire al respecto. Aparentemente IE10 va a reemplazar Trident con Linx.

*Particularmente fui víctima de un  memory leak utilizando background transparente, lo cual descubrí después de culpar y refactorear todo el javascript del site.

Mozilla: Gecko

Netscape GeckoMozilla usa Gecko (originalmente denominado NGLayout) para sus navegadores, ciertamente más poderoso que Trident. Está escrito en C++ y es multiplataforma.

Como adicional cabe destacar que tiene soporte nativo para renderizar archivos XUL (lenguaje de interfaz propio de Mozilla).

A los valientes, los invito a probar SeaMonkey, una “internet-suite” que utiliza Gecko para el render web.

Apple + Google: WebKit

WebkitChrome y Safari (entre otros) utilizan WebKit, que surge como fork de KHTML (motor gráfico para el navegador de KDE, Konkeror). Hablando con propiedad, si bien ambos utilizan este motor, no lo hacen con todos los componentes.

Como se verá más adelante Chrome utiliza WebCore (parte específicamente dedicada al render dentro de WebKit) pero no JavaScriptCore, al cual reemplaza con un intérprete propio.

Este motor gráfico es usado también por Adobe para su plataforma Air.

Dato curioso, el navegador Lunascape utiliza tres motores gráficos distintos (Trident, Gecko y WebKit), permitiendo alternar entre ellos.

Interprete JavaScript (JavaScript engine)

Por otra parte, otro componente (obviamente crítico) es el interprete Javascript, del cual depende obviamente la ejecución de los scripts. En este aspecto varían las implementaciones de browser a browser, así como también las optimizaciones realizadas.

Estos intérpretes están recibiendo mucha atención hoy en día, siendo el rendimiento de los mismos un factor crítico a la hora de promocionar los productos.

Microsoft: JScript, Chakra y la turba iracunda

Cualquier persona que se maneje mínimamente en el tema del desarrollo web sabe sobradamente lo odioso que se tornan las cosas al incluir los productos de Microsoft en la ecuación.

Obviamente esto no cambia a la hora de hablar de Internet Explorer y su (¿libre?) interpretación de JavaScript. En principio, ellos no interpretan JavaScript como está propuesto por el estándar (!), sino JScript, que representa un subset de funcionalidad JavaScript en conjunto de algunas extensiones propietarias*.

*Hay que reconocer sin embargo, que algunas de esas extensiones originalmente propietarias son muy exitosas hoy en día, siendo la más reconocible de las mismas las XMLHttpRequest(vulgarmente hablando, Ajax).

Mozilla: SpiderMonkey

Mozilla utiliza SpiderMonkey (que viene de la época de Netscape), escrito en C. A este motor se agregaron como componentes de optimización TraceMonkey (FF3.5) y JägerMonkey (FF4.0). Las optimizaciones realizadas van desde pre compilación (se optimiza el código al evaluarlo) hasta compilar en tiempo de ejecución porciones de código críticas (como ser el código interno de un loop).

Proyecto hermano de SpiderMonkey es Rhino, motor escrito en Java,  en el cual se basan los proyectos SSJ Narwhal y RingoJS.

Google: V8

V8Google presentó su navegador (Chrome) con un poderoso motor Javascript denominado V8. A diferencia de SpiderMonkey, V8 está escrito en C++.  Obviamente también presenta un conjunto de optimizaciones, siendo la más importante compilar el código Javascript a código nativo además de usar la técnica de inline caching.

Este motor es artífice del hype actual que existe con respecto a la velocidad de ejecución, desde su salida, todos los browsers del mercado tratan de seguirle el paso a Google con obvio beneficio para los usuarios.

Al igual que ocurre con Rhino, este intérprete es utilizado por (al menos) un proyecto SSJ, en este caso se trata de Node, sin duda alguna el de más renombre actualmente.

Apple: JavaScriptCore y SquirrelFish

SquirrelFishSafari utiliza por obvios motivos el intérprete JavaScript propio de WebKit, con las también obvias mejoras recibidas.

En este caso, el esfuerzo de los muchachos de WebKit se canaliza en SquirrelFish, que posteriormente evoluciona a SquirrelFish Extreme (SFX, mejor conocido Nitro) que aplica la ya mencionada receta de traducir JavaScript a código nativo con su consecuente mejora de performance.

Leer más:

Imagen de Trident tomada de: http://blogs.sitepoint.com/2010/04/01/microsoft-drop-trident-from-internet-explorer/

Funcionalidad similar a la que se puede encontrar en otros lenguajes. Usar con cuidado ya que se puede superar el tiempo limite de ejecución del browser.

/**
 * Bloquea la ejecución del script
 *
 * @author Valentin
 * @param {Number} millis
 */
function sleep(millis) {
    var d = +new Date + millis;
    while (+new Date < d);
}

Angus Croll del blog Javascript, Javascript parte de parafrasear a James Gosling (autor original de Java) para exponer la delegación de funcionalidad en JavaScript.

Bill Venners: When asked what you might do differently if you could recreate Java, you’ve said you’ve wondered what it would be like to have a language that just does delegation.
James Gosling: Yes.

Tecnicamente hablando, la delegación es la reutilización de código mediante llamadas a funciones no nativas de los objetos. En el caso de Javascript esto se puede lograr con mucha facilidad gracias a los métodos call y apply del objeto Function.

Esto lo expone mediante el código expuesto a continuación (copiado textualmente, solo se agregaron comentarios):

/**
 * Construye rectangulos
 *
 * @class
 */
var Rectangle = function(left, top, length, width, options) {
	this.left = left;
	this.top = top;
	this.length = length;
	this.width = width;
    if (options) {
    	this.color = options.color;
    	this.border = options.border;
    	this.opacity = options.opacity;
    	//... etc.
    }
}

/**
 * Verifica si el rectangulo colisiona con el objeto
 * recibido
 *
 * @param {Object}
 * @returns {Boolean}
 */
Rectangle.prototype.overlaps = function(another) {
	var r1x1 = this.left,
	    r1x2 = this.left + this.width,
	    r1y1 = this.top,
	    r1y2 = this.top + this.height,
	    r2x1 = another.left,
	    r2x2 = another.left + another.width,
	    r2y1 = another.top,
	    r2y2 = another.top + another.height;	    

    return (r1x2 >= r2x1) && (r1y2 >= r2y1) && (r1x1 <= r2x2) && (r1y1 <= r2y2);
}
// Instanciamos
var myRectangle = new Rectangle(10, 10, 30, 20, {color:'#FAFAFA', opacity:0.7});
// Y verificamos
myRectangle.overlaps(myOtherRectangle);

Hasta ahí tenemos una implementación típica del OOP “prototípico” de Javascript. Ahora, volviendo al tópico, imaginemos un escenario donde otro tipo de objeto (Dashlet según Angus) requiere de la misma funcionalidad.

En un lenguaje OOP popular como Java esto se solucionaría extendiendo la clase Rectangle o implementando alguna interfaz puntual.
La crítica que encuentra a este punto esta dada por el hecho de que esta herencia agregaría atributos y comportamientos irrelevantes (e innecesarios)*. Entonces, es aquí donde se presenta la oportunidad de “delegar” comportamiento.

*Obviamente se podría modificar la jerarquía de clases y herencia para minimizar e incluso hacer desaparecer esta problematica, pero no es tema de este post.

Esto termina en un código poco agraciado, pero aún funcional (suponiendo que los objetos dashlet contengan los atributos utilizados en la función).

Rectangle.prototype.overlaps.call(dashlet1, dashlet2);

Si bien el resultado es indiscutible, en lo personal este tipo de construcciones no me parecen lo suficientemente ventajosas como para hacerlas práctica habitual. Creo que la flexibilidad que ofrece este lenguaje permite soluciones más elegantes, legibles y no mucho menos performante.

El resto del post habla de las funciones nativas génericas, que basicamente permiten trabajar sobre objetos de distinto tipo a su clase. Para ser más claros referirse al siguiente código (nuevamente copiado textualmente).

"".trim.apply([" a","b "]).split(",");
//["a","b"]

"".toLowerCase.apply(["DIV","H1","SPAN"]).split(",");
//["div","h1","span"]

"".match.call(["a16","b44","b bar"],/[a-z][0-9]+/g);
//["a16", "b44"]

"".replace.call(
	['argentina','brazil','chile'],
	/\b./g, function(a){ return a.toUpperCase(); }
).split(',');
//['Argentina',"Brazil","Chile"]

Acá paro la pelota, y pregunto ¿qué tan predecible es el comportamiento en cada uno de los ejemplos presentados?

En mi opinión, poco poco.

Leer más:

  • Buscar

  • Ultimos Twits

  • Por fecha

    • 2012 (11)
    • 2011 (63)
    • 2010 (13)
  • Tags

  • Documentacion Javascript

    JavaScript Reference, JavaScript Guide, JavaScript API, JS API, JS Guide, JS Reference, Learn JS, JS Documentation