Esta traducción está incompleta. Por favor, ayuda a traducir este artículo del inglés.
JavaScript 1.7 es una actualización del lenguaje que le añade algunas nuevas características, como generadores, iteradores, comprensión de arrays, sentencias let
y asignación desestructurada. Evidentemente también incluye todas las características de JavaScript 1.6.
El soporte para JavaScript 1.7 estará disponible a partir de Firefox 2 Beta 1, así como en compilaciones actuales.
Los ejemplos de código incluidos en este artículo pueden ser probados en la consola JavaScript. Si quieres aprender a construir o utilizar esta consola, lee Introducción al shell de JavaScript.
Utilizando JavaScript 1.7
Para usar las nuevas caracteristicas de JavaScript 1.7, es necesario especificar explícitamente el uso de JavaScript 1.7. En HTML o XUL, utiliza:
<script type="application/javascript;version=1.7"/>
Cuando se utilice el shell de JavaScript, debes especificar la versión deseada utilizando la función version()
:
version(170);
Generadores e iteradores
Cuando se desarrolla código que involucra algoritmos iterativos (como iteraciones sobre listas, o cálculos repetitivos sobre el mismo conjunto de datos), con frecuencia hay variables de estado cuyos valores necesitan ser mantenidos durante el proceso. Tradicionalmente se utilizan funciones callback para obtener los valores intermedios de un algoritmo iterativo.
Generadores
Considera este algoritmo iterativo que obtiene los números de la serie Fibonacci:
function do_callback(num) { document.write(num + "<BR>\n"); } function fib() { var i = 0, j = 1, n = 0; while (n < 10) { do_callback(i); var t = i; i = j; j += t; n++; } } fib();
Este código utiliza rutinas callback para realizar las operaciones en cada paso del algoritmo. En este caso, cada numero Fibonacci es impreso en la consola.
Los generadores e iteradores trabajan juntos para proveer nuevas y mejores formas de hacer esto. Veamos cómo es la rutina de la serie Fibonacci escrita utilizando generadores:
function fib() { var i = 0, j = 1; while (true) { yield i; var t = i; i = j; j += t; } } var g = fib(); for (var i = 0; i < 10; i++) { document.write(g.next() + "<BR>\n"); }
La función que contiene la palabra clave yield
es un generador. Cuando es llamada, sus parámetros formales son instanciados a los argumentos actuales, pero su cuerpo no es realmente evaluado, si no que se devuelve un
generator-iterator
. Cada llamada al método next()
del
generator-iterator
realiza otro paso a través de algoritmo iterativo. El valor de cada paso es el valor especificado por la palabra clave yield
. Piensa en yield
como la versión
generator-iterator
de return
que delimita la frontera entre cada iteración del algoritmo. Cada vez que se llama a next()
, el código del generador continúa a partir de la sentencia que va después de yield
.
Se realiza un ciclo en el generator-iterator llamando repetidamente a su método next()
hasta que se consiga la condición deseada. Con este ejemplo se pueden obtener tantos números de Fibonacci como se quiera llamando simplemente a g.next()
hasta que conseguir la cantidad de números que se quiera.
Resumiendo un generador en un punto específico
Una vez que un generador ha sido iniciado por la invocación de su función next()
, puede utilizarse send()
, pasando un valor específico que será tratado como el resultado de la última producción. El generador entonces retornará el operando de la subsecuente producción.
No se puede iniciar un generador en un punto arbitrario; deberá comenzarse con next()
antes de poder enviarle [send()
] un valor específico.
send(undefined)
es equivalente a invocar next()
. Sin embargo, iniciando la aparición de un nuevo generador con cualquier otro valor que no sea indefinido [undefined] cuando invoque a send()
resultará como una excepción de error de tipo [TypeError
exception].Excepciones en los generadores
Puede forzar a un generador a lanzar una excepción mediante la invoación de su método throw()
, pasándole el valor de la excepción que debe ser lanzada. Esta excepción se lanzará desde el conxtexto actual suspendido del generador, así como si el yield
que está actualmente suspendido en lugar del sentencia throw
valor
.
Si una producción no es encontrada durante el procesamiento de la excepción lanzada, entonces la excepción se propagará ascendentemente hasta la invocación del throw()
y subsecuentemente invocará a next()
que resulta en el lanzamiento de una StopIteration
.
Cerrando un generador
Los generadores poseen el método close()
que forza al generador a cerrarse por sí mismo. Los efectos de cerrar un generador son:
- Cualquier cláusula
finally
activa en la función del generador es ejecutada. - Si una cláusula
finally
lanza cualquier excepción distinta aStopIteration
, la excepción es propagada hacia el invocador del métodoclose()
. - El generador termina.
Ejemplo de un Generador
Este código maneja un generador que se producirá cada 100 lazos.
var gen = generator(); function driveGenerator() { if (gen.next()) { window.setTimeout(driveGenerator, 0); } else { gen.close(); } } function generator() { while (i < something) { /** stuff **/ ++i; /** 100 loops per yield **/ if ((i % 100) == 0) { yield true; } } yield false; }
Iteradores
Un iterador es un objeto especial que te permite iterar datos.
En el uso cotidiano, los objetos iteradores son
invisibles
(no se necesita trabajar con ellos explícitamente) y son usados en sentencias for...in
y for each...in
para recorrer de forma natural las claves y/o los valores de los objetos.
var objectWithIterator = getObjectSomehow(); for (var i in objectWithIterator) { document.write(objectWithIterator[i] + "<BR>\n"); }
Si se está implementando un objeto iterador personalizado o se necesita manipular directamente un iterador, será preciso conocer antes el método next()
, la excepción StopIteration
y la propiedad __iterator__
.
traduzco getter como la función get Se puede crear un iterador para un objeto llamando a Iterator(
objectname
)
. El iterador para cierto objeto se conoce gracias a la propiedad __iterator__
de dicho objeto, la cual implementa predeterminadamente la iteración de acuerdo al modelo convencional de for..in
y for each...in
. Si se desea utilizar un iterador personalizado, se debería sobreescribir la función get para que __iterator__
devuelva una instancia del susodicho iterador. Para obtener el iterador de un objeto en un script se debería de usar Iterator(
obj
)
en lugar de acceder directamente a la propiedad __iterator__
.
Una vez se tiene el iterador, se puede buscar fácilmente el siguiente elemento en el objeto llamando su método next()
. Cuando no hay más datos se lanza la excepción StopIteration
.
A continuación se muestra un ejemplo simple de manipulación directa de iteradores:
var obj = {name:"Jack Bauer", username:"JackB", id:12345, agency:"CTU", region:"Los Angeles"}; var it = Iterator(obj); try { while (true) { document.write(it.next() + "<BR>\n"); } } catch (err if err instanceof StopIteration) { document.write("End of record.<BR>\n"); } catch (err) { document.write("Unknown error: " + err.description + "<BR>\n"); }
La salida de este programa tendrá este aspecto:
name,Jack Bauer username,JackB id,12345 agency,CTU region,Los Angeles End of record.
Opcionalmente se puede especificar un segundo parámetro durante la creación del iterador, el cual es un valor booleano que indica si sólo se quieren obtener las claves cada vez que se llama al método next()
o no. Cambiando var it = Iterator(obj);
por var it = Iterator(obj, true);
en el ejemplo anterior se obtendría la siguiente salida:
name username id agency region End of record.
En ambos casos, el orden final en el que los datos son devueltos puede variar dependiendo de la implementación por lo que no se garantiza el orden de los datos.
Los iteradores son un método útil de recorrer los datos de los objetos, incluyendo objetos que pueden contener datos inesperados. Esto puede ser especialmente útil si se necesitan recuperar datos para los que la aplicación no está preparada.
Comprensión de arrays
La comprensión de arrays es una forma de utilizar generadores para realizar de manera apropiada potentes inicializaciones de arrays. Por ejemplo:
function range(begin, end) { for (let i = begin; i < end; ++i) { yield i; } }
range()
es un generador que devuelve todos los valores entre <tt>begin</tt> y <tt>end</tt>. Una vez definido eso, se puede utilizar así:
var diez_cuadrados = [i * i for (i in range(0, 10))];
Esto pre-inicializa un nuevo array, diez_cuadrados, para que contenga los cuadrados de los valores del rango 0..9
.
Se puede usar cualquier sentencia condicional al inicializar un array. Si lo que se quiere es inicializar un array para que contenta los números pares entre 0 y 20, se puede usar este código.
var pares = [i for (i in range(0, 21)) if (i % 2 == 0)];
Antes de JavaScript 1.7, lo anterior debería haber sido codificado así:
var evens = []; for (var i=0; i <= 20; i++) { if (i % 2 == 0) evens.push(i); }
La comprensión de arrays no sólo es mucho más compacta sino que de hecho es mucho más fácil de leer una vez que nos hemos familiarizado con el concepto.
Reglas de ámbito
La comprensión de arrays utiliza un bloque implícito en el cual se ubica. Dicho bloque contiene todo el contenido que se halla dentro de los corchetes, además del las declaraciones let
implícitas.
''Add details.''
Ámbito de un bloque con let
Existen varias formas en las que let
puede ser usado para manejar el ámbito de un bloque de datos y funciones:
- La sentencia
let
proporciona un método de asociar valores con variables, constantes y funciones en el ámbito del bloque, sin afectar a los de las variables que tengan el mismo nombre fuera del bloque. - La expresión
let
permite establecer variables cuyo ámbito está comprendido en una única expresión. - La definición
let
define variables, constantes y funciones cuyo ámbito queda restringido al bloque en el que se han definido. Esta sintaxis es muy parecida a la usada paravar
. - Además se puede utilizar
let
para establecer variables que existan sólo dentro del contexto de un buclefor
.
El sentencia let
La sentencia let
proporciona un ámbito local para variables, constantes y funciones. Funciona reservando cero o más variables en el ámbito léxico de un único bloque de código. La validez de la sentencia let
finaliza cuando termina el bloque.
Por ejemplo:
var x = 5; var y = 0; let (x = x+10, y = 12) { document.write(x+y + "<BR>\n"); } document.write(x+y + "<BR>\n");
tendrá como salida:
27 5
Las reglas para el bloque de código son las mismas que para cualquier otro bloque de código de JavaScript. Puede tener sus propias variables establecidas usando declaraciones let
.
let
, será necesario incluir los paréntesis. El no incluirlos provocará un error de sintaxis.Reglas de ámbito
El ámbito de las variables definidas usando let
es el del mismo bloque de let
, además de cualquier bloque interno contenido dentro de él, a menos que esos bloques definan variables con el mismo nombre.
Expresiones let
Se puede usar let
para establecer variables cuyo ámbito comprende sólo una única expresión:
var x = 5; var y = 0; document.write( let(x = x + 10, y = 12) x+y + "<BR>\n"); document.write(x+y + "<BR>\n");
La salida da como resultado:
27 5
En este caso, el ámbito de las variables x = x+10
e y = 12
es utilizado solamente en la expresión x+y
Reglas de ámbito
Dada la expresión let
let (decls) expr
existe un bloque creado implícitamente que comprende el trozo expr.
Definiciones let
La palabra clave let
puede además ser usada para definir variables, constantes y funciones dentro de un bloque.
** Este código no funciona en FF 2.0 b1. ** if (x > y) { let const k = 37; let gamma : int = 12.7 + k; let i = 10; let function f(n) { return (n/3)+k; } return f(gamma) + f(i); }
Reglas de ámbito
Las variables, funciones y constantes declaradas usando let
, let function
y let const
tienen como ámbito el bloque en el que están definidas, además de cualquier sub-bloque en el que no sean redefinidas. De este modo, let
funciona como var
.
En los programas y clases, let
no crea propiedades en los objetos y clases globales como hace var
. En vez de eso, crea propiedades en un bloque implícito creado para la evaluación de sentencias en dichos contextos. Esto significa esencialmente que let
no sobreescribirá las variables previamente definidas usando var
. Por ejemplo:
** No funciona en FF 2.0 b1. Devuelve "42", no "global". var x = 'global'; let x = 42; document.write(this.x + "<BR>\n");
La salida mostrada por este código será "global", no "42".
Un bloque implícito es aquel que no está comprendido entre llaves; es creado implícitamente por el motor de JavaScript.
En las funciones, una sentencia let
ejecutada dentro de eval()
no crea propiedades en el objeto variable Esto hay que traducirlo: (activation object or innermost binding rib) como sí hace var
. En vez de eso, lo hace en un bloque creado implícitamente para la evaluación de las sentencias del programa. Esto es consecuencia de la forma de trabajar de eval()
unido a la anterior regla.
En otras palabras, cuando se usa eval()
para ejecutar código, dicho código es es tratado como un programa independiente el cual tiene su propio bloque implícito alrededor de su código.
Ámbito de variables con let
en bucles for
Se puede usar la palabra reservada let
para declarar variables localmente en el ámbito de un bucle for
, al igual que con var
.
** Add obj ** var i=0; for ( let i=i ; i < 10 ; i++ ) document.write(i + "<BR>\n"); for ( let [name,value] in obj ) document.write("Name: " + name + ", Value: " + value + "<BR>\n");
Reglas de ámbito
for (let expr1; expr2; expr3) sentencia
En este ejemplo, expr2, expr3 y sentencia están delimitadas por un bloque implícito que contiene a las variables locales al bloque declaradas por let expr1
. Esto se demuestra en el primer bucle del ejemplo.
for (expr1 in expr2) sentencia
En este caso, existe un bloque implícito que contiene a sentencia. Esto es mostrado en el segundo bucle del ejemplo.
Asignación desestructurada
La asignación desestructurada hace posible extraer datos desde arrays u objetos utilizando una sintaxis que refleja la construcción de arrays y objetos literales.
Las expresiones de objetos y arrays literales proporcionan una forma fácil de crear paquetes de datos ad hoc. Una vez creados estos paquetes de datos, pueden ser usados como se quiera. Se pueden devolver incluso desde funciones.
Una peculiaridad especialmente útil que se puede hacer con la asignación desestructurada es leer una estructura completa desde una única sentencia aunque hay un número de cosas interesantes que se pueden hacer con ella, como muestra la siguiente sección repleta de ejemplos.
Esta capacidad es similar a las características que presentan lenguajes tales como Perl o Python.
Ejemplos
La asignación desestructurada se explica mejor con ejemplos por lo que aquí se muestran un par de ellos con fines didáctico.
Intercambiando valores
Se puede usar la asignación desestructurada para, por ejemplo, intercambiar valores:
var a = 1; var b = 3; [a, b] = [b, a];
Tras ejecutar este código, b valdrá 1 y a valdrá 3.
O para rotar valores: (formato de código pobre)
<body bgcolor = "black"> <script type="application/javascript;version=1.7"/> var a = 'o'; var b = "<font color = 'green'>o</font>"; var c = 'o'; var d = 'o'; var e = 'o'; var f = "<font color = 'blue'>o</font>"; var g = 'o'; var h = 'o'; for (lp=0;lp<40;lp++) {[a, b, c, d, e, f, g, h] = [b, c, d, e, f, g, h, a]; document.write(a+''+b+''+c+''+d+''+e+''+f+''+g+''+h+''+"<br />");} </script>
Después de ejecutar este código, se mostrará un espectáculo de colores gracias a la rotación de las variables.
Devolviendo múltiples valores
Gracias a la asignación desestructurada, las funciones pueden devolver múltiples valores. Dado que las funciones siempre han podido devolver funciones, ésto proporciona una vuelta de tuerca a la flexibilidad.
function f() { return [1, 2]; }
Como se puede observar, los resultados se devuelven usando una notación parecida a la utilizada con los arrays, con los valores que se quieren devolver encerrados entre corchetes. Así, se puede devolver un número cualquiera de resultados. En el siguiente ejemplo, f()
devuelve el valor [1, 2]
.
var a, b; [a, b] = f(); document.write ("A es " + a + " B es " + b + "<BR>\n");
El comando [a, b] = f()
asigna el resultado de la función a las variables ubicadas por orden entre corchetes: a queda establecido a 1 y b a 2.
También se pueden obtener los valores devueltos como un array:
var a = f(); document.write ("A es " + a);
En este caso, a será un array que contendrá los valores 1 y 2.
Ignorar ciertos valores devueltos
Se pueden ignorar algunos valores devueltos en los que no se esté interesado:
function f() { return [1, 2, 3]; } var [a, , b] = f(); document.write ("A is " + a + " B is " + b + "<BR>\n");
Tras ejecutar este código, a valdrá 1 y b, 3. El valor 2 es ignorado.
Iteración sobre objetos
Se puede usar asignación desestructurada para recuperar datos de un objeto.
var obj = { nombre: "Bob", puntos: 1.5, edad: 35 }; for (let[nombre, valor] in obj) { document.write ("Nombre: " + nombre + ", Valor: " + valor + "<BR>\n"); }
Este bucle recorre todos los pares clave/valor del objeto obj y muestra sus nombres y valores. En este caso, la salida será algo así:
Nombre: nombre, Valor: Bob Nombre: puntos, Valor: 1.5 Nombre: edad, Valor: 35
Iteración sobre valores en arrays de objetos
Se puede iterar un array de objetos, accediendo a los campos que interesen de cada objeto.
var personas = [ { nombre: "Mike Smith", familia: { madre: "Jane Smith", padre: "Harry Smith", hermana: "Samantha Smith" }, edad: 35 }, { nombre: "Tom Jones", familia: { madre: "Norah Jones", padre: "Richard Jones", hermano: "Howard Jones" }, edad: 25 } ]; for each (let {nombre: n, familia: { padre: f } } in personas) { document.write ("Nombre: " + n + ", Padre: " + f + "<BR>\n"); }
Esto copia el valor del campo nombre a n y el del campo familia.padre a f y luego los muestra por pantalla. Esto se hace para cada objeto del array personas. La salida será algo así:
Nombre: Mike Smith, Padre: Harry Smith Nombre: Tom Jones, Padre: Richard Jones
Categorías enlaces interwikis