domingo, 12 de diciembre de 2010

POO en Javascript (I)

Este post es el primero de varios en los que pretendo consolidar ciertos conocimientos de javascript en lo relativo a la programación orientada a objetos. Mi intención es dar el salto a una programación de mejor calidad en la parte cliente a través del framework de ASP.NET AJAX. Bien podría comenzar ya, pero quiero seguir una progresión lógica que me llevará (espero) a entender correctamente como ASP.NET AJAX implementa ciertos conceptos (espacios de nombres, herencia, etc.). De este modo, primero postearé hasta donde llego ahora en POO en javascript, para después poder evaluar que me ofrece de más ASP.NET AJAX.

Lo primero es tener claros algunos conceptos de javascript que puede parecer algo confusos, sobre todo porque javascript es un lenguaje “peculiar”, es decir, podrías saber VB.NET o C#, pero enfrentarte a javascript y andar algo perdido.

En javascript, existen los siguientes tipos de datos:

·         number

·         string

·         boolean

·         object

·         function

Se denominan tipos primitivos o básicos a number, string y boolean.

La norma más habitual es encontrar que el tipo de la variable se infiere del valor asignado. Además, se dice que javascript que no es fuertemente tipado, sino que es tipado débil o incluso tipado dinámico, porque una variable puede contener un tipo de valor y acto seguido, contener un tipo totalmente diferente sin que esto produzca un error. Por ejemplo, para declarar un variable de tipo boolean, podemos encontrar el siguiente código:

        // recomenado

        var activo = false;

 

        // no recomendado

        var pasivo = new Boolean(false);

 

Para la variable “activo” se infiere que el tipo de la variable será boolean, mientras que para “pasivo” se utilizar el constructor del objeto global Boolean.

Como podrás imaginar además del objeto global Boolean, también existen los objetos globales Number, String, Object y Function (un objeto global por cada tipo básico). En general, se desaconseja utilizar estos objetos globales en beneficio de la inferencia de tipos. Si quieres ampliar porque mejor “activo” que “pasivo”, te dejo el siguiente enlace Falsy Values en Javascript.

Una cosa que puede “chocarte” (a mí me choca), es que hay un tipo de dato que es function. Se dice que las funciones son objetos de primera categoría. Esto significa que una función (al igual que un número o una cadena) puede asignarse a una variable, puede pasarse como parámetro a otra función, puede ser devuelta como valor de retorno por una función, etc. Por ejemplo:

        function saludar(nombre) {

            alert("Hola " + nombre);

        }

 

        var a = saludar; //asignación, fijarse que no lleva paréntesis

 

        a("sergio"); //llama a saludar("sergio");

 

        //p esperamos que sea una función cuya firma declara un parámetro

        function saludar2(p, nombre) {

            p(nombre);

        }

 

        saludar2(saludar, "sergio"); //pasar una función como parámetro

 

        saludar2(a, "sergio"); //pasar una función como parámetro

 

        function dameFuncion() {

            return function () { alert("Hola"); };

        }

 

        var b = dameFuncion();

        b(); //llamar a la función anónima devuelta por dameFuncion

 

Si hablamos de conocer el tipo de la variable en tiempo de ejecución, te diré es que es muy difícil y a veces casi imposible. Para conocer el tipo de una variable, podremos utilizar los operadores typeof  o instanceOf, pero saber con precisión el tipo resulta complicado.

El operador typeof devuelve el tipo de la variable. Además de poder devolver alguno de los tipos disponibles (“number”, “string”, “boolean”, “object” o “function”), también es muy habitual investigar si devuelve “undefined”, que aunque no es un tipo, es devuelto cuando la variable aún no tiene un tipo asignado (está sin inicializar) o cuando la variable no existe.

Código

typeof

var numero = 35;

number

var numero = new Number(35);

object

var cadena = "Sergio";

string

var cadena = new String("Sergio");

object

var booleano = true;

boolean

var booleano = new Boolean(true);

object

var objeto = new Object();

object

var objeto = {};

object

var fecha = new Date();

object

var vector = new Array();

object

var domEl = document.getElementById("Div1");

object

var jqEl = $("#Div1");

object

function miFuncion() {

}

function

var miFuncion = new Function();

function

var variableSinInicializar;

undefined

“preguntar por una variable no declarada”

undefined

typeof NaN     

number

typeof Infinity      

number

typeof null       

object

typeof undefined

undefined

 

Por otro lado, el operador instanceof nos permite saber si un variable es de un tipo concreto.

//var numero = 35;

//var cadena = "Sergio"

//var booleano = true;

 

/*Para que funcione instanceof con Number, String y Boolean

hay que declarar las variables con el tipo adecuado*/

var numero = new Number(35);

var cadena = new String("Sergio");

var booleano = new Boolean(true);

var objeto = new Object();

var fecha = new Date();

var vector = new Array();

function funcion() {

}

 

if (numero instanceof Number) {

alert("Number"); //ok si se declara new Number

}

if (cadena instanceof String) {

alert("String"); //ok si se declara new String

}

if (booleano instanceof Boolean) {

alert("Boolean"); //ok si se declara new Boolean

}

if (objeto instanceof Object) {

alert("Object"); //ok

}

if (fecha instanceof Date) {

alert("Date"); //ok

}

