[Reciclaje JavaScript] Fundamentos

June 11, 2013

Tal como dije en mi post del año pasado, una de las cosas que íbamos a aprender los desarrolladores de SharePoint con la nueva versión es precisamente a programar bien en JavaScript. Yo mismo me puse al día por necesidad y he descubierto que no es tan difícil programar bien JavaScript, si se entienden algunos conceptos clave. Mi idea en esta serie de posts es explicar estos conceptos de manera sencilla para que se entiendan bien. Intentaré no ser muy purista para no alejar la audiencia, que considero que tiene cierto conocimiento de JavaScript pero como una cosa totalmente auxiliar al código NET.

Puntos básicos de JavaScript

A pesar de su merecida o inmerecida fama de lenguaje “de juguete”, JavaScript es un lenguaje muy versátil y potente. Lo primero que hay que desaprender es que JavaScript no tiene nada que ver con Java (su nombre respondía a una estrategia de marketing de Netscape). JavaScript es un lenguaje funcional “vestido” de un lenguaje imperativo “tipo C”.

¿Funcional? Sí, en JavaScript las funciones son los reyes del mambo. Una función en JavaScript es objeto de primer nivel y se puede usar como resultado o parámetro de otras funciones. Una función puede tener parámetros pero éstos no son obligatorios cuando se invoca la función. Lo veremos todo más claro un poco más adelante, tened paciencia.

Tipos de datos

Todo lenguaje tiene una serie de tipos de datos básicos. JavaScript también. En esta tabla recogemos los tipos de datos de JavaScript:

Sencillos

Complejos

  • Number
  • String
  • Boolean
  • undefined
  • null
  • Object
  • Array
  • Function
  • RegExp
  • Date

A continuación voy a explicar las frikadas peculiaridades de cada tipo de datos, excepto RegExp y Data que son bastante especializados.

Wrappers

La mayoría de tipos en JavaScript se exponen dos veces: una como tipo primitivo y otra como “clase”. Por ejemplo, una cadena de texto en JavaScript es un tipo primitivo llamado string pero expone métodos como p.ej. toUpperCase() como si fuera un objeto. Realmente, hay un “objeto” de tipo String que encapsula (“wrapea”) un tipo primitivo string. Dicho esto, en JavaScript casi siempre es mejor usar el tipo primitivo que el tipo encapsulado, por tanto es mejor usar el constructor de objeto () y de matriz ([]) y no los constructores new Object() y new Array().

Number

Todos los números en JavaScript son números decimales de 64 bits en coma flotante. Teniendo en cuenta esto, hay que usarlos muy cuidadosamente para cálculos intensivos, ya que las operaciones decimales siempre son mucho más lentas que los números enteros. Algunas implementaciones de JS optimizan el tipo de datos numérico a enteros cuando no hay decimales involucradas, pero siempre hay que partir de la base de que en JS los números son “caros” para realizar operaciones.

String

El texto se representa en JavaScript como Unicode. Podemos usar las comillas simples o dobles para incluir el valor literal de un string, pero hay que usar dos iguales. Si queremos “escapar” las comillas para incluirlas en el texto, basta con usar las comillas opuestas, p.ej. var textoConComillas = "texto 'entre' comillas";

Además, podemos codificar un string para incluirlo en la URL (que no permite ciertos caracteres) y para ello tenemos 2 instrucciones:

  • encodeURI / decodeURI de usan para direcciones URL/URI completas
  • encodeURIComponent / decodeURIComponent se usa para los valores de los parámetros en una URL/URI (es decir, los caracteres como & se escaparán)

Boolean

El tipo booleano tiene valores true o false (sin comillas). La diferencia es que en JavaScript todos los tipos de datos evaluan a un booleano. Es lo que se conoce como valores “truthy” o “falsey”.

Valores que evaluan a FALSE

Valores que evaluan a TRUE

  • false
  • 0 (cero)
  • "" o '' (cadena vacía)
  • null
  • undefined
  • NaN (resultado de división por cero)
  • todos los demás (incluso las cadenas de texto “false” o “0”)
  • objetos vacíos (p.ej. var a = ;)

De esta manera podemos simplificar las comparaciones: if(a == “” || a == undefined) pasa a ser if(a)

Undefined

Con esta piedra tropieza todo programador de NET en JavaScript. La regla más fácil para recordar es que undefined es una referencia nula en JavaScript. Si no declaramos una variable o la declaramos pero no le asignamos nada, en JavaScript esa variable será del tipo undefined.

Lo que es un null en NET, es undefined en JavaScript.

Null

A pesar de su parecido con NET, el null de JavaScript es un mero marcador de valor nulo que podemos poner para indicar que una variable no tiene un valor significativo, parecido al NULL de SQL.

Es importante saber que JavaScript nunca devolverá un null a no ser que lo pongamos nosotros a una variable.

Object

