Review: Professional JavaScript for Web Developers, Capítulo 6 (Parte II)

30 agosto, 2011 8 comentarios

En la segunda parte del capítulo seis, Nicholas Zaka explica las diferentes formas que tenemos de implementar la herencia en ECMAScript.

Prototype Chaining

La idea consiste en utilizar los prototipos para heredar propiedades y métodos entre dos tipos de referencia. Esto se consigue asignando al prototipo del subtipo una instancia del supertipo antes de añadir las funciones propias al prototipo del subtipo:

function SuperTipo(){
   this.propiedad = true;
}

SuperTipo.prototype.getSuperValor(){
   return this.propiedad;
}

function SubTipo(){
    this.subpropiedad = false;
}

SubTipo.propotype = new SuperTipo();

SubTipo.prototype.getSubValor(){
   return this.subpropiedad;
}

var instancia = new SubTipo();
instancia.getSubValor(); //false
instancia.getSuperValor(); //true

La búsqueda de propiedades funciona igual que se explico en la primera parte del capítulo, remontando la cadena de prototipos. Cuando accedemos a una propiedad primero la busca en la instancia, si no la encuentra la busca en el prototipo (que es una instancia del SuperTipo) y sino la encuentra la busca en el siguiente prototipo (el prototipo de la instancia del SuperTipo). El autor hace hincapié en que el último prototipo de la cadena es el prototipo de Object, de esta manera podemos acceder desde cualquier instancia a los métodos heredados de Object como toString, valueOf, hasOwnProperty, etc.

Además podemos sobreescribir métodos en el prototipo del subtipo, de manera que las instancias del SuperTipo ejecutarán el método original, y las instancias del subtipo el método sobreescrito.

Problemas de esta aproximación:

  • No se puede utilizar la notación literal para definir el prototipo del subtipo ya que nos cargaríamos la cadena de prototipos.
  • Los valores de referencia en el prototipo son compartidos por todas las instancias, de la misma forma que pasaba en los patrones de creación de objetos.
  • No podemos pasar argumentos al constructor del super tipo.

Constructor Stealing

Para resolver el problema con los valores de referencia y permitir el paso de argumentos al super tipo, podemos llamar al constructor del super tipo en el constructor del subtipo a través de call y apply para establecer el contexto de ejecución dentro del ámbito del sub tipo.

function SuperTipo(propiedad){
   this.numeros = [1,2,3];
   this.propiedad = propiedad;
}

function SubTipo(propiedad){
   SuperTipo.call(this, propiedad);
}

var instancia = new SubTipo("Fran");
instancia.numeros.push(4); //1,2,3,4
instancia.propiedad; //"Fran"

var instancia2 = new SubTipo("Jose");
instancia2.numeros; //1,2,3
instancia.propiedad; //"Jose"

Los problemas de esta aproximación:

  • No podemos acceder a las funciones definidas en el prototipo del supertipo, por lo que deberíamos definir las funciones dentro de la función constructora del supertipo, con los problemas que eso conlleva.

Combination Inheritance

Podemos combinar los dos patrones anteriores para quedarnos con lo mejor de cada aproximación. La idea consiste en utilizar el encadenamiento de prototipos para heredar funciones y el anterior patrón para heredar propiedades.

function SuperTipo(propiedad){
   this.numeros = [1,2,3];
   this.propiedad = propiedad;
}

SuperTipo.prototype.funcionSuperTipo = function(){
   return "SuperTipo";
}

function SubTipo(propiedad){
   SuperTipo.call(this, propiedad);
}

SubTipo.prototype = new SuperTipo();

SubTipo.prototype.funcionSubTipo = function(){
   return "SubTipo";
}

Lo único malo de esta aproximación es que se invoca dos veces al constructor del supertipo. Una vez dentro del constructor del subtipo para heredar las propiedades y otra vez para crear el prototipo del subtipo con una instancia del supertipo.

Prototypal Inheritance

Douglas Crockford introdujo este patrón para implementar la herencia basándose en que para crear nuevos objetos podíamos utilizar los prototipos en vez de definir tipos propios:

function objet(o){
   function F(){}
   F.prototype = o;
   return new F();
}

Esta función crea un constructor F temporal, asigna el objeto o como el prototipo de este constructor temporal, y devuelve una nueva instancia del constructor temporal. De esta forma, si tienes un objeto que tienes que usar como base para otro objeto, solo tienes que pasarselo a esta función y modificar el objeto que te devuelve para crear su subtipo, ya que este objeto que devuelve tiene en su prototipo todas las propiedades del objeto base.

Este patrón tiene el problema al compartir valores de referencia entre prototipos. Según Nicholas Zaka este patrón solamente es útil si no tienes la necesidad de crear constructores, si solo necesitas que un objeto se comporte de manera similar que otro.

Parasitic Inheritance

Otro patrón de Douglas Crockford que consiste en utilizar una función para realizar la herencia y añadirle la funcionalidad del subtipo:

function createSubTipo(base){
   var subtipo = object(base);
   subtipo.funcionSubTipo = function(){
      return "SubTipo";
   }
   return subtipo;
}

Utiliza el patrón anterior para crear un subtipo que herede del objeto base y le define nuevas funciones al subtipo. Otro patrón para utilizar si no necesitas constructores. Aunque este patrón tiene una problemática enorme, no permite reutilizar las funciones.

Parasitic Combination Inheritance

El patrón definitivo para implementar la herencia que resuelve el problema de invocar dos veces el constructor del supertipo en el patrón Combination Inheritance. Para ello usa los patrones de Douglas Crockford para asignar al prototipo del subtipo una copia del prototipo del supertipo.

function heredarPrototipo(subTipo, superTipo){
    var prototype = object(superTipo.prototype); 
    //Creamos el objeto copia del prototipo del supertipo
    prototype.constructor = subTipo; 
    //Añadimos como constructor del prototipo al subtipo
    subTipo.prototype = protoype; 
    //Añadimos el nuevo prototipo al prototipo del subtipo
} 

function SuperTipo(propiedad){ 
   this.numeros = [1,2,3]; 
   this.propiedad = propiedad; 
} 

SuperTipo.prototype.funcionSuperTipo = function(){
   return "SuperTipo";
} 

function SubTipo(propiedad){
    SuperTipo.call(this, propiedad); 
} 

SubTipo.prototype = heredarPrototipo(SubTipo, SuperTipo); 
//De esta manera evitamos invocar dos veces el constructor SuperTipo 

SubTipo.prototype.funcionSubTipo = function(){ 
   return "SubTipo"; 
}
Categorías:JavaScript, Libros