if (vector instanceof Array) {

alert("Array"); //ok

}

if (funcion instanceof Function) {

alert("Function"); //ok

}

function gato() {

this.nombre = "Chuka";

}

var miGato = new gato();

if (miGato instanceof gato) {

alert("Gato"); //ok

}

var jqEl = $("#Div1");

if (jqEl instanceof jQuery) {

alert("jqEl es jQuery");

}

 

 

Como vemos instanceof permite saber el tipo concreto de una variable. Además nos permite afinar más que typeof, puesto que por ejemplo la clase de usuario gato nos habría devuelto “object” con typeof y sin embargo, con instanceof sabemos no sólo que es un objeto sino que es objeto de tipo gato.

Por otro lado, para trabajar con tipos primitivos o básicos (number, string o boolean) vemos que es más sencillo utilizar typeof puesto que no obliga a declarar la variable como un objeto sino que infiere el tipo de su valor. En realidad, instanceof y typeof son complementarias, y cada una servirá a un propósito distinto en nuestro código, aunque insisto en que esto de conocer el tipo de una variable puede a veces resultar complicado.

Si hablamos de tipos de objetos disponibles en javascript, hay que diferenciar entre los tipos de objetos del propio lenguaje javascript y los tipos de objetos que nos proporciona el navegador a través del DOM. Por ejemplo, tipos de datos que proporciona javascript podrían ser Array, Boolean, Date, Function, Number, Object, String, Math o RegExp. Por otro lado, para los tipos de objetos que nos proporciona el DOM podemos encontrar document o window (entre otros).

var documento = document; //document está disponible como objeto del navegador

var ventana = window; //window está disponible como objeto del navegador

 

A propósito del objeto window hay que decir que todas las variables y funciones que declaremos en nuestro código (y que no estén contenidas en una función, porque una función crea un ámbito nuevo y privado para las variables), realmente pertenecen al objeto window, por lo que se dice que son de ámbito global. Por ello, el siguiente código es correcto:

var nombre = "Sergio";

alert(nombre);

window.alert(window.nombre);

 

Pero a propósito de esto, hay que tener cuidado porque este código también es correcto:

 

        saludar("Sergio");

 

        function saludar(nombre) {

            saludo = "Hola " + nombre; //como no se utiliza var, el ámbito es global

 

            alert(saludo);

        }

 

        alert(window["saludo"]); //la variable saludo está en el ámbito global porque no se declaró con var dentro de la función saludar

 

De este código, la conclusión que se extrae es que es necesario utilizar var para declarar las variables (tanto dentro como fuera de una función), porque si no éstas se declaran en el ámbito global, y ya sabemos que las variables globales no son buenas…

 

Las funciones de javascript globales disponibles son:

 

· encodeURI

· encodeURIComponent

· decodeURI

· decodeURIComponent

· parseInt

· parseFloat

· isNaN

· isFinite

· eval

 

Puede encontrar una referencia completa de estas funciones en http://www.javascriptkit.com/jsref/globalfunctions.shtml

También es necesario saber que cualquier propiedad de un objeto puede ser accedida, bien a través del método convencional (objeto.propiedad), o bien indizando la propiedad como si fuera un array.

alert(document.location.href);

alert(document["location"]["href"]);

 

La segunda sintaxis puede parecer un poco innecesaria, pero en ciertas ocasiones es una característica que nos ayudará a realizar ciertas tareas de forma más eficiente. De hecho, como norma general cuando uno programa se amolda y utiliza las características del lenguaje para llevar a cabo su propósito, así que si javascript permite acceder a propiedades indizándolas ¿Por qué habríamos de no utilizarlo en determinadas circunstancias?

Por ejemplo, ayudándonos de la indexación de propiedades de un objeto, podemos emular una colección de tipo diccionario, ayudándonos también de que javascript es un lenguaje dinámico y permite agregar propiedades a los objetos sin tener una “clase molde” donde aparezcan:

        //declarar un objeto

        var diccionario = {};

 

        //agregar valores, en realidad agregar propiedades dinámicamente

        diccionario["clave1"] = 1;

        diccionario.clave2 = 2;

 

        //mostrar valores, ambas sintaxis son válidas

        alert(diccionario.clave1);

        alert(diccionario["clave2"]);

 

        //eliminar valores, ambas sintaxis son válidas

        delete diccionario.clave1;

        delete diccionario["clave2"];

 

        //agregar valores

        diccionario["clave1"] = 1;

 

        //preguntar por valores, cualquier sintaxis es válida

        if (typeof diccionario.clave1 != "undefined") {

            alert("existe: " + diccionario.clave1);

        }

        if (typeof diccionario["clave2"] != "undefined") {

            alert("existe: " + diccionario.clave2);

        }

        if (diccionario.hasOwnProperty("clave1")) {

            alert("existe: " + diccionario["clave1"]);

        }

        if (diccionario.hasOwnProperty("clave2")) {

            alert("existe: " + diccionario["clave2"]);

        }

 

Por último, aquí dejo algunos enlaces interesantes:

http://www.javascriptkit.com/jsref/

http://www.herongyang.com/JavaScript/Object-Indexed-Property.html

http://es.debugmodeon.com/articulo/arrays-asociativos

Un saludo!.

1 comentario: