26 ago 2012

Notas sobre High Perfomance JavaScript

Hace ya tiempo que medio leí un libro que se llama High Perfomance JavaScript, muy interesante en realidad sobre la optimización del uso de javascript desde su posicionamiento en una página web hasta la manipulación de los objetos DOM de la misma. No esta muy actualizado con respecto a los nuevos engines de los browser en los que trabajan Mozilla, Google o Microsoft, pero aun así me parece una buena lectura.

Pocas veces tomo notas de lo que leo, aunque considero que es una muy buena costumbre; pero en este caso en particular lo hice y dejo aquí esas notas,  no si antes recomendar la lectura del libro, ya que mis notas pueden ser confusas o estar incompletas:

Anotaciones:

- El browser dibuja la página conforme va leyendo las instrucciones. Si encuentra una instrucción <Scriptdetiene el dibujado de la pagina hasta que el script se halla parseado y ejecutado.

- En el caso de que las clausula script contenga archivos “src” estos deben bajarse, parsearse y ejecutarse, mientras pasa todo esto, el dibujado de la pagina se encuentra detenido y el usuario se enfrenta a una página en blanco por lo general.

- Siempre que sea posible poner los scripts lo más cerca de la etiqueta final del body y no en el head como generalmente se usa. Así la página se dibujara todo lo posible antes de parsear los scripts.
<html>
<head>
   <title>Script Example</title>
   <link rel="stylesheet" type="text/css" href="styles.css">
</head>
<body>
  <p>Hello world!</p>
   <--Ejemplo del posicionamiento recomendado de los scripts -->
   <script type="text/javascript" src="file1.js"></script>
   <script type="text/javascript" src="file2.js"></script>
   <script type="text/javascript" src="file3.js"></script>
</body>
</html>

- Tratar de agrupar los scripts en la menor cantidad de archivos js ya que cada llamada para bajar un archivo (cada src) es un http Request al servidor lo que añade overhead.

- Hay una alternativa para cargar archivos que no utilicen el DOM de la pagina (osea que no modifiquen controles o añadan contenido dinámico a la pagina) El IE 4 en adelante y Firefox 3.5 en adelante soporta la clausula Defer (Defferred) para continuar cargando la pagina. El archivo se baja, pero se ejecuta al final (para no bloquear el dibujado de la pagina) en los otros browsers la sentencia se ignora
<script type="text/javascript" src="file1.js" defer></script>

- Los valores literales (nombre que representa variables pero sin la palabra var) son trivialmente más rápidas de leer que las variables (var) éstas son mucho más rápidas de leer que los arrays y estos similar en tiempo de lectura que las miembros de objetos (Recomendación no usar arrays y miembros de objetos mientras sea posible)

- Por el orden de la “cadena de alcance (scope chain)” Las variables local son siempre más eficientes que las globales. Tratar de no usar with, try catch, eval en javascript a menos que sea absolutamente necesario.

- Tratar de no usar funciones o propiedades de los prototipos (en lo que se basan los objetos) y tratar de no usar cadenas de objetos: location.href es más rápido que usar window.location.href que a su vez es más rápido que usar window.location.href.toString().

- Nunca leer directamente la propiedad de un objeto directamente más de una vez, mejor guardarlo en una variable para usarla después NO usar esto:
function hasEitherClass(element, className1, className2){
    return element.className == className1 || element.className == className2;
}
En lugar usar esto (más eficiente)
function hasEitherClass(element, className1, className2){
    var currentClassName = element.className;
    return currentClassName == className1 || currentClassName == className2;
}

- En la mayoría de navegadores es mejor usar innerHTML que DOM puro (createElement, createTextNode, etc) solo es al revés en SAFARI 4 y CHROME 3 y es muy poca la diferencia.

- Siempre es más eficiente guardar en una variable local las propiedades o métodos de elementos DOM si van a utilizar más de una vez en la función. Esto es lento:
function collectionGlobal() {
    var coll = document.getElementsByTagName('div'),
        len = coll.length,
        name = '';
    for (var count = 0; count < len; count++) {
        name = document.getElementsByTagName('div')[count].nodeName;
        name = document.getElementsByTagName('div')[count].nodeType;
        name = document.getElementsByTagName('div')[count].tagName;
    }
    return name;
};
Esto es mucho más rápido
 var coll = document.getElementsByTagName('div'),
        len = coll.length,
        name = '',
        el = null;
    for (var count = 0; count < len; count++) {
        el = coll[count];
        name = el.nodeName;
        name = el.nodeType;
        name = el.tagName;
    }
    return name;
};

- Usar nexttSibling si está disponible y si programamos para IE. Por ejemplo: Esta función se puede usar sin inconveniente en la mayoría de browser con un buen rendimiento.
function testNextSibling() {
    var el = document.getElementById('mydiv'),
        ch = el.firstChild,
        name = '';
    do {
        name = ch.nodeName;
    } while (ch = ch.nextSibling);
    return name;
};
Sin embargo esta alternativa es mucho más rápida en IE (en IE6 16 veces y en IE7 10.5 veces más rápida que la anterior)
function testChildNodes() {
    var el = document.getElementById('mydiv'),
        ch = el.childNodes,
        len = ch.length,
        name = '';
    for (var count = 0; count < len; count++) {
        name = ch[count].nodeName;
    }
    return name;
};

- (Existen dos métodos en DOM que se disparan cada vez que cambiamos características de elementos DOM y son computacionalmente costosos; el primero es reflow que recalcula el árbol de elementos de una página cada vez que los cambios afecten la geometría de elementos o distribución dentro de la pagina y el otro es repaint que dibuja los elementos de la pagina. Cambiar el fondo de un div, por ejemplo, dispara repaint pero no reflow, los elementos ocultos solo afectan repaint, no reflow. Todo es se nota más en browser viejos) Se puede optimizar el re-dibujado (repaint) de la pagina cuando se manipulan directamente propiedades de elementos DOM utilizando el atributo cssText de estos elementos En lugar de hacer esto:
var el = document.getElementById('mydiv');
el.style.borderLeft = '1px';
el.style.borderRight = '2px';
el.style.padding = '5px';
Mejor esto:
var el = document.getElementById('mydiv');
el.style.cssText = 'border-left: 1px; border-right: 2px; padding: 5px;';
e incluso es mejor, más limpio y mantenible tener una hoja de estilo y cambiarla:
var el = document.getElementById('mydiv');
el.className = 'active';

- El reflow se puede optimizar si primero sacamos del árbol de elementos de la pagina, el elemento que deseamos modificar esto se puede lograr si utilizando tres aproximaciones:
1. Ocultando el elemento (elemento.style.display = 'none';) hacemos los cambios y luego lo volvemos visible (ul.style.display = 'block')
 2. Clonar elemento, realizar los cambios sobre el elemento clon y luego usar replaceChild para actualizar los cambios con el clon.
3. Por último haciendo uso de la sentencia similar a esta var fragment = document.createDocumentFragment(); para añadir elementos y luego usar elemento.appendChild(fragment); para añadir los cambios, esto permite que los elementos se agreguen fuera del árbol “en vivo” de la pagina. Esta es la recomendada por el libro.


Estas son mis notas del libro, ojala y sirvan para mejorar nuestras implementaciones; insisto en que es mejor leer el libro que un "resumen" tan a grosso modo como este.

5 ago 2012

SQLServer: Saving changes is not Permitted.

Algunas veces los mensajes se vuelven irritantes. Este el caso del mensaje que aparece cuando en SQL Server estamos rediseñando una tabla y necesitamos cambiar el tipo de una columna y nos sale un mensaje como este:

¿Que pasa? si esto lo hemos hecho cientos de veces, ¿Ahora que hicimos mal?
Nada. No pasa nada. Lo que nos indica el mensaje es que SQL esta previniendo el borrado y recreación de una o varias tablas relacionadas. Esto sucede cuando esas tablas ya cuentan con registros. Lo importante de todo esto, es que su solución es muy simple.

Todo lo que necesitamos hacer es ir a Tools -> Options y en el panel de la izquierda buscar "Designers" y dentro de este la sección "Tables and Database Designers", una vez aquí implemente desmarcamos la casilla "Prevent saving changes that require table re-creation" y listo.

Ya con este cambio podemos ejecutar los cambios que necesitemos en nuestras tablas aun cuando contengan datos y requieran la reconstrucción de la mismas.