Review: Professional JavaScript for Web Developers, Capítulo 6 (Parte I)

30 agosto, 2011 Deja un comentario

Nicholas Zaka dedica todo el capítulo seis a explicar como utilizar la orientación a objetos en JavaScript. Desde el primer momento remarca la inexistencia del concepto de clases y explica varias técnicas que han surgido para la creación de objetos y la herencia. También cubre los prototipos y su relación con la orientación a objetos.

En JavaScript un objeto es una colección desordenada de pares clave-valor, así que podemos pensar en ellos como tablas hashes. La manera más sencilla de crear un objeto es crear una nueva instancia de Object y añadirle propiedades y métodos. Esta aproximación es muy pobre debido a la cantidad de código duplicado que necesitaríamos cada vez que quisiéramos crear un nuevo objeto.

El patrón Factory

Este patrón abstrae el proceso de creación de objetos. Como no existe el concepto de clases, podemos crear una función que encapsule la creación de objetos:

function createPerson(name, age){
   var o = new Object();
   o.name = name;
   o.age = age;
   o.sayName = function(){
      alert(this.name);
   }
   return o;
}

Esta aproximación sigue siendo pobre ya que no podemos identificar el tipo referencia del objeto creado, ya que creamos el objeto a partir del tipo base Object.

El patrón Constructor

Además de los constructores nativos como Object y Array podemos definir nuestros propios constructores. Un constructor es una función a la que invocamos con el operador new para crear una instancia de un tipo específico.

function Person(name, age){
    this.name = name;
    this.age = age;
    this.sayName = function(){
       alert(this.name);
    }
}

var persona = new Person("Fran", 23);
persona.sayName(); //"Fran"

Cuando invocamos esta función con el operador new se crea un nuevo objeto; se asigna el ámbito del constructor al nuevo objeto (por lo que this apunta al nuevo objeto); se ejecuta el código añadiendo propiedades y funciones; y se devuelve el objeto que se ha creado. De esta forma podemos identificar facilmente el tipo del objeto creado con el operador instanceof o con la propiedad que tienen todos los objetos, constructor (propiedad que mantiene una referencia al constructor):

persona instanceof Person; //true
persona.constructor == Person; //true
persona instanceof Object; //true ya que todos los objetos heredan de Object

El inconveniente de esta aproximación es que los métodos son creados cada vez que se instancia un nuevo objeto. Logicamente dado que las funciones en JavaScript son objetos, dos objetos distintos creados con el mismo constructor mantienen referencias distintas a sus métodos:

persona1.sayName == persona2.sayName; //false

Aunque el método sea el mismo, se crea dos veces el objeto function y cada uno tiene una posición distinta en memoria, no mantienen las mismas referencias.

El patrón Prototype

Cada función tiene una propiedad prototype que contiene todas las propiedades y métodos que comparten y están disponibles para todos los objetos de un tipo de referencia específico. Podemos asignar todas las propiedades y métodos que queramos compartir entre todos los objetos para evitar el problema anterior:

function Person(){
}

Person.prototype.name = "Fran";
Person.prototype.age = 23;
Person.prototype.sayName = function(){
   alert(this.name);
}

var persona1 = new Person();
persona1.sayName(); //"Fran"

var persona2 = new Person();
persona2.sayName(); //"Fran"

El objeto prototype tiene una propiedad constructor que mantiene una referencia a la función de la cuál es su prototipo, por lo que Person.prototype.constructor apunta a Person. Cada instancia que se crea con el constructor tiene una propiedad que apunta a su prototipo; esta propiedad __proto__ puede ser accedida vía script en algunas implementaciones.

Cuando accedemos a una propiedad de un objeto, primero busca la propiedad en la instancia misma del objeto; si no la encuentra busca la propiedad en su prototipo. Si definimos una propiedad de instancia en un objeto con el mismo nombre que una propiedad de su prototipo, se oculta la propiedad del prototipo pero no se cambia, por lo que las demás instancia seguirán teniendo el valor del prototipo:

persona1.name = "Jose";
alert(persona1.name); //"Jose"
alert(persona2.nombre); //"Fran"

La única forma de volver a acceder a la propiedad del prototipo es borrando la propiedad de la instancia que tiene el mismo nombre con el operador delete:

delete persona1.name;
alert(persona1.name); //"Fran"

Todos los objetos cuentan con una función hasOwnProperty() que devuelve true solo si una propiedad es de instancia (si la hemos definido en el propio objeto) y false si la propiedad proviene de su prototipo. En cambio el operador in devuelve true si encuentra la propiedad en su instancia o en su prototipo:

alert("name" in persona2); //true;
alert(persona2.hasOwnProperty("name")); //false

El prototipo es un objeto, por lo que podemos utilizar la notación literal para definirlo. Aún así tenemos que tener cuidado y recordar que el prototipo tiene una propiedad constructor que apunta a la función constructora de la cual es su prototipo, por lo que si utilizamos la notación literal tendríamos que añadir la propiedad para seguir manteniendo la referencia:

function Person(){
}

Person.prototype = {
    constructor: Person,
    name : "Fran",
    age : 23,
    sayName : function () {
        alert(this.name);
    }
};

Los cambios en un prototipo se reflejan en las instancias ya creadas, excepto cuando sobreescribimos el prototipo con uno nuevo utilizando la notación literal. Esto es así porque cada objeto mantiene una propiedad __proto__ que referencia a su prototipo.

Podemos añadir nuevas funciones a los prototipos de los tipos nativos, pero no es una buena práctica según el autor.

Para acabar la parte de los prototipos, el autor comenta el problema que tienen, y es que compartir propiedades de tipo referencia entre prototipos puede tener consecuencias inesperadas:

function Person(){
}

Person.prototype = {
    constructor: Person,
    name : "Fran",
    age : 23,
    friends : ["Jose", "Alex"],
    sayName : function () {
        alert(this.name);
    }
};

var persona1 = new Person();
var persona2 = new Person();
persona1.friends.push("Estef");

alert(persona1.friends); //"Jose,Alex,Estef"
alert(persona2.friends); //"Jose,Alex,Estef"

alert(persona1.friends === persona2.friends); //true

Combinación patrón Constructor/Prototype

Combinando los dos patrones podemos aprovechar sus ventajas y ahorrarnos los inconvenientes del patrón Constructor. De esta forma definimos lo propio a cada instancia en la función constructora, y lo global a todas ellas en el prototipo.

La última parte del capítulo analiza las técnicas para implementar herencia en JavaScript.

Categorías:JavaScript, Libros

Review: Professional JavaScript for Web Developers, Capítulo 5 (Parte II)

29 agosto, 2011 Deja un comentario

El tipo RegExp

ECMAScript soporta expresiones regulares a través del tipo RegExp. No voy a entrar mucho en detalle sobre las expresiones regulares pero son muy parecidas a las expresiones regulares de PERL.

Podemos crear una experesión regular utilizando el constructor RegExp o la siguiente sintaxis:

var expresion = new RegExp("patron", "banderas");
var expresion = /patrón/banderas

Las banderas (flags) indican de que forma se ejecutará la expresión regular. Hay tres modos:

  • g: Indica el modo global, significa que se buscarán coincidencias con el patrón en toda la cadena y no parará después de encontrar la primera coincidencia.
  • i: Indica modo case-insensitive. No se tiene en cuenta las diferencias entre mayúsculas y minúsculas.
  • m: Indica el modo multilinea. Significa que el patrón seguirá buscándose en textos multilineas, aunque llegue al final de una línea.

Para el patrón existen un conjunto de carácteres ( [ { \ ^ $ | ) ? * + . que tienen un significado especial; por ejemplo el carácter ^ significa al principio por lo que la siguiente expresión regular /^a/ coincidiría con todas las cadenas que tengan una a al principio. El autor no explica el significado de cada carácter especial pero significa lo mismo que en las expresiones regulares de PERL.

Para ejecutar la expresión regular sobre una cadena basta con invocar al método exec sobre el patrón; nos devolverá un array con todas las coincidencias que haya encontrado:

var patron = /^a/g;
var texto = "abcde";
var coincidencias = patron.exec(texto);

Si lo que queremos es saber si una cadena pasa una determinada expresión regular podemos utilizar el método test() que devuelve true si hay al menos una coincidencia y false en caso contrario.

if(patron.test(texto)){
    alert("El patrón coincide");
}

El tipo Function

Las funciones actúan como objetos. Cada función que definimos es una instancia del tipo Function. Tenemos varias forma de definir una función:

function sumar(num1, num2){
    return num1+num2;
}

var sumar = function(num1, num2){
    return num1+num2;
};

De la segunda forma no hay nombre incluido despues de la palabra clave function, ya que la función puede ser referenciada a través de la variable sumar. También hay que fijarse que en el segundo caso hay un punto y coma ya que se trata de una inicialización de una variable.

Existe otra forma de definir una función utilizando el constructor Function pero no se recomienda por ser menos legible y tener menos rendimiento.

Lo importante de todo esto es tener claro que las funciones son objetos, por lo que el nombre de una función no es más que un puntero al objeto Function. De esta forma podemos copiar funciones en otras variables como haciamos con los objetos:

var hola = function(){
   return "Hola";
}

hola(); //devuelve "Hola"

var otroSaludo = hola;

otroSaludo(); //Devuelve "Hola";

Importante es notar que utilizamos el nombre de la variable solamente, ya que si utilizaramos los paréntesis estaríamos invocando la función y asignando a la variable otraFuncion el resultado “Hola” y no el puntero al objeto Function.

Ahora podemos entender mejor porque no existe la sobrecarga de funciones en JavaScript. Ya que los nombres son punteros al objeto Function, estaríamos sobreescribiendo el valor al definirla dos veces con el mismo nombre, por lo que la función válida sería la última en definirse.

Dado que las funciones son objetos, podemos utilizar las funciones como parámetros de una función, e incluso devolver una función como resultado de otra función:

function unaFunction(otraFuncion, argumento){
    return otraFuncion(argumento);
}

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

var dameFuncion = devolverFuncion(); //en dameFuncion tenemos un objeto Function
dameFuncion(); //"Hola"

Todas las funciones tienen dos objetos especiales, arguments y this. El autor ya habló de arguments en capítulos anteriores pero no había nombrado que el objeto arguments posee una propiedad llamada callee que es un puntero a la función que contiene el propio objeto arguments:

function numero(num){
   alert(num);
   if(num == 0){
     return 0;
   } else {
     return arguments.callee(num-1);
   }
}

El otro objeto especial es this que es una referencia al objeto donde la función está operando – o mejor dicho, el ámbito en el cual la función ha sido ejecutada. Cuando definimos una función en el ámbito global, el objeto this apunta a window.

window.nombre = "Fran";
var objeto = {
    nombre: "Jose"
};

function saludar(){
    alert(this.nombre);
}

saludar(); //"Fran" dado que la función se ha definido en el contexto global, this apunta a window.
window.saludar(); //Es exactamente lo mismo que lo anterior, dado que todo lo que definimos en el contexto global pertenece al objeto window

objeto.saludar = saludar; //Asignamos la función saludar al objeto
objeto.saludar(); //"Pepe" dado que ahora la función apunta al objeto que la invoca

La función global saludar() y la función del objeto apuntan al mismo objeto Function, pero se ejecutan en contextos diferentes. De ahí deducimos que el valor de this no se sabe hasta que la función se invoca y se determina el contexto de ejecución donde se está invocando.

Como las funciones son objetos tienen propiedades y métodos. Cada función tiene dos propiedades, length que indica el número de argumentos de la definición de la función, y prototype, la parte más interesante del núcleo de ECMAScript. La propiedad prototype indica la localización actual de todos los métodos de instancia para los tipos de referencia; esto significa que métodos como toString() y valueOf() existen en el prototipo y son accedidas desde todas las instancias de un objeto. Esta propiedad es muy importante y la explicará mejor en el siguiente capítulo.

También cuentan con dos funciones, apply() y call(). Estos métodos llaman a la función dentro de un ámbito.
El método apply() acepta dos argumentos, el ámbito en el que se ejecutará la función, y un array de argumentos.
El método call() tiene el mismo objetivo, pero se invocan de manera diferente, el primer argumento indica el ámbito en el que se ejecutará la función, los siguientes argumentos serán los argumentos con los que se invoque la función.

Con estas dos funciones podemos aumentar el ámbito en el que la función se ejecutará:

window.nombre = "Fran";
var objeto = {
    nombre : "Jose"
};

function saludar(){
    alert(this.nombre);
}

saludar.call(this); //"Fran", dado que el objeto this apunta al objeto global window
saludar.call(window); //"Fran", dado que el objeto this apunta a window
saludar.call(objeto); //"Jose", dado que se ejecuta en el ámbito del objeto

Wrappers de tipo primitivo

En ECMAScript existen wrappers (envoltorios) de los tipos primitivos String, Number y Boolean. Podemos crear nuevas instancias a través del operador new:

var booleano = new Boolean(false);
var numero = new Number(23);
var cadena = new String("Fran");

Pero hay que tener ciertos aspectos en cuenta. Por ejemplo:

if(new Boolean(false)){
    alert("Entra dentro del if");
}

Esto es así, porque si recordamos, cualquier objeto que no sea null se transforma en un booleano true a través de la función casting Boolean (coerción de tipos), incluso aunque sea un objeto de tipo Boolean con valor false.

Los objetos de tipo referencia que envuelven a los valores primitivos cuentan con una serie de métodos que podemos utilizar. De entro los más interesantes podemos destacar todo lo relevante a la manipulación de cadenas y números.

Objetos preconstruidos

Por último, tenemos los objetos preconstruidos Math y Global.

El objeto Global es un atajo para métodos y propiedades. Todo lo que definimos globalmente se convierten en propiedades del objeto Global; funciones como isNaN(), isFinite(), parseInt(), parseFloat() pertenecen al objeto Global. También pertenecen otros métodos como encodeURI() y encondeURIComponent(), utilizados para codificar URIs; decodeURI() y decodeURIComponent() para decodificar URIs, eval() para interpretar y ejecutar código.

La mayoría de navegadores implementan todo esto como parte del objeto window. Todas las variables y funciones declaradas en el ámbito global forman parte como propiedades del objeto window.

El objeto Math se utiliza para ejecutar operaciones matemáticas y es muy similar al objeto Math de otros lenguajes como Java.

Por donde y hacia donde vamos

De momento llevamos cinco capítulos resumidos (unas 200 páginas del libro), donde hemos visto muchas características importantes del lenguaje. Estudiando estos capítulos he entendido perfectamente la importancia de los contextos de ejecución, del valor que toma el objeto this dentro de las funciones, y como utilizar apply y call para invocar funciones dentro de un ámbito. El siguiente capítulo está dedicado a la orientación a objetos en JavaScript, como instanciar objetos, herencia, prototipos, etc. La verdad es que estudiar así un libro es mucho mejor, resumirlo después de leerse cada capítulo ayuda a aclarar las ideas y a entender profundamente el lenguaje. Sobretodo probando ejemplos en el navegador e intentando jugar un poco con ellos. Voy un poco más lento de lo que me gustaría, dado que quiero leerme muchos libros en lo que me queda de año, pero creo que es mejor leerlos en profundidad a darles una pasada.

Categorías:JavaScript, Libros

Review: Professional JavaScript for Web Developers, Capítulo 5 (Parte I)

29 agosto, 2011 1 comentario

El capítulo cinco cubre todos los detalles de los tipos de referencia que incluye JavaScript, como Object y Array. El autor discute todos los tipos de referencia incluidos en ECMA-262 y como se relacionan con su implementación en los navegadores.

También lo voy a separar en dos entradas para no hacerlo muy cansado de leer. La primera parte dedicada a los tipos de referencia Object, Array y Date. La segunda parte dedicada a los tipos RegExp, Function y los wrappers de tipos primitvos.

Un valor de tipo referencia es una instancia de un determinado tipo de referencia.  En ECMAScript los tipos de referencia son estructuras que agrupan datos y funcionalidad, pero no son el equivalente a las clases en los demás lenguajes orientados a objetos.

Los objetos se crean con el operador new seguido de un constructor. Un contructor no es más que una función cuyo objetivo es crear un nuevo objeto:

var objeto = new Object();

EcmaScript incluye nativamente una serie de tipos de referencia.

El tipo Object

El tipo Object es el tipo base. Tenemos dos maneras de crear una instancia de Object, utilizando el constructor como en el ejemplo anterior. O utilizar la notación literal (JSON); que no es más que una especie de atajo para crear un objecto con propiedades y funciones.

var persona = new Object();
persona.nombre = "Fran";
persona.edad = 23;

var persona = {
   "nombre" : "Fran",
   "edad" : 23
}

Según Nicholas c. Zaka, es recomendable utilizar JSON para la creación de objetos, ya que gana mayor legibilidad. Además comenta un truco muy utilizado entre los desarrolladores cuando tenemos funciones con muchos argumentos que pueden ser opcionales; el truco consiste en encapsular todos esos argumentos en un objeto y pasarlo a la función. Aunque recomienda definir por otra parte los argumentos que sean obligatorios.

Podemos acceder a las propiedades de un objeto a través de la notación con el punto o como si fueran arrays.

El tipo Array

Un array es un poco diferente a los arrays de otros lenguajes de programación, ya que no tienen tamaño fijo (crecen dinámicamente) y pueden almacenar distintos tipos de datos en cada una de las posiciones del array.

Podemos crear un array a partir del constructor Array utilizando el operador new como en los tres primeros ejemplos, o mediante la notación JSON:

var numeros = new Array(); //Se crea un array vacío<
var numeros = new Array(20); //Se crea un array con 20 posiciones con el valor undefined
var numeros = new Array(1,2,3); //Se crea un array de tres posiciones con los valores 1, 2 y 3
var numeros = [1,2,3]; //Mismo ejemplo que el anterior utilizando la notación JSON

Cuentan con una serie de métodos y propiedades (como length para calcular el tamaño de un array). Accedemos a sus posiciones a través de los corchetes. Son de índice cero.

Pueden comportarse como una pila utilizando los métodos push para poner elementos al final y pop para extraer el último elemento. Pueden comportarse como una cola utilizando el método shift que extrae el primer elemento.Estas dos operaciones afectan al array original.

Podemos ordernar arrays utilizando el método sort, pero hay que tener en cuenta que este método primero llama a la función casting String() y luego los compara para determinar el orden correcto. Aún así podemos pasarle como argumento a sort una función que compare dos valores y devuelva -1, 0 o 1 dependiendo de si el primero es menor, son iguales o el primero es mayor. Esta operación afecta al array original.

Podemos concatenar arrays utilizando el método concat y separar arrays utilizando el método slice pasándole como argumento la posición inicial y final. Estas dos operaciones no afectan al array original, devuelven un nuevo array concatenado o cortado según la operación realizada.

El autor acaba con el tipo Array explicando un método muy utilizado para insertar elementos en medio del array; splice. Dependiendo de la forma en la que invoquemos podemos desde borrar, insertar y reemplazar elementos. Ahora que conozco esta función me evitaré muchos dolores de cabeza.

Borrar: Utilizando dos argumentos indicamos con el primero el primer elemento a borrar, y con el segundo el número de elementos que se borraran desde el elemento seleccionado.

var colores = ["rojo", "azul", "verde"];
var borrados = colores.splice(0,1); //Desde el primer elemento, borramos uno
alert(borrados); //"rojo"
alert(colores); // "azul,verde"

Insertar: Utilizando tres argumentos indicamos con el primero la posición del array donde insertaremos el elemento, 0 como segundo argumento para indicar que no queremos borrar ningún elemento, y como tercer argumento el elemento a insertar.

var colores = ["rojo", "azul", "verde"];
var borrados = colores.splice(1,0, "negro", "amarillo"); //Desde el elemento en la posición uno que equivale al segundo en un array, borramos cero elementos, e insertamos el elemento negro y después el amarillo
alert(borrados); //string vacío
alert(colores); "rojo, negro, amarillo, azul, verde"

Reemplazar: Podemos reemplazar un elemento si indicamos en el segundo argumento el número de elementos a borrar.

var colores = ["rojo", "azul", "verde"];
var borrados = colores.splice(1,1, "negro", "amarillo");
alert(borrados); "azul"
alert(colores); "rojo,negro,amarillo,verde"

El tipo Date

Personalmente odio el trato con fechas en cualquier lenguaje de programación que he tocado a fondo. Es un engorro cuando tienes que pelearte con diferentes representaciones según el país, diferentes zonas horarias, restar días, minutos, segundos, sincronizar fechas con las diferentes bases de datos, que si timestamp, etc.

ECMAScript incorpora el objeto Date para representar fechas. Para crear una instancia de Date basta con utilizar la función constructora Date junto con el operador new. Si no pasamos ningún parámetro crea una instancia representando el tiempo actual.

var tiempoActual = new Date();

Internamente las fechas se representan como el número de milisegundos que ha pasado desde el uno de enero de 1970, al igual que la mayoría de lenguajes de programación.

Para crear otra fecha distinta al tiempo actual, tenemos que pasarle en milisegundos la representación de la fecha. Para ayudarnos con el proceso, el método parse() del objeto Date nos devuelve el número de milisegundos dada una fecha en un formato (formato que depende mucho de la implementación realizada en el navegador por lo que no es una buena alternativa a utilizar); o el método UTC() que acepta muchos argumentos, el año, mes (en base cero, 0 para Enero, 1 para Febrero), día (1 a 31), horas (0 a 23), minutos, segundos, milisegundos de la fecha.

var otraFecha = new Date(Date.UTC(2011, 7, 23, 18, 24)); // 23 de Agosto del 2011 a las 18:24

De todas formas podemos utilizar la función constructora directamente porque dependiendo de los argumentos pasados internamente invoca al método parse o al método UTC.

Para convertir una fecha a una representación en string tenemos el método toString() o toLocaleString() heredados de Object, pero estos dos método devuelven una representación distinta en cada navegador…

Podemos comparar fechas facilmente, ya que el método valueOf() devuelve el número de milisegundos que representa cada fecha:

var fecha1 = new Date(2011, 0, 1); //1 de Enero de 2011
var fecha2 = new Date(2011, 1, 1); //1 de Febrero de 2011
alert(fecha1 > fecha2); //false;
alert(fecha1 < fecha2); //true;

El objeto Date cuenta con un gran conjunto de métodos para establecer o recuperar diferentes partes de la fecha, getDay(), getMonth(), getMinutes(), getSeconds(), setDay(25), setMonth(3), setMinutes(40), getFullYear(), setFullYear(2011), etc.

Categorías:JavaScript, Libros

Review: Professional JavaScript for Web Developers, Capítulo 4

24 agosto, 2011 3 comentarios

Ya he acabado de estudiarme el capítulo 4 así que vamos con el resumen de las cosas más destacadas que podemos aprender en este capítulo dedicado al ámbito de las variables y el recolector de basura.

Valores primitivos y referencia

En esta parte del capítulo explica la diferencia entre los dos tipos: valores primitivos y valores por referencia.

Los valores primitivos los vimos en la primera parte del capítulo tres y son Undefined, Null, Boolean, Number y String (a diferencia de otros lenguajes que interpretan los strings como objetos, en JavaScript se consideran tipos primitivos). Los valores primitivos tienen un tamaño fijo y se almacenan en la parte de memoria conocida como el stack. Cuando copiamos el valor primitivo de una variable a otra, se crea una copia de ese valor en memoria, por lo que si cambiamos el valor en una de las dos variables la otra no se ve afectada.

var valor1 = 5;
var valor2 = valor1;
valor2 = 3;
alert(valor1); //5 
alert(valor2); //3

Los valores por referencia son objetos, no tienen un tamaño fijo y se almacenan en la parte de memoria conocida como el heap. Una variable que almacena un valor por referencia no es más que un puntero a la dirección de memoria donde está almacenado realmente el objeto. Cuando copiamos un valor por referencia de una variable a otra estamos copiando el valor de la dirección de memoria donde está almacenado el objeto, por lo que si modificamos el objeto almacenado en una variable, los cambios se verán reflejados en el objeto de la otra variable, ya que apuntan al mismo.

var objeto1 = new Object();
var objeto2 = objeto1;
objeto1.nombre = "Fran";
alert(objeto1.nombre); //"Fran"
alert(objeto2.nombre); //"Fran"

El operador typeof viene bien para determinar de que tipo es un valor primitivo, pero cuando lo utilizamos con valores por referencia siempre nos devuelve la cadena “object”. El operador instanceof es el operador indicado para determinar el tipo de referencia de un valor.

var numeros = [1,2,3,4];
alert(numeros instanceof Array); //true
alert(typeof numeros); //"object"

Contexto de ejecución y ámbitos

Todas las variables primitivas y referencia existen dentro de un contento de ejecución (ámbito) que determina el tiempo de vida de una variable y que partes del código pueden acceder a ella.

Los contextos de ejecución existen tanto globalmente (el contexto de ejecución global) como localmente (los contextos locales dentro de las funciones).

Cada vez que se crea un nuevo contexto de ejecución se añade a una cadena de contextos de ejecución donde se pueden buscar variables y funciones. Los contextos que son locales a una función tienen acceso, no solo a las variables en ese contexto, sino también a las pertenecientes a su cadena de contexto y al contexto global.

var nombre = "Fran";
function saludar(){
    alert(nombre); //"Fran"
}

En este código hay dos contextos de ejecución, el contexto global donde definimos una variable llamada nombre y una función llamada saludar; y el contexto local a la función saludar. Podemos utilizar la variable nombre dentro del contexto de la función porque la variable existe dentro de su cadena de contextos (intenta buscarla dentro del contexto local de la función, como no la encuentra, pasa al siguiente contexto en su cadena de contextos). Buscar en contextos superiores tiene un precio, es más rápido cuando se buscan las variables dentro del contexto local.

A diferencia de otros lenguajes, no existe ámbitos diferentes entre sentencias encerradas en bloques, por lo que podemos acceder a las variables fuera de los bloques donde se hayan definidos:

if(true){
   var nombre = "Fran";
}
alert(nombre); //"Fran"

El contexto global solo tiene acceso a las variables y funciones definidas dentro del contexto global y no puede acceder directamente a los datos dentro de contextos locales. Si definimos una variable sin utilizar la palabra var, se convierte en una variable que forma parte del contexto global.

Recolector de basura

La última parte del capítulo está dedicado al recolector de basura. Como otros lenguajes, JavaScript cuenta con un recolector de basura que elimina los datos que no se van a utilizar más de la memoria.

Uno de los algoritmos que utiliza se conoce como mark-and-sweep (algo así como marcar y barrer), que lo que hace es marcar los valores que nunca más se utilizarán para que podamos utilizar esa memoria.

Otro de los algoritmos es el contador por referencia, que mantiene una cuenta de cuantas referencias mantiene un determinado valor. Este algoritmo puede causar problemas cuando tenemos en el código referencias circulares:

function referenciaCircular(){
   var objectoA = new Object();
   var objectoB = new Object();
   objectoA.unObjeto = objectoB;
   objectoB.otroObjecto = objectoA;
}

El objeto A y B nunca serán marcados para la recolección. Eliminar una referencia marcandola a null ayuda a evitar referencias circulares y al recolector de basura, que permite reclamar esa memoria.

function solucion(){
   var objectoA = new Object();
   var objectoB = new Object();
   objectoA.unObjeto = objectoB;
   objectoB.otroObjecto = objectoA;
   objetoA = null; objetoB = null; 
}
Categorías:JavaScript, Libros

Review: Professional JavaScript for Web Developers, Capítulo 3 (Parte II)

24 agosto, 2011 5 comentarios

Seguimos con el review del capítulo tres. Le toca el turno a los operadores, sentencias y funciones.

El estándar describe un conjunto de operadores que podemos usar para manipular los datos de una variable. Cualquiera con conocimiento de un lenguaje de programación conoce los operadores matemáticos básicos, como la suma (+), resta (-), multiplicación (*), división(/) y módulo(%), por lo que los operadores matemáticos me los voy a saltar, al igual que los de desplazamiento de bits, más que nada por no alargar mucho el review del capítulo.

OPERADORES.

Operadores Incremento y Decremento

Los operadores de incremento y decremento se utilizan para añadir o restar uno a un valor numérico. Estos operadores no sólo trabajan con enteros, pueden trabajar con cadenas, booleanos, valores de coma flotante y objetos a través de la coerción de tipos:

  • Cuando se utilizan con una cadena que tiene una representación de un número, convierte el número y aplica el cambio.
  • Cuando se utiliza con una cadena que no tiene una representación válida de un número, la variable coge el valor NaN.
  • Cuando se utiliza con un valor booleano false, convierte el valor a cero y aplica el cambio.
  • Cuando se utiliza con un valor booleano true, convierte el valor a uno y aplica el cambio.
  • Cuando se usa con un valor de coma flotante, añade o resta uno.
  • Cuando se usa con un objeto, llama al método valueOf() o para coger un valor con el que trabajar. Aplica las otras reglas. Si el resultado es NaN, llama al método toString() y vuelve a aplicar las reglas otra vez.

En todo caso, la variable se convierte a un valor numérico.

var edad = 23;
edad++;
alert(edad); //24
Operadores booleanos

Como todo lenguaje de programación, ECMAScript cuenta con un conjunto de operadores booleanos.

El operador NOT (!) niega el valor de un booleano; sino se utiliza un booleano, primero se convierte y después lo niega. Aquí puede entrar en juego la coerción de los demás tipos de datos a un booleano, por ejemplo, si aplicamos el operador NOT a un valor null, nos devolverá true. Esto es así porque el valor null se evalúa como false, y el operador NOT, niega el valor convirtiéndolo en true.

var encontrado = false;
alert(!encontrado); //true;

El operador AND (&&) se aplica a dos valores y devuelve true si los dos valores booleanos son true; con que un valor se evalúe a false, el operador AND devuelve false. El operador AND puede utilizarse con cualquier tipo de datos, no solo valores booleanos; pero hay que tener en cuenta que si uno de los dos valores no es un booleano no siempre devolverá un booleano. Por ejemplo, si uno de los dos operadores es null, undefined o NaN, el operador AND devolverá null, undefined o NaN en cada caso.

var encontrado = true;
var ultimo = true;
alert(encontrado && ultimo); //true

El operador OR (||) se aplica a dos valores y devuelve true si algún valor es true; solo devuelve false cuando los dos valores se evalúan a false. Al igual que el operador AND, puede que no devuelve un booleano si alguno de los dos valores no es un booleano. Con valores como null, NaN y undefined se comporta de la misma manera. Pero tiene algunas cosas importantes a destacar. Por ejemplo, si el primer valor es un objeto, entonces se devuelve ese objeto. Si el primer valor se evalúa a false, entonces se devuelve el segundo valor. Y si ambos valores son objetos, se devuelve el primero.

Debido a este comportamiento, se suele utilizar el operador OR para establecer valores por defecto a las variables:

var objeto = objetoPreferido || objetoPorDefecto;

De esta forma, se evaluará objetoPreferido primero; y si se evalúa a false (si la variable contiene null), se devolverá el segundo operando, el objetoPorDefecto.

Operadores relacionales

Los operadores relaciones comparan valores, menor que (<), menor o igual que (<=), mayor que (>), mayor o igual que (>=). Cada uno de estos operadores devuelve un valor booleano, true si se cumple la condición, false si no se cumple.

Algunas cosas importantes a destacar, si los dos operadores son números se hace una comparación numérica. Si los dos valores son strings, se comparan carácter a carácter. Si algún valor es un objeto, se llama al método valueOf() para obtener un valor con el que trabajar. Si valueOf() no está disponible se llama al método toString(), es decir, aplica la coerción de tipos para convertir un objeto en su equivalente booleano.

alert(5>3); //true
alert(a>b); //false
Operadores de igualdad

Existen dos tipos de operadores de igualdad en ECMAScript. Tenemos por un lado el operador de igualdad == que devuelve true si los dos operados son iguales, y desigualdad != que devuelve true si los dos operandos son distintos. Pero estos operadores permiten comparar tipos de datos distintos ya que se tiene en cuenta la coerción de tipos para compararlos.

Algunas cosas a tener en cuenta:

  • Si un operando es un valor Boolean, lo convierte en un valor numérico siguiendo las reglas que vimos, 1 es el equivalente a true y 0 el equivalente a false.
  • Si un operando es un string y el otro es un número, se convierte el string en su equivalente numérico.
  • Si uno de los operandos es un objeto, se invoca al método valueOf() para coger un valor primitivo con el que trabajar, si valueOf() no está disponible se invoca el método toString().
  • Los valores null y undefined son iguales.
  • Si uno de los operandos es NaN se devuelve false con el de igualdad y true con el de desigualdad. Si los dos valores son NaN se devuelve false con el operador de igualdad porque NaN no es equivale a Nan.
  • Si los dos operadores son objetos, se comparan para ver si son el mismo objeto, es decir, si ambos apuntan al mismo objeto. Si son objetos iguales pero no apuntan a la misma dirección de mmoria devuelve false.

Tabla de ejemplos extraída del libro:

null == undefined true
“NaN” == NaN false
5 == NaN false
NaN == NaN false
NaN != NaN true
false == 0 true
true == 1 true
true == 2 false
undefined == 0 false
null == 0 false
“5” == 5 true

Por otro lado tenemos los operadores idénticamente iguales === y idénticamente desiguales !== que funcionan de manera similar al anterior pero sin ejercer la coerción de datos. Es decir no compara tipos diferentes, el operador de idénticamente igual siempre devolverá false si evalúa operandos de distinto tipo.

Un ejemplo que muestra la diferencia más importante con las conversiones de tipo:

55 === "55" false
55 == "55" true

La verdad, es un lío el tema de la coerción de tipos en ECMAScript, pero con la práctica no es muy difícil entender como funciona el mecanismo de conversiones.

Sentencias

Esta parte del capítulo describe las sentencias para controlar el flujo de la aplicación; tanto condicionales (if…else, switch), como repetitivas, (while, do-while, for, for-in, etc). Vamos a ver algunas cosas interesantes sobre estas estructuras.

Las condiciones no hacen falta que sean booleanos, como en la mayoría de lenguajes orientados a objetos, ya que automáticamente se transforma a un booleano utilizando la función casting Boolean(). La estructura for-in se utiliza para recorrer todas las propiedades de un objeto. Las propiedades de un objeto no tienen orden, por lo que no se puede predecir en que pasada se utilizará cada propiedad del objeto.

for(var propiedad in objeto){
   ....
}

Las palabras reservadas break y continue tienen la misma función que en los demás lenguajes. La palabra break obliga a salir del bucle, continue salta a la siguiente pasada del bucle.

La sentencia with se utiliza para establecer el ámbito del código  en un objeto en concreto.  Si tenemos el siguiente código para acceder a la URL de la página:

var url = location.href;

Utilizando with con el ámbito en el objeto location, podemos utilizar sus propiedades sin referirnos al objeto location:

with(location){
var url = href;
}

El autor del libro no recomienda utilizar esta sentencia porque tiene un impacto negativo de rendimiento y dificulta la depuración del código encerrado dentro de la sentencia.

La sentencia switch compara los valores con el operador de igualdad idéntica, por lo que no se produce ningún tipo de casting o coerción de tipos. Además en los case podemos utilizar cualquier expresión, sin necesidad de utilizar ninguna constante.

Funciones

Las funciones en JavaScript no tienen mucho que ver con la de otros lenguajes de programación.

  • Devueven undefined si no especificamos un valor a devolver.
  • No se preocupan de cuantos argumentos pasemos. Definir una función con dos argumentos no significa que solo podamos pasarle dos argumentos. Esto es así porque los argumentos de una función vienen representados como un array internamente. Pero hay que tener en cuenta que los argumentos que hayamos definido valdrán undefined si no los pasamos.
  • Podemos acceder a los argumentos de una función a través del objeto arguments que actúa como un array (podemos obtener el número de argumentos a través de la propiedad length de arguments), aunque realmente no es una instancia de Array.
  • Todos los argumentos se pasan por valor, no por referencia.
  • No existe la sobrecarga de funciones como en otros lenguajes de programación. Si se definen dos funciones con el mismo nombre se tiene en cuenta la última, aunque el número de argumentos sea diferente.
Categorías:JavaScript, Libros

Review: Professional JavaScript for Web Developers, Capítulo 3 (Parte I)

17 agosto, 2011 5 comentarios

El capítulo tres explica las características básicas del lenguaje; es uno de los capítulos más largos del libro, así que voy a resumirlo muy brevemente porque la mayoría son cosas que ya sabe cualquier persona con un mínimo de conocimientos de JavaScript.

Sintaxis

El primer concepto es entender que el lenguaje es case-sensitivy – o lo que es lo mismo, existe diferencia entre mayúsculas y minúsculas – tanto en los nombres de las variables, funciones, operadores. Por lo que una variable que se llame nombre es diferente a una llamada Nombre.

Al igual que otros lenguajes existen unas reglas para definir los identificadores (o lo que es lo mismo, los nombres de las variables, funciones, propiedades, etc) y diferentes formatos para escribir comentarios (formato de una línea //, y formato de múltiples líneas encerrados entre /* */).

Las sentencias en JavaScript acaban con un punto y coma; aunque no es obligatorio es recomendable por un motivo muy importante. Las herramientas que comprimen el código JavaScript suelen quitar todos los espacios y saltos de línea, por lo que se producirían errores de sintaxis. De la misma forma se pueden combinar múltiples sentencias en bloques de código encerradas entre llaves {}. No es obligatorio las llaves cuando solo hay una sentencia pero se recomienda también su uso.

Palabras claves y reservadas

Como todo lenguaje existe un conjunto de palabras que tienen un objetivo especial y que no se pueden utilizar como identificadores.

Variables

Las variables en ECMAScript tienen un tipado blando, lo que significa que pueden almacenar diferentes tipos de datos. Para definir una variable utilizamos la palabra clave var seguido del nombre de la variable:

var nombre;

Cuando no inicializamos la variable, toma el valor undefined. Pero también podemos inicializar la variable cuando la definimos. Además podemos cambiar el tipo de la variable aunque inicialmente haya sido utilizada para almacenar un string.

var nombre = "Francisco";
nombre = 10;

No es recomendable cambiar el tipo de datos de una variable.

Lo más importante de esta sección es destacar que el operador var define una variable local al ámbito en el que ha sido definida. Es posible definir variables globales omitiendo la palabra var pero no se recomienda en absoluto ya que son mucho más dificiles de mantener y pueden causar confusión.

function hola(){
    var saludo = "hola"; //variable local
    alert(saludo);
}
hola(); //"hola"
alert(saludo); //undefined

function hola(){
    saludo = "hola"; //variable global
    alert(saludo);
}
hola(); //"hola"
alert(saludo); //"hola"

Para no estar utilizando todo el rato el operador var, podemos definir e inicializar tantas variables como queramos con una sola sentencia:

var nombre = "Fran",
edad = 23,
esChico = true;

Tipos de datos

En ECMAScript solamente hay cinco tipos de datos primitivos, Undefined, Null, Boolean, Number y String. Hay otro tipo de dato más complejo, Object, que es una lista de pares clave-valor. Con estos seis tipos de datos podemos representar todos los datos que imaginemos.

Al ser un lenguaje de tipado blando necesitamos determinar de alguna manera el tipo de una variable. El operador typeof nos devuelve un string indicando el tipo de la variable:

  • “undefined” si el valor es undefined
  • “boolean” si el valor es un Boolean
  • “string” si el valor es un string
  • “number” si el valor es un número
  • “object” si el valor es un objeto o null
  • “function” si el valor es una función

Un ejemplo de su uso:

var nombre = "Fran";
alert(typeof nombre); //"string"
El tipo undefined

El tipo Undefined solo tiene un posible valor, undefined. Cuando declaramos una variable pero no la inicializamos, se asigna el valor undefined. También podemos guardar el valor undefined en una variable pero no es una buena práctica.

Tenemos que tener en cuenta que una variable con el valor undefined es muy diferente de una variable que ni siquiera se haya declarado, ya que si utilizamos una variable que no ha sido definida causará un error. De todas formas el operador typeof devuelve undefined también para aquellas variables que no hayan sido declaradas.

El tipo null

El tipo null solo tiene un posible valor, null. Su valor indica un puntero a un objeto vacío, por lo que el operador typeof devuelve “object” cuando lo utilizamos con una variable que contiene el valor null.

El valor undefined es un valor derivado de null:

alert(null == undefined); //true

Aunque son valores relacionados, tienen diferentes usos. Nunca deberíamos establacer explicitamente el valor undefined a una variable. Si esperamos un objeto que todavía no está disponible, deberíamos de utilizar null en vez de undefined.

El tipo Boolean

Como cualquier persona con conocimientos de programación sabrá, el tipo boolean puede almacenar dos valores literales, true o false. Estos dos literales son case-sensitive por lo que True o False no son valores válidos para representar un booleano.

Para mí, lo más interesante de esta parte del capítulo es la conversión de los diferentes tipos de datos a un valor booleano, ya sea por medio del casting:

var nombre = "Fran";
var nombreComoBooleano = Boolean(nombre);

como por medio de la conversión automática, por ejemplo en sentencias condicionales:

var nombre = "Fran";
if(nombre){
     ....
}

Es importante saber que los siguientes valores se evaluarán como false: el mismo valor false, un string vacío “”, el número 0 y NaN (Not a Number), null y undefined. Cualquier otro valor se evaluará como true, incluido los números negativos, positivos, cadenas no vacías, el literal true y cualquier objeto.

El tipo Number

El tipo Number en ECMAScript permite almacenar cualquier número, ya sea en coma flotante según el formato IEEE 754, como números en distintas bases, decimal, octal o hexadecimal.

Como nota importante para evitar futuros problemas, destacar el problema de la aritmética en coma flotante. Por ejemplo al sumar 0.1 y 0.2 el resultado es 0.30000000000004 y no 0.3 como podemos esperar. Este problema no ocurre solo en ECMAScript, sino en todos los lenguajes que usen el formato IEEE 754.

El tipo Number tiene restricciones de memoria, el número más pequeño que podemos almacenar está almacenado en Number.MIN_VALUE, y el número más grande en Number.MAX_VALUE. Cualquier número que se salga de este rango automaticamente se ttrasnforma en un valor especial, Infinity. Para determinar si un valor es finito, podemos utilizar la función isFinite() que devuelve true solo si el número está entre el mínimo y el máximo valor.

NaN (Not a Number), es un valor númerico especial que indica que el valor almacenado no es válido, no es un número. En otros lenguajes de programación cuando dividimos por 0 causa un error de ejecución, en ECMAScript, dividir por 0 devuelve el valor NaN. Podemos utilizar la función isNaN() para determinar si un valor no es un número válido.

Al igual que con los booleanos, tenemos varias funciones para convertir cualquier tipo de dato a un valor numérico. La función casting Number() acepta cualquier tipo de datos e intenta devolver un número que lo represente, por ejemplo para el booleano false, devuelve 0. Es más recomendable utilizar las funciones parseInt() y parseFloat() aunque solo acepten strings.

El tipo String

El tipo String, al igual que otros lenguajes, representan un conjunto de carácteres de 16 bits Unicode. Pueden marcarse utilizando comillas dobles o simples, aunque no es como en PHP, no hay diferencia en como se interpreta una o otra, son exactamente iguales.

var nombreDoble = "Fran";
var nombreSimple = 'Fran';

Podemos utilizar la barra \ como carácter de escape:

var nombre = "Fran\nBelmonte"

Podemos hacer uso de la propiedad length para calcular el tamaño de un string:

nombre.length

Son inmutables, por lo que una vez creados su valor no puede cambiar. Si queremos cambiar el valor, debemos sustituir el valor de la variable con un nuevo string. Podemos utilizar el operador + para concatenar cadenas:

var nombre = "Fran";
nombre = nombre + "cisco";

Podemos utilizar toString() para convertir cualquier tipo de datos en una cadena:

var edad = 23;
var edadComoString = edad.toString(); //Devuelve el string "23"

Si el valor es null o undefined el método toString() no está disponible, por lo que podemos utilizar la función casting String() donde si el valor tiene disponible el método toString() lo utiliza, si el valor es null devuelve “null” y si el valor es undefined devuelve “undefined”.

Y hasta aquí la primera parte del capítulo, en la segunda parte explica los operadores, sentencias condicionales, bucles y funciones. Lo resumiré muy brevemente, que el capitulo cuatro es mucho más interesante.

Categorías:JavaScript, Libros