Tal como he mencionado anteriormente, los objetos en JS no son tan “fuertes” como en NET. No existe el concepto de clase ni de instancia.

Para instanciar un objeto podemos usar:

<table cellspacing="0" cellpadding="2" width="400" border="0"><tbody><tr><td valign="top" width="200">El <strong>constructor</strong> usando la notación “orientada a objetos”</td><td valign="top" width="200">var a = <strong>new Object();</strong><br>a.nombre = 'Edin';</td></tr><tr><td valign="top" width="200">El constructor <strong>literal</strong> usando la sintaxis JSON</td><td valign="top" width="200">var a = <strong>{</strong> nombre: 'Edin'<strong> };</strong></td></tr><tr><td valign="top" width="200">Una <strong>función</strong>, usando la palabra clave this para referirnos a la “instancia” actual de la función.</td><td valign="top" width="200">var a = <strong>function()</strong><br><strong>{</strong><br>this.nombre = 'Edin';<br><strong>return this;</strong><br><strong>}();</strong></td></tr></tbody></table>

En el último ejemplo hay dos paréntesis después de la declaración de la función. Son muy importantes. Sirven para ejecutar la función y devolver el resultado. Si no los ponemos, nuestra variable a sólo contendrá la definición de la función (es decir, será de tipo Function) y no el objeto que la función devuelve.

image image

¿Y si nos olvidamos de poner return this; dentro de la función? La función devolverá un objeto vacío (porque no hay ninguna clausula return) que no tiene definido el método (mejor dicho, la función) llamado "nombre". Por tanto, el typeof sería undefined. Es muy importante entender y asimilar este punto para evitar sorpresas con JavaScript.

Array

Las matrices (arrays) se definen en JS con un objeto Array o bien con el literal [] separando los elementos con comas**.** Empiezan siempre en la posición 0 y pueden contener elementos de tipos diferentes (no son tipadas). Incluso podemos tener funciones u otros arrays como elementos de un array.

El número de elementos de un array se obtiene con la propiedad length.

var array = **\[**'Edin'**,** 8.123**,** "Kapic"**\]**;  
var longitud\_array = array.**length**;  

Function

Como hemos visto antes, las funciones son mucho más "maleables" en JavaScript que en NET. Hay dos maneras de declarar una función.

La primera y la más frecuente es usar la sintaxis NET y escribir una declaración:

**function miFuncion**(parametros)  
**{**  
  /\* el código de la función \*/  
**}**

La otra manera es asignar la función a una variable como una expresión:

**var miFuncion = function** (parametros)  
**{**  
  /\* el código de la función \*/  
**};**

Notad que cuando usamos una expresión, hay que poner punto y coma al final, ya que no deja de ser una asignación de un tipo de datos Function a una variable que se llama miFuncion.

Las dos maneras son equivalentes pero la primera (la declaración) tiene el efecto no deseado de hoisting. Se trata de que JavaScript automáticamente sube la declaración de la función arriba de todo el código JavaScript (mejor dicho hasta su contenedor más alto). Siempre que veamos una declaración, hay que imaginarse una variable con el nombre de la función, puesta arriba del todo e inicializada.

**/\* codigo \*/ function a() { … }**  es lo mismo que **var a = function() { … }; /\* codigo \*/**

En el ejemplo siguiente se observa el efecto pernicioso de hoisting:

function f() {  
    /\* es como si aquí pusiera: var g = function()… \*/  
    var **g** = 1;   
    **function g()** {  
        alert("hola");  
   **}  
**    g();  
}

La función g() es una declaración, sube por encima de var g y por tanto se pierde al inicializar g con el valor numérico. La ejecución g() no será válida.

Si lo cambiamos a una expresión, entonces la última asignación de g será la de la función y todo funcionará tal como esperamos.

function f() {  
    var g = 1;   
    **var g = function()** {  
        alert("hola");  
  **  }**  
    g();  
}

Variable Hoisting

El maldito hoisting existe también para las variables. Si ponemos var nombre = valor en un código, JavaScript lo va a separar en dos al ejecutar: var nombre arriba del todo y nombre = valor en el sitio donde está la expresión.

var a = 1;  
function abc() {  
  alert(a);  
  **var a = 2;**  
}

devolverá undefined como resultado del alert, ya que es idéntico a este código:

var a = 1;  
function abc() {  
  **var a;** /\* a = undefined \*/  
  alert(a);  
  **a = 2;**  
}

Resumen

Con estos ejemplos espero haber cubierto la base para poder avanzar con JavaScript bien programado. En los próximos posts veremos más características de JavaScript moderno. Hasta entonces, practicad los ejemplos anteriores con JsFiddle y procurad de entender la idiosincrasia del JavaScript.


Profile picture

Written by Edin Kapić Insatiably curious code-writing tinkerer. Geek father. Aviation enthusiast. Cuisine journeyman. Follow me on Twitter