diff --git a/files/es/web/javascript/about_javascript/index.html b/files/es/web/javascript/about_javascript/index.html deleted file mode 100644 index 489247ef6fb97a..00000000000000 --- a/files/es/web/javascript/about_javascript/index.html +++ /dev/null @@ -1,60 +0,0 @@ ---- -title: Acerca de JavaScript -slug: Web/JavaScript/About_JavaScript -tags: - - JavaScript -translation_of: Web/JavaScript/About_JavaScript -original_slug: Web/JavaScript/Acerca_de_JavaScript ---- -
{{JsSidebar}}
- -

¿Qué es JavaScript?

- -

JavaScript® (a menudo abreviado como JS) es un lenguaje ligero, interpretado y orientado a objetos con funciones de primera clase, y mejor conocido como el lenguaje de programación para las páginas Web, pero {{Interwiki("wikipedia", "JavaScript#Otras_características", "también se utiliza en muchos entornos que no son de navegador")}}. Es un lenguaje de scripts que es dinámico, multiparadigma, {{Interwiki("wikipedia", "Programación_basada_en_prototipos", "basado en prototipos")}} y admite estilos de programación orientados a objetos, imperativos y funcionales.

- -

JavaScript se ejecuta en el lado del cliente de la web, y se puede utilizar para estilizar/programar cómo se comportan las páginas web cuando ocurre un evento. JavaScript es un potente lenguaje de scripts y fácil de aprender, ampliamente utilizado para controlar el comportamiento de las páginas web.

- -

Contrariamente a la creencia popular, JavaScript no es "Java interpretado". En pocas palabras, JavaScript es un lenguaje de scripts dinámico que admite {{JSxRef("../Guide/Details_of_the_Object_Model", "construcción de objetos basada en prototipos", "#Lenguajes_basados_en_clases_vs._basados_en_prototipos")}}. Intencionalmente, la sintaxis básica es similar a Java y C++ para reducir la cantidad de conceptos nuevos necesarios para aprender el lenguaje. Construcciones del lenguaje, como las declaraciones if, los bucles for y while, y switch y los bloques try...catch funcionan igual que en esos lenguajes (o casi).

- -

JavaScript puede funcionar como un {{JSxRef("../Introduction_to_Object-Oriented_JavaScript", "lenguaje orientado a objetos")}} y {{Interwiki("wikipedia", "Programación_por_procedimientos", "procedimental")}}. Los objetos se crean mediante programación en JavaScript, adjuntando métodos y propiedades a objetos que de otro modo en tiempo de ejecución estarían vacíos, a diferencia de las definiciones de clases sintácticas comunes en lenguajes compilados como C++ y Java. Una vez que se ha construido un objeto, se puede utilizar como plano (o prototipo) para crear objetos similares.

- -

Las capacidades dinámicas de JavaScript incluyen la construcción de objetos en tiempo de ejecución, listas de parámetros variables, variables de función, creación dinámica de scripts (a través de {{JSxRef("Objetos_globales/eval", "eval")}}, introspección de objetos (a través de for...in) y recuperación de código fuente (los programas JavaScript pueden descompilar los cuerpos de las funciones en su texto fuente).

- -

Para una explicación más profunda de la programación de JavaScript, sigue los enlaces recursos de JavaScript a continuación.

- -

¿Qué implementaciones de JavaScript están disponibles?

- -

El proyecto Mozilla proporciona dos implementaciones de JavaScript. El primer JavaScript fue creado por Brendan Eich en Netscape, y a partir de entonces se ha actualizado para cumplir con ECMA-262 Edición 5 y versiones posteriores. Este motor, cuyo nombre en código es {{web.link("/es/docs/Mozilla/Projects/SpiderMonkey", "SpiderMonkey")}}, está implementado en C/C++. El motor {{web.link("/es/docs/Rhino", "Rhino")}}, creado principalmente por Norris Boyd (también en Netscape) es una implementación de JavaScript escrita en Java. Al igual que SpiderMonkey, Rhino también es compatible con ECMA-262 Edition 5.

- -

Con el transcurso del tiempo, y tras varias importantes optimizaciones del entorno de ejecución como TraceMonkey (Firefox 3.5), JägerMonkey (Firefox 4) e IonMonkey se agregaron al motor de JavaScript SpiderMonkey. El trabajo siempre está en curso para mejorar el rendimiento de ejecución de JavaScript.

- -

Más allá de las implementaciones anteriores, existen otros motores JavaScript populares como:—

- - - -

Cada uno de los motores de JavaScript de Mozilla expone una API pública que los desarrolladores de aplicaciones pueden utilizar para integrar JavaScript en su software. Hasta ahora, el entorno de alojamiento más común para JavaScript son los navegadores web. Los navegadores web suelen utilizar la API pública para crear objetos del anfitrión responsables de reflejar el DOM en JavaScript.

- -

Otra aplicación común para JavaScript es como lenguaje de programación de lado del servidor (Web). Un servidor web JavaScript expondría objetos del anfitrión que representan una solicitud HTTP y objetos de respuesta, que luego podría manipular un programa JavaScript para generar páginas web dinámicamente. Node.js es un ejemplo popular de esto.

- -

Recursos de JavaScript

- -
-
{{web.link("/es/docs/Mozilla/Projects/SpiderMonkey", "SpiderMonkey")}}
-
Información específica sobre la implementación de JavaScript de Mozilla en el motor C/C++ (también conocido como SpiderMonkey), incluye cómo integrarlo en aplicaciones.
-
- -
-
{{web.link("/es/docs/Rhino", "Rhino")}}
-
Información específica para la implementación de JavaScript escrita en Java (también conocido como Rhino).
-
{{JSxRef("../Language_Resources", "Recursos del lenguaje")}}
-
Punteros a estándares JavaScript publicados.
-
{{JSxRef("../A_re-introduction_to_JavaScript", "Una reintroducción a JavaScript")}}
-
{{JSxRef("../Guide", "Guía de JavaScript")}} y {{JSxRef("../Referencia", "Referencia de JavaScript")}}.
-
- -

JavaScript® es una marca comercial o una marca comercial registrada de Oracle en EE. UU. y otros países.

diff --git a/files/es/web/javascript/about_javascript/index.md b/files/es/web/javascript/about_javascript/index.md new file mode 100644 index 00000000000000..424927bd90bb53 --- /dev/null +++ b/files/es/web/javascript/about_javascript/index.md @@ -0,0 +1,54 @@ +--- +title: Acerca de JavaScript +slug: Web/JavaScript/About_JavaScript +tags: + - JavaScript +translation_of: Web/JavaScript/About_JavaScript +original_slug: Web/JavaScript/Acerca_de_JavaScript +--- +{{JsSidebar}} + +## ¿Qué es JavaScript? + +**JavaScript**® (a menudo abreviado como **JS**) es un lenguaje ligero, interpretado y orientado a objetos con [funciones de primera clase](https://en.wikipedia.org/wiki/First-class_function), y mejor conocido como el lenguaje de programación para las páginas Web, pero {{Interwiki("wikipedia", "JavaScript#Otras_características", "también se utiliza en muchos entornos que no son de navegador")}}. Es un lenguaje de scripts que es dinámico, multiparadigma, {{Interwiki("wikipedia", "Programación_basada_en_prototipos", "basado en prototipos")}} y admite estilos de programación orientados a objetos, imperativos y funcionales. + +JavaScript se ejecuta en el lado del cliente de la web, y se puede utilizar para estilizar/programar cómo se comportan las páginas web cuando ocurre un evento. JavaScript es un potente lenguaje de scripts y fácil de aprender, ampliamente utilizado para controlar el comportamiento de las páginas web. + +Contrariamente a la creencia popular, **JavaScript _no_ es "Java interpretado"**. En pocas palabras, JavaScript es un lenguaje de scripts dinámico que admite {{JSxRef("../Guide/Details_of_the_Object_Model", "construcción de objetos basada en prototipos", "#Lenguajes_basados_en_clases_vs._basados_en_prototipos")}}. Intencionalmente, la sintaxis básica es similar a Java y C++ para reducir la cantidad de conceptos nuevos necesarios para aprender el lenguaje. Construcciones del lenguaje, como las declaraciones `if`, los bucles `for` y `while`, y `switch` y los bloques `try...catch` funcionan igual que en esos lenguajes (o casi). + +JavaScript puede funcionar como un {{JSxRef("../Introduction_to_Object-Oriented_JavaScript", "lenguaje orientado a objetos")}} y {{Interwiki("wikipedia", "Programación_por_procedimientos", "procedimental")}}. Los objetos se crean mediante programación en JavaScript, adjuntando métodos y propiedades a objetos que de otro modo **en tiempo de ejecución** estarían vacíos, a diferencia de las definiciones de clases sintácticas comunes en lenguajes compilados como C++ y Java. Una vez que se ha construido un objeto, se puede utilizar como plano (o prototipo) para crear objetos similares. + +Las capacidades dinámicas de JavaScript incluyen la construcción de objetos en tiempo de ejecución, listas de parámetros variables, variables de función, creación dinámica de scripts (a través de {{JSxRef("Objetos_globales/eval", "eval")}}, introspección de objetos (a través de `for...in`) y recuperación de código fuente (los programas JavaScript pueden descompilar los cuerpos de las funciones en su texto fuente). + +Para una explicación más profunda de la programación de JavaScript, sigue los enlaces [recursos de JavaScript](#recursos_de_javascript) a continuación. + +## ¿Qué implementaciones de JavaScript están disponibles? + +El proyecto Mozilla proporciona dos implementaciones de JavaScript. El primer JavaScript **fue creado** por Brendan Eich en Netscape, y a partir de entonces se ha actualizado para cumplir con ECMA-262 Edición 5 y versiones posteriores. Este motor, cuyo nombre en código es {{web.link("/es/docs/Mozilla/Projects/SpiderMonkey", "SpiderMonkey")}}, está implementado en C/C++. El motor {{web.link("/es/docs/Rhino", "Rhino")}}, creado principalmente por Norris Boyd (también en Netscape) es una implementación de JavaScript escrita en Java. Al igual que SpiderMonkey, Rhino también es compatible con ECMA-262 Edition 5. + +Con el transcurso del tiempo, y tras varias importantes optimizaciones del entorno de ejecución como TraceMonkey (Firefox 3.5), JägerMonkey (Firefox 4) e IonMonkey se agregaron al motor de JavaScript SpiderMonkey. El trabajo siempre está en curso para mejorar el rendimiento de ejecución de JavaScript. + +Más allá de las implementaciones anteriores, existen otros motores JavaScript populares como:— + +- [V8](https://code.google.com/p/v8/) de Google , que se utiliza en el navegador Google Chrome y las versiones recientes del navegador Opera. Este también es el motor que utiliza [Node.js](http://nodejs.org). +- [JavaScriptCore](https://www.webkit.org/projects/javascript/index.html) (SquirrelFish/Nitro) utilizado en algunos navegadores WebKit como Apple Safari. +- [Carakan](http://my.opera.com/ODIN/blog/carakan-faq) en versiones antiguas de Opera. +- [Motor Chakra](http://en.wikipedia.org/wiki/Chakra_%28JScript_engine%29) utilizado en Internet Explorer (aunque el lenguaje que implementa formalmente se llama "JScript" para evitar problemas de marcas registradas). + +Cada uno de los motores de JavaScript de Mozilla expone una API pública que los desarrolladores de aplicaciones pueden utilizar para integrar JavaScript en su software. Hasta ahora, el entorno de alojamiento más común para JavaScript son los navegadores web. Los navegadores web suelen utilizar la API pública para crear **objetos del anfitrión** responsables de reflejar el [DOM](http://www.w3.org/DOM/) en JavaScript. + +Otra aplicación común para JavaScript es como lenguaje de programación de lado del servidor (Web). Un servidor web JavaScript expondría objetos del anfitrión que representan una solicitud HTTP y objetos de respuesta, que luego podría manipular un programa JavaScript para generar páginas web dinámicamente. [Node.js](http://nodejs.org) es un ejemplo popular de esto. + +## Recursos de JavaScript + +- {{web.link("/es/docs/Mozilla/Projects/SpiderMonkey", "SpiderMonkey")}} + - : Información específica sobre la implementación de JavaScript de Mozilla en el motor C/C++ (también conocido como SpiderMonkey), incluye cómo integrarlo en aplicaciones. + +- {{web.link("/es/docs/Rhino", "Rhino")}} + - : Información específica para la implementación de JavaScript escrita en Java (también conocido como Rhino). +- {{JSxRef("../Language_Resources", "Recursos del lenguaje")}} + - : Punteros a estándares JavaScript publicados. +- {{JSxRef("../A_re-introduction_to_JavaScript", "Una reintroducción a JavaScript")}} + - : {{JSxRef("../Guide", "Guía de JavaScript")}} y {{JSxRef("../Referencia", "Referencia de JavaScript")}}. + +**JavaScript®** es una marca comercial o una marca comercial registrada de Oracle en EE. UU. y otros países. diff --git a/files/es/web/javascript/closures/index.html b/files/es/web/javascript/closures/index.html deleted file mode 100644 index a84903a6fbb685..00000000000000 --- a/files/es/web/javascript/closures/index.html +++ /dev/null @@ -1,320 +0,0 @@ ---- -title: Closures -slug: Web/JavaScript/Closures -tags: - - Closures - - Guia(2) - - Guía - - JavaScript -translation_of: Web/JavaScript/Closures ---- -
{{jsSidebar("Intermediate")}}
- -

Una clausura o closure es una función que guarda referencias del estado adyacente (ámbito léxico). En otras palabras, una clausura permite acceder al ámbito de una función exterior desde una función interior. En JavaScript, las clausuras se crean cada vez que una función es creada.

- -

Ámbito léxico

- -

Consideremos el siguiente ejemplo:

- -
-
function iniciar() {
-  var nombre = "Mozilla";  // La variable nombre es una variable local creada por iniciar.
-  function mostrarNombre() {  // La función mostrarNombre es una función interna, una clausura.
-    alert(nombre);  // Usa una variable declarada en la función externa.
-  }
-  mostrarNombre();
-}
-iniciar();  
-
- -

La función iniciar() crea una variable local llamada nombre y una función interna llamada mostrarNombre(). Por ser una función interna, esta última solo está disponible dentro del cuerpo de iniciar(). Notemos a su vez que mostrarNombre() no tiene ninguna variable propia; pero, dado que las funciones internas tienen acceso a las variables de las funciones externas, mostrarNombre() puede acceder a la variable nombre declarada en la función iniciar().

- -

Ejecuta el código usando este enlace de JSFiddle y observa que la sentencia alert(), dentro de mostrarNombre(), muestra con éxito el valor de la variable nombre, la cual fue declarada en la función externa. Este es un ejemplo de ámbito léxico, el cual describe cómo un analizador sintáctico resuelve los nombres de las variables cuando hay funciones anidadas. La palabra léxico hace referencia al hecho de que el ámbito léxico se basa en el lugar donde una variable fue declarada para determinar dónde esta variable estará disponible. Las funciones anidadas tienen acceso a las variables declaradas en su ámbito exterior.

- -

Clausuras

- -

Considera el siguiente ejemplo:

- -
function creaFunc() {
-  var nombre = "Mozilla";
-  function muestraNombre() {
-    alert(nombre);
-  }
-  return muestraNombre;
-}
-
-var miFunc = creaFunc();
-miFunc(); 
- -

Si se ejecuta este código tendrá exactamente el mismo efecto que el ejemplo anterior: se mostrará el texto "Mozilla" en un cuadro de alerta de Javascript. Lo que lo hace diferente (e interesante) es que la función externa nos ha devuelto la función interna muestraNombre() antes de ejecutarla.

- -

Puede parecer poco intuitivo que este código funcione. Normalmente, las variables locales dentro de una función sólo existen mientras dura la ejecución de dicha función. Una vez que creaFunc() haya terminado de ejecutarse, es razonable suponer que no se pueda ya acceder a la variable nombre. Dado que el código funciona como se esperaba, esto obviamente no es el caso.

- -

La solución a este rompecabezas es que miFunc se ha convertido en un closure. Un closure es un tipo especial de objeto que combina dos cosas: una función, y el entorno en que se creó esa función. El entorno está formado por las variables locales que estaban dentro del alcance en el momento que se creó el closure. En este caso, miFunc es un closure que incorpora tanto la función muestraNombre como el string "Mozilla" que existían cuando se creó el closure.

- -

Este es un ejemplo un poco más interesante: una función creaSumador:

- -
function creaSumador(x) {
-  return function(y) {
-    return x + y;
-  };
-}
-
-var suma5 = creaSumador(5);
-var suma10 = creaSumador(10);
-
-console.log(suma5(2));  // muestra 7
-console.log(suma10(2)); // muestra 12 
- -

En este ejemplo, hemos definido una función creaSumador(x) que toma un argumento único x y devuelve una nueva función. Esa nueva función toma un único argumento y, devolviendo la suma de x + y.

- -

En esencia, creaSumador es una fábrica de función: crea funciones que pueden sumar un valor específico a su argumento. En el ejemplo anterior utilizamos nuestra fábrica de función para crear dos nuevas funciones: una que agrega 5 a su argumento y otra que agrega 10.

- -

suma5 y suma10 son ambos closures. Comparten la misma definición de cuerpo de función, pero almacenan diferentes entornos. En el entorno suma5, x es 5. En lo que respecta a suma10, x es 10.

- -

Closures prácticos

- -

Hasta aquí hemos visto teoría, pero ¿son los closures realmente útiles? Vamos a considerar sus implicaciones prácticas. Un closure permite asociar algunos datos (el entorno) con una función que opera sobre esos datos. Esto tiene evidentes paralelismos con la programación orientada a objetos, en la que los objetos nos permiten asociar algunos datos (las propiedades del objeto) con uno o más métodos.

- -

En consecuencia, puede utilizar un closure en cualquier lugar en el que normalmente pondría un objeto con un solo método.

- -

En la web hay situaciones habituales en las que aplicarlos. Gran parte del código JavaScript para web está basado en eventos: definimos un comportamiento y lo conectamos a un evento que es activado por el usuario (como un click o pulsación de una tecla). Nuestro código generalmente se adjunta como una devolución de llamada (callback): que es una función que se ejecuta en respuesta al evento.

- -

Aquí está un ejemplo práctico: Supongamos que queremos añadir algunos botones a una página para ajustar el tamaño del texto. Una manera de hacer esto es especificar el tamaño de fuente del elemento body en píxeles y, a continuación, ajustar el tamaño de los demás elementos de la página (como los encabezados) utilizando la unidad relativa em:

- -
body {
-  font-family: Helvetica, Arial, sans-serif;
-  font-size: 12px;
-}
-
-h1 {
-  font-size: 1.5em;
-}
-h2 {
-  font-size: 1.2em;
-} 
- -

Nuestros botones interactivos de tamaño de texto pueden cambiar la propiedad font-size del elemento body, y los ajustes serán aplicados por los otros elementos de la página gracias a las unidades relativas.

- -

Aquí está el código JavaScript:

- -
function makeSizer(size) {
-  return function() {
-    document.body.style.fontSize = size + 'px';
-  };
-}
-
-var size12 = makeSizer(12);
-var size14 = makeSizer(14);
-var size16 = makeSizer(16);
-
- -

size12, size14 y size16 ahora son funciones que cambian el tamaño del texto de body a 12, 14 y 16 pixels, respectivamente. Podemos conectarlos a botones (en este caso enlaces) de la siguiente forma:

- -
document.getElementById('size-12').onclick = size12;
-document.getElementById('size-14').onclick = size14;
-document.getElementById('size-16').onclick = size16;
-
- -
<a href="#" id="size-12">12</a>
-<a href="#" id="size-14">14</a>
-<a href="#" id="size-16">16</a>
-
- -

Emulando métodos privados con closures

- -

Lenguajes como Java ofrecen la posibilidad de declarar métodos privados, es decir, que sólo pueden ser llamados por otros métodos en la misma clase.

- -

JavaScript no proporciona una forma nativa de hacer esto, pero es posible emular métodos privados utilizando closures. Los métodos privados no son sólo útiles para restringir el acceso al código: también proporcionan una poderosa manera de administrar tu espacio de nombres global, evitando que los métodos no esenciales embrollen la interfaz pública de tu código.

- -

Aquí vemos cómo definir algunas funciones públicas que pueden acceder a variables y funciones privadas utilizando closures. A esto se le conoce también como el patrón módulo:

- -
var Counter = (function() {
-  var privateCounter = 0;
-  function changeBy(val) {
-    privateCounter += val;
-  }
-  return {
-    increment: function() {
-      changeBy(1);
-    },
-    decrement: function() {
-      changeBy(-1);
-    },
-    value: function() {
-      return privateCounter;
-    }
-  }
-})();
-
-alert(Counter.value()); /* Muestra 0 */
-Counter.increment();
-Counter.increment();
-alert(Counter.value()); /* Muestra 2 */
-Counter.decrement();
-alert(Counter.value()); /* Muestra 1 */ 
- -

Hay mucho aquí. En los ejemplos anteriores cada closure ha tenido su propio entorno; aquí creamos un único entorno compartido por tres funciones: Counter.increment, Counter.decrement y Counter.value.

- -

El entorno compartido se crea en el cuerpo de una función anónima, que se ejecuta en el momento que se define. El entorno contiene dos elementos privados: una variable llamada privateCounter y una función llamada changeBy. No se puede acceder a ninguno de estos elementos privados directamente desde fuera de la función anónima. Se accede a ellos por las tres funciones públicas que se devuelven desde el contenedor anónimo.

- -

Esas tres funciones públicas son closures que comparten el mismo entorno. Gracias al ámbito léxico de Javascript, cada uno de ellas tienen acceso a la variable privateCounter y a la función changeBy.

- -

En este caso hemos definido una función anónima que crea un contador, y luego la llamamos inmediatamente y asignamos el resultado a la variable Counter. Pero podríamos almacenar esta función en una variable independiente y utilizarlo para crear varios contadores:

- -
var makeCounter = function() {
-  var privateCounter = 0;
-  function changeBy(val) {
-    privateCounter += val;
-  }
-  return {
-    increment: function() {
-      changeBy(1);
-    },
-    decrement: function() {
-      changeBy(-1);
-    },
-    value: function() {
-      return privateCounter;
-    }
-  }
-};
-
-var Counter1 = makeCounter();
-var Counter2 = makeCounter();
-alert(Counter1.value()); /* Muestra 0 */
-Counter1.increment();
-Counter1.increment();
-alert(Counter1.value()); /* Muestra 2 */
-Counter1.decrement();
-alert(Counter1.value()); /* Muestra 1 */
-alert(Counter2.value()); /* Muestra 0 */ 
- -

Ten en cuenta que cada uno de los dos contadores mantiene su independencia del otro. Su entorno durante la llamada de la función makeCounter() es diferente cada vez. La variable del closure llamada privateCounter contiene una instancia diferente cada vez.

- -

Utilizar closures de este modo proporciona una serie de beneficios que se asocian normalmente con la programación orientada a objectos, en particular la encapsulación y la ocultación de datos.

- -

Creando closures en loops: Un error común

- -

Antes de la introducción de la palabra clave let en JavaScript 1.7, un problema común con closures ocurría cuando se creaban dentro de un bucle 'loop'. Veamos el siguiente ejemplo:

- -
<p id="help">Helpful notes will appear here</p>
-<p>E-mail: <input type="text" id="email" name="email"></p>
-<p>Name: <input type="text" id="name" name="name"></p>
-<p>Age: <input type="text" id="age" name="age"></p>
-
- -
function showHelp(help) {
-  document.getElementById('help').innerHTML = help;
-}
-
-function setupHelp() {
-  var helpText = [
-      {'id': 'email', 'help': 'Dirección de correo electrónico'},
-      {'id': 'name', 'help': 'Nombre completo'},
-      {'id': 'age', 'help': 'Edad (debes tener más de 16 años)'}
-    ];
-
-  for (var i = 0; i < helpText.length; i++) {
-    var item = helpText[i];
-    document.getElementById(item.id).onfocus = function() {
-      showHelp(item.help);
-    }
-  }
-}
-
-setupHelp();
-
- -

Ver en el JSFiddle

- -

El array helpText define tres avisos de ayuda, cada uno asociado con el ID de un campo de entrada en el documento. El bucle recorre estas definiciones, enlazando un evento onfocus a cada uno que muestra el método de ayuda asociada.

- -

Si pruebas este código, verás que no funciona como esperabas. Independientemente del campo en el que se haga foco, siempre se mostrará el mensaje de ayuda relativo a la edad.

- -

La razón de esto es que las funciones asignadas a onfocus son closures; que constan de la definición de la función y del entorno abarcado desde el ámbito de la función setupHelp. Se han creado tres closures, pero todos comparten el mismo entorno. En el momento en que se ejecutan las funciones callback de onfocus, el bucle ya ha finalizado y la variable item (compartida por los tres closures) ha quedado apuntando a la última entrada en la lista de helpText.

- -

En este caso, una solución es utilizar más closures: concretamente añadiendo una fábrica de función como se ha descrito anteriormente:

- -
function showHelp(help) {
-  document.getElementById('help').innerHTML = help;
-}
-
-function makeHelpCallback(help) {
-  return function() {
-    showHelp(help);
-  };
-}
-
-function setupHelp() {
-  var helpText = [
-      {'id': 'email', 'help': 'Dirección de correo electrónico'},
-      {'id': 'name', 'help': 'Nombre completo'},
-      {'id': 'age', 'help': 'Edad (debes tener más de 16 años)'}
-    ];
-
-  for (var i = 0; i < helpText.length; i++) {
-    var item = helpText[i];
-    document.getElementById(item.id).onfocus = makeHelpCallback(item.help);
-  }
-}
-
-setupHelp();
-
- -

Ver en el JSFiddle

- -

Esto funciona como se esperaba. En lugar de los tres callbacks compartiendo el mismo entorno, la función makeHelpCallback crea un nuevo entorno para cada uno en el que help se refiere a la cadena correspondiente del array helpText.

- -

Consideraciones de rendimiento

- -

No es aconsejable crear innecesariamente funciones dentro de otras funciones si no se necesitan los closures para una tarea particular ya que afectará negativamente el rendimiento del script tanto en consumo de memoria como en velocidad de procesamiento.

- -

Por ejemplo, cuando se crea un nuevo objeto/clase, los métodos normalmente deberían asociarse al prototipo del objeto en vez de definirse en el constructor del objeto. La razón es que con este último sistema, cada vez que se llama al constructor (cada vez que se crea un objeto) se tienen que reasignar los métodos.

- -

Veamos el siguente caso, que no es práctico pero sí demostrativo:

- -
function MyObject(name, message) {
-  this.name = name.toString();
-  this.message = message.toString();
-  this.getName = function() {
-    return this.name;
-  };
-
-  this.getMessage = function() {
-    return this.message;
-  };
-}
-
- -

El código anterior no aprovecha los beneficios de los closures. Podríamos modificarlo de la siguiente manera:

- -
function MyObject(name, message) {
-  this.name = name.toString();
-  this.message = message.toString();
-}
-MyObject.prototype = {
-  getName: function() {
-    return this.name;
-  },
-  getMessage: function() {
-    return this.message;
-  }
-};
-
- -

Sin embargo, no se recomienda redefinir el prototipo, así que el siguiente ejemplo es aún mejor que el anterior, porque lo que hace es añadir funcionalidad al prototipo existente:

- -
function MyObject(name, message) {
-  this.name = name.toString();
-  this.message = message.toString();
-}
-MyObject.prototype.getName = function() {
-  return this.name;
-};
-MyObject.prototype.getMessage = function() {
-  return this.message;
-};
-
- -

En los dos ejemplos anteriores, todos los objetos comparten el prototipo heredado y no se van a definir los métodos cada vez que se crean de objetos. Ver Detalles del Modelo de Objetos para más información.

diff --git a/files/es/web/javascript/closures/index.md b/files/es/web/javascript/closures/index.md new file mode 100644 index 00000000000000..2baf1b42a94205 --- /dev/null +++ b/files/es/web/javascript/closures/index.md @@ -0,0 +1,339 @@ +--- +title: Closures +slug: Web/JavaScript/Closures +tags: + - Closures + - Guia(2) + - Guía + - JavaScript +translation_of: Web/JavaScript/Closures +--- +{{jsSidebar("Intermediate")}} + +Una clausura o _closure_ es una función que guarda referencias del estado adyacente (**ámbito léxico**). En otras palabras, una clausura permite acceder al ámbito de una función exterior desde una función interior. En JavaScript, las clausuras se crean cada vez que una función es creada. + +## Ámbito léxico + +Consideremos el siguiente ejemplo: + +```js +function iniciar() { + var nombre = "Mozilla"; // La variable nombre es una variable local creada por iniciar. + function mostrarNombre() { // La función mostrarNombre es una función interna, una clausura. + alert(nombre); // Usa una variable declarada en la función externa. + } + mostrarNombre(); +} +iniciar(); +``` + +La función `iniciar()` crea una variable local llamada `nombre` y una función interna llamada `mostrarNombre()`. Por ser una función interna, esta última solo está disponible dentro del cuerpo de `iniciar()`. Notemos a su vez que `mostrarNombre()` no tiene ninguna variable propia; pero, dado que las funciones internas tienen acceso a las variables de las funciones externas, `mostrarNombre()` puede acceder a la variable `nombre` declarada en la función `iniciar()`. + +Ejecuta el código usando [este enlace de JSFiddle](http://jsfiddle.net/xAFs9/3/) y observa que la sentencia `alert()`, dentro de `mostrarNombre()`, muestra con éxito el valor de la variable `nombre`, la cual fue declarada en la función externa. Este es un ejemplo de _ámbito léxico_, el cual describe cómo un analizador sintáctico resuelve los nombres de las variables cuando hay funciones anidadas. La palabra _léxico_ hace referencia al hecho de que el ámbito léxico se basa en el lugar donde una variable fue declarada para determinar dónde esta variable estará disponible. Las funciones anidadas tienen acceso a las variables declaradas en su ámbito exterior. + +## Clausuras + +Considera el siguiente ejemplo: + +```js +function creaFunc() { + var nombre = "Mozilla"; + function muestraNombre() { + alert(nombre); + } + return muestraNombre; +} + +var miFunc = creaFunc(); +miFunc(); +``` + +Si se ejecuta este código tendrá exactamente el mismo efecto que el ejemplo anterior: se mostrará el texto "Mozilla" en un cuadro de alerta de Javascript. Lo que lo hace diferente (e interesante) es que la función externa nos ha devuelto la función interna ` muestraNombre`` () `antes de ejecutarla. + +Puede parecer poco intuitivo que este código funcione. Normalmente, las variables locales dentro de una función sólo existen mientras dura la ejecución de dicha función. Una vez que `creaFunc()` haya terminado de ejecutarse, es razonable suponer que no se pueda ya acceder a la variable `nombre`. Dado que el código funciona como se esperaba, esto obviamente no es el caso. + +La solución a este rompecabezas es que `miFunc` se ha convertido en un _closure_. Un _closure_ es un tipo especial de objeto que combina dos cosas: una función, y el entorno en que se creó esa función. El entorno está formado por las variables locales que estaban dentro del alcance en el momento que se creó el closure. En este caso, `miFunc` es un closure que incorpora tanto la función `muestraNombre` como el string "Mozilla" que existían cuando se creó el closure. + +Este es un ejemplo un poco más interesante: una función `creaSumador`: + +```js +function creaSumador(x) { + return function(y) { + return x + y; + }; +} + +var suma5 = creaSumador(5); +var suma10 = creaSumador(10); + +console.log(suma5(2)); // muestra 7 +console.log(suma10(2)); // muestra 12 +``` + +En este ejemplo, hemos definido una función ` creaSumador``(x) ` que toma un argumento único `x` y devuelve una nueva función. Esa nueva función toma un único argumento `y`, devolviendo la suma de `x` + `y`. + +En esencia, ` creaSumador`` `es una fábrica de función: crea funciones que pueden sumar un valor específico a su argumento. En el ejemplo anterior utilizamos nuestra fábrica de función para crear dos nuevas funciones: una que agrega 5 a su argumento y otra que agrega 10. + +`suma5` y `suma10` son ambos closures. Comparten la misma definición de cuerpo de función, pero almacenan diferentes entornos. En el entorno `suma5`, `x` es 5. En lo que respecta a `suma10`, `x` es 10. + +## Closures prácticos + +Hasta aquí hemos visto teoría, pero ¿son los closures realmente útiles? Vamos a considerar sus implicaciones prácticas. Un closure permite asociar algunos datos (el entorno) con una función que opera sobre esos datos. Esto tiene evidentes paralelismos con la programación orientada a objetos, en la que los objetos nos permiten asociar algunos datos (las propiedades del objeto) con uno o más métodos. + +En consecuencia, puede utilizar un closure en cualquier lugar en el que normalmente pondría un objeto con un solo método. + +En la web hay situaciones habituales en las que aplicarlos. Gran parte del código JavaScript para web está basado en eventos: definimos un comportamiento y lo conectamos a un evento que es activado por el usuario (como un click o pulsación de una tecla). Nuestro código generalmente se adjunta como una devolución de llamada (callback): que es una función que se ejecuta en respuesta al evento. + +Aquí está un ejemplo práctico: Supongamos que queremos añadir algunos botones a una página para ajustar el tamaño del texto. Una manera de hacer esto es especificar el tamaño de fuente del elemento `body` en píxeles y, a continuación, ajustar el tamaño de los demás elementos de la página (como los encabezados) utilizando la unidad relativa `em`: + +```css +body { + font-family: Helvetica, Arial, sans-serif; + font-size: 12px; +} + +h1 { + font-size: 1.5em; +} +h2 { + font-size: 1.2em; +} +``` + +Nuestros botones interactivos de tamaño de texto pueden cambiar la propiedad `font-size` del elemento `body`, y los ajustes serán aplicados por los otros elementos de la página gracias a las unidades relativas. + +Aquí está el código JavaScript: + +```js +function makeSizer(size) { + return function() { + document.body.style.fontSize = size + 'px'; + }; +} + +var size12 = makeSizer(12); +var size14 = makeSizer(14); +var size16 = makeSizer(16); +``` + +`size12`, `size14` y `size16` ahora son funciones que cambian el tamaño del texto de `body` a 12, 14 y 16 pixels, respectivamente. Podemos conectarlos a botones (en este caso enlaces) de la siguiente forma: + +```js +document.getElementById('size-12').onclick = size12; +document.getElementById('size-14').onclick = size14; +document.getElementById('size-16').onclick = size16; +``` + +```html +12 +14 +16 +``` + +## Emulando métodos privados con closures + +Lenguajes como Java ofrecen la posibilidad de declarar métodos privados, es decir, que sólo pueden ser llamados por otros métodos en la misma clase. + +JavaScript no proporciona una forma nativa de hacer esto, pero es posible emular métodos privados utilizando closures. Los métodos privados no son sólo útiles para restringir el acceso al código: también proporcionan una poderosa manera de administrar tu espacio de nombres global, evitando que los métodos no esenciales embrollen la interfaz pública de tu código. + +Aquí vemos cómo definir algunas funciones públicas que pueden acceder a variables y funciones privadas utilizando closures. A esto se le conoce también como el [patrón módulo](http://www.google.com/search?q=javascript+patron+modulo "javascript patron modulo"): + +```js +var Counter = (function() { + var privateCounter = 0; + function changeBy(val) { + privateCounter += val; + } + return { + increment: function() { + changeBy(1); + }, + decrement: function() { + changeBy(-1); + }, + value: function() { + return privateCounter; + } + } +})(); + +alert(Counter.value()); /* Muestra 0 */ +Counter.increment(); +Counter.increment(); +alert(Counter.value()); /* Muestra 2 */ +Counter.decrement(); +alert(Counter.value()); /* Muestra 1 */ +``` + +Hay mucho aquí. En los ejemplos anteriores cada closure ha tenido su propio entorno; aquí creamos un único entorno compartido por tres funciones: `Counter.increment`, `Counter.decrement` y `Counter.value`. + +El entorno compartido se crea en el cuerpo de una función anónima, que se ejecuta en el momento que se define. El entorno contiene dos elementos privados: una variable llamada `privateCounter` y una función llamada `changeBy`. No se puede acceder a ninguno de estos elementos privados directamente desde fuera de la función anónima. Se accede a ellos por las tres funciones públicas que se devuelven desde el contenedor anónimo. + +Esas tres funciones públicas son closures que comparten el mismo entorno. Gracias al ámbito léxico de Javascript, cada uno de ellas tienen acceso a la variable `privateCounter` y a la función `changeBy.` + +En este caso hemos definido una función anónima que crea un contador, y luego la llamamos inmediatamente y asignamos el resultado a la variable `Counter`. Pero podríamos almacenar esta función en una variable independiente y utilizarlo para crear varios contadores: + +```js +var makeCounter = function() { + var privateCounter = 0; + function changeBy(val) { + privateCounter += val; + } + return { + increment: function() { + changeBy(1); + }, + decrement: function() { + changeBy(-1); + }, + value: function() { + return privateCounter; + } + } +}; + +var Counter1 = makeCounter(); +var Counter2 = makeCounter(); +alert(Counter1.value()); /* Muestra 0 */ +Counter1.increment(); +Counter1.increment(); +alert(Counter1.value()); /* Muestra 2 */ +Counter1.decrement(); +alert(Counter1.value()); /* Muestra 1 */ +alert(Counter2.value()); /* Muestra 0 */ +``` + +Ten en cuenta que cada uno de los dos contadores mantiene su independencia del otro. Su entorno durante la llamada de la función `makeCounter()` es diferente cada vez. La variable del closure llamada `privateCounter `contiene una instancia diferente cada vez. + +Utilizar closures de este modo proporciona una serie de beneficios que se asocian normalmente con la programación orientada a objectos, en particular la encapsulación y la ocultación de datos. + +## Creando closures en loops: Un error común + +Antes de la introducción de la palabra clave [`let`](/es/docs/JavaScript/Reference/Statements/let "let") en JavaScript 1.7, un problema común con closures ocurría cuando se creaban dentro de un bucle 'loop'. Veamos el siguiente ejemplo: + +```html +

Helpful notes will appear here

+

E-mail:

+

Name:

+

Age:

+``` + +```js +function showHelp(help) { + document.getElementById('help').innerHTML = help; +} + +function setupHelp() { + var helpText = [ + {'id': 'email', 'help': 'Dirección de correo electrónico'}, + {'id': 'name', 'help': 'Nombre completo'}, + {'id': 'age', 'help': 'Edad (debes tener más de 16 años)'} + ]; + + for (var i = 0; i < helpText.length; i++) { + var item = helpText[i]; + document.getElementById(item.id).onfocus = function() { + showHelp(item.help); + } + } +} + +setupHelp(); +``` + +[Ver en el JSFiddle](https://jsfiddle.net/v7gjv) + +El array `helpText` define tres avisos de ayuda, cada uno asociado con el ID de un campo de entrada en el documento. El bucle recorre estas definiciones, enlazando un evento onfocus a cada uno que muestra el método de ayuda asociada. + +Si pruebas este código, verás que no funciona como esperabas. Independientemente del campo en el que se haga foco, siempre se mostrará el mensaje de ayuda relativo a la edad. + +La razón de esto es que las funciones asignadas a onfocus son closures; que constan de la definición de la función y del entorno abarcado desde el ámbito de la función `setupHelp`. Se han creado tres closures, pero todos comparten el mismo entorno. En el momento en que se ejecutan las funciones callback de onfocus, el bucle ya ha finalizado y la variable `item` (compartida por los tres closures) ha quedado apuntando a la última entrada en la lista de `helpText.` + +En este caso, una solución es utilizar más closures: concretamente añadiendo una fábrica de función como se ha descrito anteriormente: + +```js +function showHelp(help) { + document.getElementById('help').innerHTML = help; +} + +function makeHelpCallback(help) { + return function() { + showHelp(help); + }; +} + +function setupHelp() { + var helpText = [ + {'id': 'email', 'help': 'Dirección de correo electrónico'}, + {'id': 'name', 'help': 'Nombre completo'}, + {'id': 'age', 'help': 'Edad (debes tener más de 16 años)'} + ]; + + for (var i = 0; i < helpText.length; i++) { + var item = helpText[i]; + document.getElementById(item.id).onfocus = makeHelpCallback(item.help); + } +} + +setupHelp(); +``` + +[Ver en el JSFiddle](https://jsfiddle.net/v7gjv/1) + +Esto funciona como se esperaba. En lugar de los tres callbacks compartiendo el mismo entorno, la función `makeHelpCallback` crea un nuevo entorno para cada uno en el que `help` se refiere a la cadena correspondiente del array `helpText`. + +## Consideraciones de rendimiento + +No es aconsejable crear innecesariamente funciones dentro de otras funciones si no se necesitan los closures para una tarea particular ya que afectará negativamente el rendimiento del script tanto en consumo de memoria como en velocidad de procesamiento. + +Por ejemplo, cuando se crea un nuevo objeto/clase, los métodos normalmente deberían asociarse al prototipo del objeto en vez de definirse en el constructor del objeto. La razón es que con este último sistema, cada vez que se llama al constructor (cada vez que se crea un objeto) se tienen que reasignar los métodos. + +Veamos el siguente caso, que no es práctico pero sí demostrativo: + +```js +function MyObject(name, message) { + this.name = name.toString(); + this.message = message.toString(); + this.getName = function() { + return this.name; + }; + + this.getMessage = function() { + return this.message; + }; +} +``` + +El código anterior no aprovecha los beneficios de los closures. Podríamos modificarlo de la siguiente manera: + +```js +function MyObject(name, message) { + this.name = name.toString(); + this.message = message.toString(); +} +MyObject.prototype = { + getName: function() { + return this.name; + }, + getMessage: function() { + return this.message; + } +}; +``` + +Sin embargo, no se recomienda redefinir el prototipo, así que el siguiente ejemplo es aún mejor que el anterior, porque lo que hace es añadir funcionalidad al prototipo existente: + +```js +function MyObject(name, message) { + this.name = name.toString(); + this.message = message.toString(); +} +MyObject.prototype.getName = function() { + return this.name; +}; +MyObject.prototype.getMessage = function() { + return this.message; +}; +``` + +En los dos ejemplos anteriores, todos los objetos comparten el prototipo heredado y no se van a definir los métodos cada vez que se crean de objetos. Ver [Detalles del Modelo de Objetos](/es/docs/Web/JavaScript/Guide/Details_of_the_Object_Model "en-US/docs/JavaScript/Guide/Details of the Object Model") para más información. diff --git a/files/es/web/javascript/data_structures/index.html b/files/es/web/javascript/data_structures/index.html deleted file mode 100644 index 5a75d6a883004c..00000000000000 --- a/files/es/web/javascript/data_structures/index.html +++ /dev/null @@ -1,452 +0,0 @@ ---- -title: Tipos de datos y estructuras en JavaScript -slug: Web/JavaScript/Data_structures -tags: - - JavaScript - - Novato - - Principiante - - Tipado -translation_of: Web/JavaScript/Data_structures ---- -
{{jsSidebar("More", "Más")}}
- -

Todos los lenguajes de programación tienen estructuras de datos integradas, pero estas a menudo difieren de un lenguaje a otro. Este artículo intenta enumerar las estructuras de datos integradas disponibles en JavaScript y las propiedades que tienen. Estas se pueden utilizar para construir otras estructuras de datos. Siempre que es posible, se hacen comparaciones con otros lenguajes.

- -

Tipado dinámico

- -

JavaScript es un lenguaje débilmente tipado y dinámico. Las variables en JavaScript no están asociadas directamente con ningún tipo de valor en particular, y a cualquier variable se le puede asignar (y reasignar) valores de todos los tipos:

- -
let foo = 42;    // foo ahora es un número
-foo     = 'bar'; // foo ahora es un string
-foo     = true;  // foo ahora es un booleano
-
- -

Estructuras y tipos de datos

- -

El último estándar ECMAScript define nueve tipos:

- - - -

Ten en cuenta que el único propósito valioso del uso del operador typeof es verificar el tipo de dato. Si deseamos verificar cualquier Tipo Estructural derivado de Object, no tiene sentido usar typeof para eso, ya que siempre recibiremos "object". La forma correcta de comprobar qué tipo de Objeto estamos usando es la palabra clave {{jsxref("Operators/instanceof", "instanceof")}}. Pero incluso en ese caso, puede haber conceptos erróneos.

- -

Valores primitivos

- -

Todos los tipos, excepto los objetos, definen valores inmutables (es decir, valores que no se pueden cambiar). Por ejemplo (y a diferencia de C), las cadenas son inmutables. Nos referimos a los valores de estos tipos como "valores primitivos".

- -

Tipo Boolean

- -

Boolean representa una entidad lógica y puede tener dos valores: true y false. Consulta {{Glossary("Boolean")}} y {{jsxref("Boolean")}} para obtener más detalles.

- -

Tipo Null

- -

El tipo Null tiene exactamente un valor: null. Consulta {{jsxref("null")}} y {{Glossary("Null")}} para obtener más detalles.

- -

Tipo Undefined

- -

Una variable a la que no se le ha asignado un valor tiene el valor undefined. Consulta {{jsxref("undefined")}} y {{Glossary("Undefined")}} para obtener más detalles.

- -

Tipo Number

- -

ECMAScript tiene dos tipos numéricos integrados: Number y BigInt (ve más abajo).

- -

El tipo Number es un valor en formato binario de 64 bits de doble precisión IEEE 754 (números entre -(253 - 1) y 253 - 1). Además de representar números de punto flotante, el tipo Number tiene tres valores simbólicos: +Infinity, -Infinity y {{jsxref("NaN")}} ("Not a Number" o No es un número).

- -

Para verificar el valor disponible más grande o el valor más pequeño disponible dentro de {{jsxref("Infinity", "±Infinity")}}, puedes usar las constantes {{jsxref("Number.MAX_VALUE")}} o {{jsxref("Number.MIN_VALUE")}}.

- -
-

A partir de ECMAScript 2015, también puedes comprobar si un número está en el rango de números de punto flotante de doble precisión mediante {{jsxref("Number.isSafeInteger()")}} así como {{jsxref("Number.MAX_SAFE_INTEGER")}} y {{jsxref("Number.MIN_SAFE_INTEGER")}}.

- -

Más allá de este rango, los enteros en JavaScript ya no son seguros y serán una aproximación de punto flotante de doble precisión del valor.

-
- -

El tipo number solo tiene un entero con dos representaciones: 0 se representa como -0 y +0. (0 es un alias de +0).

- -

En la praxis, esto casi no tiene impacto. Por ejemplo, +0 === -0 es true. Sin embargo, puedes notar esto cuando divides entre cero:

- -
> 42 / +0
-Infinity
-> 42 / -0
--Infinity
-
- -

Aunque un number a menudo representa solo su valor, JavaScript proporciona {{jsxref("Operators/Bitwise_Operators", "operadores binarios (bitwise)")}}.

- -
-

Precaución: Aunque los operadores bit a bit se pueden usar para representar múltiples valores Booleanos dentro de un solo número usando el enmascaramiento de bits, esto generalmente se considera una mala práctica. JavaScript ofrece otros medios para representar un conjunto de valores booleanos (como un arreglo de valores booleanos o un objeto con valores booleanos asignados a propiedades con nombre). El enmascaramiento de bits también tiende a hacer que el código sea más difícil de leer, comprender y mantener.

-
- -

Posiblemente sea necesario utilizar estas técnicas en entornos muy restringidos, como cuando se intenta hacer frente a las limitaciones del almacenamiento local, o en casos extremos (como cuando cada bit de la red cuenta). Esta técnica solo se debe considerar cuando sea la última medida que se pueda tomar para optimizar el tamaño.

- -

Tipo BigInt

- -

El tipo {{jsxref("BigInt")}} es un primitivo numérico en JavaScript que puede representar números enteros con precisión arbitraria. Con BigInts, puedes almacenar y operar de forma segura en números enteros grandes incluso más allá del límite seguro de enteros para Numbers.

- -

Un BigInt se crea agregando n al final de un número entero o llamando al constructor.

- -

Puedes obtener el valor más seguro que se puede incrementar con Numbers utilizando la constante {{jsxref("Number.MAX_SAFE_INTEGER")}}. Con la introducción de BigInts, puedes operar con números más allá de {{jsxref("Number.MAX_SAFE_INTEGER")}}.

- -

Este ejemplo demuestra, dónde, incrementando el {{jsxref("Number.MAX_SAFE_INTEGER")}} devuelve el resultado esperado:

- -
> const x = 2n ** 53n;
-9007199254740992n
-> const y = x + 1n;
-9007199254740993n
-
- -

Puedes utilizar los operadores +, *, -, ** y % con BigInts, igual que con Numbers. Un BigInt no es estrictamente igual a un Number, pero lo es en términos generales.

- -

Un BigInt se comporta como un Number en los casos en que se convierte a Boolean: if, ||, &&, Boolean, !.

- -

Los BigInt no se pueden utilizar indistintamente con los Number. En su lugar, se lanzará un {{jsxref("TypeError")}}.

- -

Tipo String

- -

El tipo {{jsxref("String")}} de JavaScript se utiliza para representar datos textuales. Es un conjunto de "elementos" de valores enteros sin signo de 16 bits. Cada elemento del String ocupa una posición en la cadena. El primer elemento está en el índice 0, el siguiente en el índice 1, y así sucesivamente. La longitud de una cadena es el número de elementos que contiene.

- -

A diferencia de algunos lenguajes de programación (tal como C), las cadenas de JavaScript son inmutables. Esto significa que una vez que se crea una cadena, no es posible modificarla.

- -

Sin embargo, todavía es posible crear otra cadena basada en una operación en la cadena original. Por ejemplo:

- - - -

¡Ten cuidado de no "convertir a cadenas" tu código!

- -

Puede resultar tentador utilizar cadenas para representar datos complejos. Hacer esto viene con beneficios a corto plazo:

- - - -

Con las convenciones, es posible representar cualquier estructura de datos en una cadena. Esto no la convierte en una buena idea. Por ejemplo, con un separador, se podría emular una lista (mientras que un arreglo de JavaScript sería más adecuado). Desafortunadamente, cuando el separador se usa en uno de los elementos de la "lista", la lista se rompe. Se puede elegir un caracter de escape, etc. Todo esto requiere convenciones y crea una innecesaria carga de mantenimiento.

- -

Utiliza cadenas para datos textuales. Cuando quieras representar datos complejos, procesa las cadenas y usa la abstracción adecuada.

- -

Tipo Symbol

- -

Un símbolo es un valor primitivo único e inmutable y se puede utilizar como clave de una propiedad de objeto (ve más abajo). En algunos lenguajes de programación, los símbolos se denominan "átomos".

- -

Para obtener más detalles, consulta {{Glossary("Symbol")}} y el contenedor de objetos {{jsxref("Symbol")}} en JavaScript.

- -

Objetos

- -

En ciencias de la computación, un objeto es un valor en la memoria al que posiblemente hace referencia un {{Glossary("Identifier", "identificador")}}.

- -

Propiedades

- -

En JavaScript, los objetos se pueden ver como una colección de propiedades. Con la sintaxis de objeto literal, se inicia un conjunto limitado de propiedades; luego se pueden agregar y eliminar propiedades. Los valores de propiedad pueden ser valores de cualquier tipo, incluidos otros objetos, lo que permite construir estructuras de datos complejas. Las propiedades se identifican mediante valores clave. Un valor clave es un valor de cadena o un símbolo.

- -

Hay dos tipos de propiedades de objeto que tienen ciertos atributos: la propiedad data y la propiedad accessor.

- -
-

Nota: Cada propiedad tiene atributos correspondientes. Los atributos, internamente los utiliza el motor JavaScript, por lo que no puedes acceder a ellos directamente. Es por eso que los atributos se enumeran entre corchetes dobles, en lugar de simples.

- -

Consulta {{jsxref("Object.defineProperty()")}} para obtener más información.

-
- -

Propiedad Data

- -

Asocia una clave con un valor y tiene los siguientes atributos:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Atributos de una propiedad data
AtributoTipoDescripciónValor predeterminado
[[Value]]Cualquier tipo de JavaScriptEl valor recuperado por un captador de acceso get a la propiedad.undefined
[[Writable]]BooleanSi es false, el [[Value]] de la propiedad no se puede cambiar.false
[[Enumerable]]Boolean -

Si es true, la propiedad se enumerará en bucles for...in.
- Consulta también Enumerabilidad y posesión de propiedades.

-
false
[[Configurable]]BooleanSi es false, la propiedad no se puede eliminar, no se puede cambiar a una propiedad de acceso descriptor y los atributos que no sean [[Value]] y [[Writable]] no se pueden cambiar.false
- - - - - - - - - - - - - - - - - - - - - - - - - - - -
Atributos obsoletos (a partir de ECMAScript 3, renombrado en ECMAScript 5)
AtributoTipoDescripción
Read-onlyBooleanEstado inverso del atributo ES5 [[Writable]].
DontEnumBooleanEstado inverso del atributo ES5 [[Enumerable]].
DontDeleteBooleanEstado inverso del atributo ES5 [[Configurable]].
- -

Propiedad Accessor

- -

Asocia una clave con una de las dos funciones de acceso (get y set) para recuperar o almacenar un valor y tiene los siguientes atributos:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Atributos de una propiedad accessor
AtributoTipoDescripciónValor predeterminado
[[Get]]Objeto Function o undefinedLa función se llama con una lista de argumentos vacía y recupera el valor de la propiedad cada vez que se realiza un acceso al valor.
- Consulta también get.
undefined
[[Set]]Objeto Function o undefinedLa función se llama con un argumento que contiene el valor asignado y se ejecuta siempre que se intenta cambiar una propiedad específica.
- Consulta también set.
undefined
[[Enumerable]]BooleanSi es true, la propiedad se enumerará en bucles for...in.false
[[Configurable]]BooleanSi es false, la propiedad no se puede eliminar y no se puede cambiar a una propiedad de datos.false
- -

Objetos y funciones "normales"

- -

Un objeto JavaScript es una asociación entre claves y valores. Las claves son cadenas (o {{jsxref("Symbol")}}s), y los valores pueden ser cualquier cosa. Esto hace que los objetos se ajusten naturalmente a hashmaps.

- -

Las funciones son objetos regulares con la capacidad adicional de ser invocables.

- -

Fechas

- -

Al representar fechas, la mejor opción es utilizar la utilidad Date incorporada en JavaScript.

- -

Colecciones indexadas: arreglos y arreglos tipados

- -

Los arreglos son objetos regulares para los que existe una relación particular entre las propiedades de clave entera y la Propiedad length.

- -

Además, los arreglos heredan de Array.prototype, que les proporciona un puñado de convenientes métodos para manipular arreglos. Por ejemplo, indexOf (buscando un valor en el arreglo) o push (agrega un elemento al arreglo), y así sucesivamente. Esto hace que el Array sea un candidato perfecto para representar listas o conjuntos.

- -

Los Arreglos tipados son nuevos en JavaScript con ECMAScript 2015 y presentan una vista similar a un arreglo de un búfer de datos binarios subyacente. La siguiente tabla ayuda a determinar los tipos de datos equivalentes en C:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TipoIntervalo de valoresTamaño en bytesDescripciónTipo de IDL webTipo C equivalente
{{jsxref("Int8Array")}}-128 a 1271Dos enteros complementarios de 8 bits con signobyteint8_t
{{jsxref("Uint8Array")}}0 a 2551Entero de 8-bit sin signooctetuint8_t
{{jsxref("Uint8ClampedArray")}}0 a 2551Entero de 8 bits sin signo (sujeto)octetuint8_t
{{jsxref("Int16Array")}}-32768 a 327672Dos enteros complementarios de 16 bits con signoshortint16_t
{{jsxref("Uint16Array")}}0 a 655352Entero de 16 bits sin signoShort sin signouint16_t
{{jsxref("Int32Array")}}-2147483648 a 21474836474dos enteros complementarios de 32 bits con signolongint32_t
{{jsxref("Uint32Array")}}0 a 42949672954Enteros de 32 bits sin signolong sin signouint32_t
{{jsxref("Float32Array")}}1.2×10-38 a 3.4×10384Número de coma flotante IEEE de 32 bits (7 dígitos significativos, p. ej., 1.1234567)float sin restriccionesfloat
{{jsxref("Float64Array")}}5.0×10-324 a 1.8×103088Número de coma flotante IEEE de 64 bits (16 dígitos significativos, p. ej., 1.123...15)doble sin restriccionesdouble
{{jsxref("BigInt64Array")}}-263 a 263-18Dos enteros complementarios de 64 bits con signobigintint64_t (long long con signo)
{{jsxref("BigUint64Array")}}0 a 264-18Entero de 64 bits sin signobigintuint64_t (long long sin signo)
- -

Colecciones con clave: mapas, conjuntos, WeakMaps, WeakSets

- -

Estas estructuras de datos, introducidas en ECMAScript Edition 6, toman referencias a objetos como claves. {{jsxref("Set")}} y {{jsxref("WeakSet")}} representan un conjunto de objetos, mientras que {{jsxref("Map")}} y {{jsxref("WeakMap")}} se asocian un valor a un objeto.

- -

La diferencia entre Maps y WeakMaps es que en el primero, las claves de objeto se pueden enumerar. Esto permite la optimización de la recolección de basura en el último caso.

- -

Se podrían implementar Maps y Sets en ECMAScript 5 puro. Sin embargo, dado que los objetos no se pueden comparar (en el sentido de < "menor que", por ejemplo), el rendimiento de búsqueda sería necesariamente lineal. Las implementaciones nativas de ellos (incluidos los WeakMaps) pueden tener un rendimiento de búsqueda que es aproximadamente logarítmico al tiempo constante.

- -

Por lo general, para vincular datos a un nodo DOM, se pueden establecer propiedades directamente en el objeto o usar atributos data-*. Esto tiene la desventaja de que los datos están disponibles para cualquier script que se ejecute en el mismo contexto. Los Maps y WeakMaps facilitan la vinculación privada de datos a un objeto.

- -

Datos estructurados: JSON

- -

JSON (Java Script Object Notation) es un formato ligero de intercambio de datos, derivado de JavaScript, pero utilizado por muchos lenguajes de programación. JSON crea estructuras de datos universales.

- -

Consulta {{Glossary("JSON")}} y {{jsxref("JSON")}} para obtener más detalles.

- -

Más objetos en la biblioteca estándar

- -

JavaScript tiene una biblioteca estándar de objetos integrados.

- -

Échale un vistazo a la referencia para conocer más objetos.

- -

Determinación de tipos usando el operador typeof

- -

El operador typeof te puede ayudar a encontrar el tipo de tu variable.

- -

Lee la página de referencia para obtener más detalles y casos extremos.

- -

Especificaciones

- - - - - - - - - - - - -
Especificación
{{SpecName('ESDraft', '#sec-ecmascript-data-types-and-values', 'Tipos Data y Values ECMAScript')}}
- -

Ve también

- - diff --git a/files/es/web/javascript/data_structures/index.md b/files/es/web/javascript/data_structures/index.md new file mode 100644 index 00000000000000..e10336bd9e4881 --- /dev/null +++ b/files/es/web/javascript/data_structures/index.md @@ -0,0 +1,252 @@ +--- +title: Tipos de datos y estructuras en JavaScript +slug: Web/JavaScript/Data_structures +tags: + - JavaScript + - Novato + - Principiante + - Tipado +translation_of: Web/JavaScript/Data_structures +--- +{{jsSidebar("More", "Más")}} + +Todos los lenguajes de programación tienen estructuras de datos integradas, pero estas a menudo difieren de un lenguaje a otro. Este artículo intenta enumerar las estructuras de datos integradas disponibles en JavaScript y las propiedades que tienen. Estas se pueden utilizar para construir otras estructuras de datos. Siempre que es posible, se hacen comparaciones con otros lenguajes. + +## Tipado dinámico + +JavaScript es un lenguaje _débilmente tipado_ y _dinámico_. Las variables en JavaScript no están asociadas directamente con ningún tipo de valor en particular, y a cualquier variable se le puede asignar (y reasignar) valores de todos los tipos: + +```js +let foo = 42; // foo ahora es un número +foo = 'bar'; // foo ahora es un string +foo = true; // foo ahora es un booleano +``` + +## Estructuras y tipos de datos + +El último estándar ECMAScript define nueve tipos: + +- Seis **tipos de datos** {{Glossary("Primitive", "primitivos")}}, controlados por el {{jsxref("Operators/typeof", "operador typeof")}} + + - {{Glossary("Undefined")}}: `typeof instance === "undefined"` + - {{Glossary("Boolean")}}: `typeof instance === "boolean"` + - {{Glossary("Number")}}: `typeof instance === "number"` + - {{Glossary("String")}}: `typeof instance === "string"` + - {{Glossary("BigInt")}}: `typeof instance === "bigint"` + - {{Glossary("Symbol")}}: `typeof instance === "symbol"` + +- {{Glossary("Null")}}: `typeof instance === "object"`. Tipo {{Glossary("Primitive", "primitivo")}} especial que tiene un uso adicional para su valor: si el objeto no se hereda, se muestra `null`; +- {{Glossary("Object")}}: `typeof instance === "object"`. Tipo estructural especial que no es de datos pero para cualquier instancia de objeto [construido](/es/docs/Learn/JavaScript/Objects#The_Constructor) que también se utiliza como estructuras de datos: new {{jsxref("Object")}}, new {{jsxref("Array")}}, new {{jsxref("Map")}}, new {{jsxref("Set")}}, new {{jsxref("WeakMap")}}, new {{jsxref("WeakSet")}}, new {{jsxref("Date")}} y casi todo lo hecho con la [palabra clave `new`](/es/docs/Web/JavaScript/Reference/Operators/new); +- {{Glossary("Function")}}: una estructura sin datos, aunque también responde al operador `typeof`: `typeof instance === "function"`. Esta simplemente es una forma abreviada para funciones, aunque cada constructor de funciones se deriva del constructor `Object`. + +Ten en cuenta que el único propósito valioso del uso del operador `typeof` es verificar el tipo de dato. Si deseamos verificar cualquier Tipo Estructural derivado de `Object`, no tiene sentido usar `typeof` para eso, ya que siempre recibiremos "`object`". La forma correcta de comprobar qué tipo de Objeto estamos usando es la palabra clave {{jsxref("Operators/instanceof", "instanceof")}}. Pero incluso en ese caso, puede haber conceptos erróneos. + +## Valores primitivos + +Todos los tipos, excepto los objetos, definen valores inmutables (es decir, valores que no se pueden cambiar). Por ejemplo (y a diferencia de C), las cadenas son inmutables. Nos referimos a los valores de estos tipos como "_valores primitivos_". + +### Tipo `Boolean` + +`Boolean` representa una entidad lógica y puede tener dos valores: `true` y `false`. Consulta {{Glossary("Boolean")}} y {{jsxref("Boolean")}} para obtener más detalles. + +### Tipo `Null` + +El tipo `Null` tiene exactamente un valor: `null`. Consulta {{jsxref("null")}} y {{Glossary("Null")}} para obtener más detalles. + +### Tipo `Undefined` + +Una variable a la que no se le ha asignado un valor tiene el valor `undefined`. Consulta {{jsxref("undefined")}} y {{Glossary("Undefined")}} para obtener más detalles. + +### Tipo `Number` + +ECMAScript tiene dos tipos numéricos integrados: **`Number`** y **`BigInt`** (ve más abajo). + +El tipo `Number` es un [valor en formato binario de 64 bits de doble precisión IEEE 754](https://es.wikipedia.org/wiki/Formato_en_coma_flotante_de_doble_precisión) (números entre -(253 - 1) y 253 - 1). Además de representar números de punto flotante, el tipo `Number` tiene tres valores simbólicos: `+Infinity`, `-Infinity` y {{jsxref("NaN")}} ("**N**ot a **N**umber" o No es un número). + +Para verificar el valor disponible más grande o el valor más pequeño disponible dentro de {{jsxref("Infinity", "±Infinity")}}, puedes usar las constantes {{jsxref("Number.MAX_VALUE")}} o {{jsxref("Number.MIN_VALUE")}}. + +> **Nota:** **A partir de ECMAScript 2015**, también puedes comprobar si un número está en el rango de números de punto flotante de doble precisión mediante {{jsxref("Number.isSafeInteger()")}} así como {{jsxref("Number.MAX_SAFE_INTEGER")}} y {{jsxref("Number.MIN_SAFE_INTEGER")}}.Más allá de este rango, los enteros en JavaScript ya no son seguros y serán una aproximación de punto flotante de doble precisión del valor. + +El tipo `number` solo tiene un entero con dos representaciones: `0` se representa como `-0` y `+0`. (`0` es un alias de `+0`). + +En la praxis, esto casi no tiene impacto. Por ejemplo, `+0 === -0` es `true`. Sin embargo, puedes notar esto cuando divides entre cero: + +```js +> 42 / +0 +Infinity +> 42 / -0 +-Infinity +``` + +Aunque un `number` a menudo representa solo su valor, JavaScript proporciona {{jsxref("Operators/Bitwise_Operators", "operadores binarios (bitwise)")}}. + +> **Nota:** **Precaución**: Aunque los operadores bit a bit se _pueden_ usar para representar múltiples valores Booleanos dentro de un solo número usando el [enmascaramiento de bits](), esto generalmente se considera una mala práctica. JavaScript ofrece otros medios para representar un conjunto de valores booleanos (como un arreglo de valores booleanos o un objeto con valores booleanos asignados a propiedades con nombre). El enmascaramiento de bits también tiende a hacer que el código sea más difícil de leer, comprender y mantener. + +Posiblemente sea necesario utilizar estas técnicas en entornos muy restringidos, como cuando se intenta hacer frente a las limitaciones del almacenamiento local, o en casos extremos (como cuando cada bit de la red cuenta). Esta técnica solo se debe considerar cuando sea la última medida que se pueda tomar para optimizar el tamaño. + +### Tipo `BigInt` + +El tipo {{jsxref("BigInt")}} es un primitivo numérico en JavaScript que puede representar números enteros con precisión arbitraria. Con `BigInt`s, puedes almacenar y operar de forma segura en números enteros grandes incluso más allá del límite seguro de enteros para `Number`s. + +Un `BigInt` se crea agregando `n` al final de un número entero o llamando al constructor. + +Puedes obtener el valor más seguro que se puede incrementar con `Number`s utilizando la constante {{jsxref("Number.MAX_SAFE_INTEGER")}}. Con la introducción de `BigInt`s, puedes operar con números más allá de {{jsxref("Number.MAX_SAFE_INTEGER")}}. + +Este ejemplo demuestra, dónde, incrementando el {{jsxref("Number.MAX_SAFE_INTEGER")}} devuelve el resultado esperado: + +```js +> const x = 2n ** 53n; +9007199254740992n +> const y = x + 1n; +9007199254740993n +``` + +Puedes utilizar los operadores `+`, `*`, `-`, `**` y `%` con `BigInt`s, igual que con `Number`s. Un `BigInt` no es estrictamente igual a un `Number`, pero lo es en términos generales. + +Un `BigInt` se comporta como un `Number` en los casos en que se convierte a `Boolean`: `if`, `||`, `&&`, `Boolean`, `!`. + +Los `BigInt` no se pueden utilizar indistintamente con los `Number`. En su lugar, se lanzará un {{jsxref("TypeError")}}. + +### Tipo `String` + +El tipo {{jsxref("String")}} de JavaScript se utiliza para representar datos textuales. Es un conjunto de "elementos" de valores enteros sin signo de 16 bits. Cada elemento del `String` ocupa una posición en la cadena. El primer elemento está en el índice `0`, el siguiente en el índice `1`, y así sucesivamente. La longitud de una cadena es el número de elementos que contiene. + +A diferencia de algunos lenguajes de programación (tal como C), las cadenas de JavaScript son inmutables. Esto significa que una vez que se crea una cadena, no es posible modificarla. + +Sin embargo, todavía es posible crear otra cadena basada en una operación en la cadena original. Por ejemplo: + +- Una subcadena de la original seleccionando letras individuales o usando {{jsxref("String.substr()")}}. +- Una concatenación de dos cadenas usando el operador de concatenación (`+`) o {{jsxref("String.concat()")}}. + +#### ¡Ten cuidado de no "convertir a cadenas" tu código! + +Puede resultar tentador utilizar cadenas para representar datos complejos. Hacer esto viene con beneficios a corto plazo: + +- Es fácil construir cadenas complejas con concatenación. +- Las cadenas son fáciles de depurar (lo que ves impreso siempre es lo que está en la cadena). +- Las cadenas son el denominador común de muchas APIs ([campos de entrada —`input`s—](/es/docs/Web/API/HTMLInputElement "HTMLInputElement"), [valores de almacenamiento local](/es/docs/Storage "Storage"), respuestas [`XMLHttpRequest`](/es/docs/Web/API/XMLHttpRequest "Usa objetos XMLHttpRequest (XHR) para interactuar con servidores. Puedes recuperar datos de una URL sin tener que actualizar la página completa. Esto permite que una página web actualice solo parte de su contenido sin interrumpir lo que el usuario está haciendo.") cuando se usa `responseText`, etc.) y puede resultar tentador trabajar solo con cadenas. + +Con las convenciones, es posible representar cualquier estructura de datos en una cadena. Esto no la convierte en una buena idea. Por ejemplo, con un separador, se podría emular una lista (mientras que un arreglo de JavaScript sería más adecuado). Desafortunadamente, cuando el separador se usa en uno de los elementos de la "lista", la lista se rompe. Se puede elegir un caracter de escape, etc. Todo esto requiere convenciones y crea una innecesaria carga de mantenimiento. + +Utiliza cadenas para datos textuales. Cuando quieras representar datos complejos, _procesa_ las cadenas y usa la abstracción adecuada. + +### Tipo `Symbol` + +Un símbolo es un valor primitivo **único** e **inmutable** y se puede utilizar como clave de una propiedad de objeto (ve más abajo). En algunos lenguajes de programación, los símbolos se denominan "átomos". + +Para obtener más detalles, consulta {{Glossary("Symbol")}} y el contenedor de objetos {{jsxref("Symbol")}} en JavaScript. + +## Objetos + +En ciencias de la computación, un objeto es un valor en la memoria al que posiblemente hace referencia un {{Glossary("Identifier", "identificador")}}. + +### Propiedades + +En JavaScript, los objetos se pueden ver como una colección de propiedades. Con la [sintaxis de objeto literal](/es/docs/Web/JavaScript/Guide/Values,_variables,_and_literals#Object_literals), se inicia un conjunto limitado de propiedades; luego se pueden agregar y eliminar propiedades. Los valores de propiedad pueden ser valores de cualquier tipo, incluidos otros objetos, lo que permite construir estructuras de datos complejas. Las propiedades se identifican mediante valores _clave_. Un valor _clave_ es un valor de cadena o un símbolo. + +Hay dos tipos de propiedades de objeto que tienen ciertos atributos: la propiedad _data_ y la propiedad _accessor_. + +> **Nota:** **Nota**: Cada propiedad tiene _atributos correspondientes_. Los atributos, internamente los utiliza el motor JavaScript, por lo que no puedes acceder a ellos directamente. Es por eso que los atributos se enumeran entre corchetes dobles, en lugar de simples.Consulta {{jsxref("Object.defineProperty()")}} para obtener más información. + +#### Propiedad `Data` + +Asocia una clave con un valor y tiene los siguientes atributos: + +**Atributos de una propiedad `data`** + +| Atributo | Tipo | Descripción | Valor predeterminado | +| ---------------- | ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------- | +| [[Value]] | Cualquier tipo de JavaScript | El valor recuperado por un captador de acceso `get` a la propiedad. | `undefined` | +| [[Writable]] | `Boolean` | Si es `false`, el [[Value]] de la propiedad no se puede cambiar. | `false` | +| [[Enumerable]] | `Boolean` | Si es `true`, la propiedad se enumerará en bucles [`for...in`](/es/docs/Web/JavaScript/Reference/Statements/for...in). Consulta también [Enumerabilidad y posesión de propiedades](/es/docs/Web/JavaScript/Enumerability_and_ownership_of_properties). | `false` | +| [[Configurable]] | `Boolean` | Si es `false`, la propiedad no se puede eliminar, no se puede cambiar a una propiedad de acceso descriptor y los atributos que no sean [[Value]] y [[Writable]] no se pueden cambiar. | `false` | + +**Atributos obsoletos (a partir de ECMAScript 3, renombrado en ECMAScript 5)** + +| Atributo | Tipo | Descripción | +| ------------ | --------- | ------------------------------------------------- | +| `Read-only` | `Boolean` | Estado inverso del atributo ES5 [[Writable]]. | +| `DontEnum` | `Boolean` | Estado inverso del atributo ES5 [[Enumerable]]. | +| `DontDelete` | `Boolean` | Estado inverso del atributo ES5 [[Configurable]]. | + +#### Propiedad `Accessor` + +Asocia una clave con una de las dos funciones de acceso (`get` y `set`) para recuperar o almacenar un valor y tiene los siguientes atributos: + +| Atributo | Tipo | Descripción | Valor predeterminado | +| ---------------- | ------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------- | +| [[Get]] | Objeto `Function` o `undefined` | La función se llama con una lista de argumentos vacía y recupera el valor de la propiedad cada vez que se realiza un acceso al valor. Consulta también [`get`](/es/docs/Web/JavaScript/Reference/Operators/get). | `undefined` | +| [[Set]] | Objeto `Function` o `undefined` | La función se llama con un argumento que contiene el valor asignado y se ejecuta siempre que se intenta cambiar una propiedad específica. Consulta también [`set`](/es/docs/Web/JavaScript/Reference/Operators/set). | `undefined` | +| [[Enumerable]] | `Boolean` | Si es `true`, la propiedad se enumerará en bucles [`for...in`](/es/docs/Web/JavaScript/Reference/Statements/for...in). | `false` | +| [[Configurable]] | `Boolean` | Si es `false`, la propiedad no se puede eliminar y no se puede cambiar a una propiedad de datos. | `false` | + +### Objetos y funciones "normales" + +Un objeto JavaScript es una asociación entre _claves_ y _valores_. Las claves son cadenas (o {{jsxref("Symbol")}}s), y los _valores_ pueden ser cualquier cosa. Esto hace que los objetos se ajusten naturalmente a [`hashmaps`](http://en.wikipedia.org/wiki/Hash_table). + +Las funciones son objetos regulares con la capacidad adicional de ser _invocables_. + +### Fechas + +Al representar fechas, la mejor opción es utilizar la utilidad [`Date` incorporada](/es/docs/Web/JavaScript/Reference/Global_Objects/Date) en JavaScript. + +### Colecciones indexadas: arreglos y arreglos tipados + +[Los arreglos](/es/docs/JavaScript/Reference/Global_Objects/Array "Array") son objetos regulares para los que existe una relación particular entre las propiedades de clave entera y la Propiedad `length`. + +Además, los arreglos heredan de `Array.prototype`, que les proporciona un puñado de convenientes métodos para manipular arreglos. Por ejemplo, [`indexOf`](/es/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf "es/JavaScript/Reference/Global_Objects/Array/indexOf") (buscando un valor en el arreglo) o [`push`](/es/docs/JavaScript/Reference/Global_Objects/Array/push "es/JavaScript/Reference/Global_Objects/Array/push") (agrega un elemento al arreglo), y así sucesivamente. Esto hace que el `Array` sea un candidato perfecto para representar listas o conjuntos. + +Los [Arreglos tipados](/es/docs/Web/JavaScript/Typed_arrays) son nuevos en JavaScript con ECMAScript 2015 y presentan una vista similar a un arreglo de un búfer de datos binarios subyacente. La siguiente tabla ayuda a determinar los tipos de datos equivalentes en C: + +| Tipo | Intervalo de valores | Tamaño en bytes | Descripción | Tipo de IDL web | Tipo C equivalente | +| ---------------------------------------- | ------------------------------ | --------------- | ----------------------------------------------------------------------------------------- | ------------------------- | -------------------------------- | +| {{jsxref("Int8Array")}} | `-128` a `127` | 1 | Dos enteros complementarios de 8 bits con signo | `byte` | `int8_t` | +| {{jsxref("Uint8Array")}} | `0` a `255` | 1 | Entero de 8-bit sin signo | `octet` | `uint8_t` | +| {{jsxref("Uint8ClampedArray")}} | `0` a `255` | 1 | Entero de 8 bits sin signo (sujeto) | `octet` | `uint8_t` | +| {{jsxref("Int16Array")}} | `-32768` a `32767` | 2 | Dos enteros complementarios de 16 bits con signo | `short` | `int16_t` | +| {{jsxref("Uint16Array")}} | `0` a `65535` | 2 | Entero de 16 bits sin signo | `Short sin signo` | `uint16_t` | +| {{jsxref("Int32Array")}} | `-2147483648` a `2147483647` | 4 | dos enteros complementarios de 32 bits con signo | `long` | `int32_t` | +| {{jsxref("Uint32Array")}} | `0` a `4294967295` | 4 | Enteros de 32 bits sin signo | `long sin signo` | `uint32_t` | +| {{jsxref("Float32Array")}} | `1.2`×`10-38` a `3.4`×`1038` | 4 | Número de coma flotante IEEE de 32 bits (7 dígitos significativos, p. ej., `1.1234567`) | `float sin restricciones` | `float` | +| {{jsxref("Float64Array")}} | `5.0`×`10-324` a `1.8`×`10308` | 8 | Número de coma flotante IEEE de 64 bits (16 dígitos significativos, p. ej., `1.123...15`) | `doble sin restricciones` | `double` | +| {{jsxref("BigInt64Array")}} | `-263` a `263-1` | 8 | Dos enteros complementarios de 64 bits con signo | `bigint` | `int64_t (long long con signo)` | +| {{jsxref("BigUint64Array")}} | `0` a `264-1` | 8 | Entero de 64 bits sin signo | `bigint` | `uint64_t (long long sin signo)` | + +### Colecciones con clave: mapas, conjuntos, `WeakMaps`, `WeakSets` + +Estas estructuras de datos, introducidas en ECMAScript Edition 6, toman referencias a objetos como claves. {{jsxref("Set")}} y {{jsxref("WeakSet")}} representan un conjunto de objetos, mientras que {{jsxref("Map")}} y {{jsxref("WeakMap")}} se asocian un valor a un objeto. + +La diferencia entre `Map`s y `WeakMap`s es que en el primero, las claves de objeto se pueden enumerar. Esto permite la optimización de la recolección de basura en el último caso. + +Se podrían implementar `Map`s y `Set`s en ECMAScript 5 puro. Sin embargo, dado que los objetos no se pueden comparar (en el sentido de `<` "menor que", por ejemplo), el rendimiento de búsqueda sería necesariamente lineal. Las implementaciones nativas de ellos (incluidos los `WeakMap`s) pueden tener un rendimiento de búsqueda que es aproximadamente logarítmico al tiempo constante. + +Por lo general, para vincular datos a un nodo DOM, se pueden establecer propiedades directamente en el objeto o usar atributos `data-*`. Esto tiene la desventaja de que los datos están disponibles para cualquier script que se ejecute en el mismo contexto. Los `Map`s y `WeakMap`s facilitan la vinculación _privada_ de datos a un objeto. + +### Datos estructurados: JSON + +JSON (**J**ava **S**cript **O**bject **N**otation) es un formato ligero de intercambio de datos, derivado de JavaScript, pero utilizado por muchos lenguajes de programación. JSON crea estructuras de datos universales. + +Consulta {{Glossary("JSON")}} y {{jsxref("JSON")}} para obtener más detalles. + +### Más objetos en la biblioteca estándar + +JavaScript tiene una biblioteca estándar de objetos integrados. + +Échale un vistazo a la [referencia](/es/docs/Web/JavaScript/Reference/Global_Objects) para conocer más objetos. + +## Determinación de tipos usando el operador `typeof` + +El operador `typeof` te puede ayudar a encontrar el tipo de tu variable. + +Lee la [página de referencia](/es/docs/Web/JavaScript/Reference/Operators/typeof) para obtener más detalles y casos extremos. + +## Especificaciones + +| Especificación | +| ------------------------------------------------------------------------------------------------------------------------------------ | +| {{SpecName('ESDraft', '#sec-ecmascript-data-types-and-values', 'Tipos Data y Values ECMAScript')}} | + +## Ve también + +- [Colección de estructura de datos común y algoritmos comunes en JavaScript de Nicholas Zakas.](https://github.com/nzakas/computer-science-in-javascript/) +- [Estructuras de datos implementadas en JavaScript](https://github.com/monmohan/DataStructures_In_Javascript) diff --git a/files/es/web/javascript/eventloop/index.html b/files/es/web/javascript/eventloop/index.html deleted file mode 100644 index a1b3e9f8aaa8cf..00000000000000 --- a/files/es/web/javascript/eventloop/index.html +++ /dev/null @@ -1,110 +0,0 @@ ---- -title: Modelo de concurrencia y loop de eventos -slug: Web/JavaScript/EventLoop -tags: - - Avanzado - - JavaScript -translation_of: Web/JavaScript/EventLoop ---- -
{{JsSidebar("Advanced")}}
- -
JavaScript poseé un modelo de concurrencia basado en un "loop de eventos". Este modelo es bastante diferente al modelo de otros lenguajes como C o Java.
- -
- -

Conceptos de un programa en ejecución

- -

Las siguientes secciones explican un modelo teórico. Los motores modernos de JavaScript implementan y optimizan fuertemente la semántica descrita a continuación.

- -

Representación visual

- -

Stack, heap, queue

- -

Pila (Stack)

- -

Las llamadas a función forman una pila de frames. Un frame encapsula información como el contexto y las variables locales de una función.

- -
function f(b){
-  var a = 12;
-  return a+b+35;
-}
-
-function g(x){
-  var m = 4;
-  return f(m*x);
-}
-
-g(21);
-
- -

Cuando se llama a g, un primer frame es creado, el cual contiene g argumentos y variables locales. Cuando g llama a f, un segundo frame es creado y colocado encima del primero, con f argumentos y variables locales. Cuando f termina de ejecutarse, el último frame (en este caso f) es sacado de la pila (déjando solo el frame de g). Cuando g termina de ejecutarse, la pila está vacía.

- -

Montículo (Heap)

- -

Los objetos son colocados en un montículo, el cual, como su nombre lo dice, denota una gran región de memoria, mayormente sin estructura u orden.

- -

Cola (Queue)

- -

Un programa en ejecución en JavaScript contiene una cola de mensajes, la cual es una lista de mensajes a ser procesados. Cada mensaje se asocia con una función. Cuando la pila está vacía, un mensaje es sacado de la cola y procesado. Procesar un mensaje consiste en llamar a la función asociada al mensaje (y por ende crear una frame en la pila). El mensaje procesado termina cuando la pila está vacía de nuevo.

- -

Loop de eventos

- -

El loop de eventos obtiene su nombre por la forma en que es usualmente implementado, la cual generalmente se parece a:

- -
while(queue.waitForMessage()){
-  queue.processNextMessage();
-}
- -

queue.waitForMessage espera de manera síncrona a que llegue un mensaje si no hay ninguno actualmente.

- -

"Ejecutar-hasta-completar"

- -

Cada mensaje es procesado completamente antes que cualquier otro mensaje sea procesado. Esto ofrece algunas propiedades convenientes al momento de pensar en un programa, incluido el hecho de que cada vez que una función se ejecuta, ésta no puede ser terminada y se ejecutará totalmente antes de que cualquier otro código se ejecute (y de este modo pueda modificar la información que la función manipula). Esto es diferente de C, por ejemplo, donde si una función se ejecuta en un hilo, esta puede ser detenida en cualquier punto para ejecutar código en otro hilo.

- -

Una desventaja de este modelo es que, si un mensaje toma mucho tiempo en completarse, la aplicación es incapaz de procesar las interacciones de usuario, tales como clicks o scrolling. El navegador mitiga esta desventaja con el mensaje "un script esta tomando mucho tiempo en ejecutarse". Una buena práctica es hacer que el procesamiento del mensaje sea corto y, si es posible, dividir une mensaje en varios más.

- -

Añadiendo mensajes

- -

En los navegadores web, los mensajes son añadidos cada vez que un evento ocurre y hay un escuchador de eventos asociado a él. Si no hay un escuchador, el evento se pierde. De este modo, al hacer click en un elemento con un manejador de eventos tipo click, se añadirá un mensaje. Lo mismo sucede en otros tipos de eventos.

- -

Al llamar setTimeout se añadirá un mensaje a la cola después de el tiempo especificado como segundo parámetro. Si no hay ningún otro mensaje en la cola, el mensaje es procesado en el momento; sin embargo, si hay mensajes en la cola, el mensaje de setTimeout tendrá que esperar a que los otros mensajes sean procesados. Por esta razón el segundo parámetro indica el tiempo mínimo tiempo esperado y no es una garantía.

- -

Cero retraso

- -

Cero retraso no significa que una llamada a una función (call back) se disparará después de cero milisegundos. Al llamar {{domxref("WindowTimers.setTimeout", "setTimeout")}} con un retraso de 0 (cero) milisegundos, no se ejecuta la llamada de la función después de el intervado dado. La ejecución depende del número de tareas en espera en la cola. En el ejemplo de abajo el mensaje "this is just a message" será escrito en la terminal antes de que el mensaje de la llamada a la función sea procesado, esto es por que el retraso es el tiempo mínimo requerido para que el programa procese la petición, pero no es un tiempo garantizado.

- -
(function () {
-
-  console.log('this is the start');
-
-  setTimeout(function cb() {
-    console.log('this is a msg from call back');
-  });
-
-  console.log('this is just a message');
-
-  setTimeout(function cb1() {
-    console.log('this is a msg from call back1');
-  }, 0);
-
-  console.log('this is the  end');
-
-})();
-
-// "this is the start"
-// "this is just a message"
-// "this is the end"
-// cabe notar que la función retorna en este punto (undefined)
-// "this is a msg from call back"
-// "this is a msg from call back1"
-
- -

Varios programas comunicandose al mismo tiempo

- -

Un web worker o cross-origin iframe tiene su propia pila, montículo y cola de mensajes. Dos programas diferentes solo se pueden comunicar enviando mensajes a través del método postMessage. Este método añade un mensaje al otro programa si éste último escucha eventos de tipo message.

- -

Nunca se interrumpe

- -

Una propiedad muy interesante del modelo de loop de eventos es que JavaScript, a diferencia de otros lenguajes, nunca interrumpe otros programas en ejecución. Manejar operaciones de I/O (entrada/salida) es normalmente hecho a través de eventos y llamadas a función, de modo que cuando la aplicación, por ejemplo, está esperando por el retorno de una consulta IndexedDB o una petición XHR, ésta puede continuar procesando otras cosas como interacciones con el usuario (e.g. clicks).

- -

Excepciones a esta regla existe en versiones anteriores del lenguaje, tales como alert o XHR síncrono, pero es considerada una buena práctica evitar su uso. Finalmente, hay que estar conscientes que hay excepciones a las excepciones (pero son usualmente errores de implementación mas que otra cosa).

diff --git a/files/es/web/javascript/eventloop/index.md b/files/es/web/javascript/eventloop/index.md new file mode 100644 index 00000000000000..4997ad066e8f80 --- /dev/null +++ b/files/es/web/javascript/eventloop/index.md @@ -0,0 +1,110 @@ +--- +title: Modelo de concurrencia y loop de eventos +slug: Web/JavaScript/EventLoop +tags: + - Avanzado + - JavaScript +translation_of: Web/JavaScript/EventLoop +--- +{{JsSidebar("Advanced")}}JavaScript poseé un modelo de concurrencia basado en un "loop de eventos". Este modelo es bastante diferente al modelo de otros lenguajes como C o Java. + +## Conceptos de un programa en ejecución + +Las siguientes secciones explican un modelo teórico. Los motores modernos de JavaScript implementan y optimizan fuertemente la semántica descrita a continuación. + +### Representación visual + +![Stack, heap, queue](the_javascript_runtime_environment_example.svg) + +### Pila (Stack) + +Las llamadas a función forman una pila de _frames._ Un frame encapsula información como el contexto y las variables locales de una función. + +```js +function f(b){ + var a = 12; + return a+b+35; +} + +function g(x){ + var m = 4; + return f(m*x); +} + +g(21); +``` + +Cuando se llama a `g`, un primer frame es creado, el cual contiene `g` argumentos y variables locales. Cuando `g` llama a `f`, un segundo frame es creado y colocado encima del primero, con `f` argumentos y variables locales. Cuando `f` termina de ejecutarse, el último frame (en este caso `f`) es sacado de la pila (déjando solo el frame de `g`). Cuando `g` termina de ejecutarse, la pila está vacía. + +### Montículo (Heap) + +Los objetos son colocados en un montículo, el cual, como su nombre lo dice, denota una gran región de memoria, mayormente sin estructura u orden. + +### Cola (Queue) + +Un programa en ejecución en JavaScript contiene una cola de mensajes, la cual es una lista de mensajes a ser procesados. Cada mensaje se asocia con una función. Cuando la pila está vacía, un mensaje es sacado de la cola y procesado. Procesar un mensaje consiste en llamar a la función asociada al mensaje (y por ende crear una frame en la pila). El mensaje procesado termina cuando la pila está vacía de nuevo. + +## Loop de eventos + +El `loop de eventos` obtiene su nombre por la forma en que es usualmente implementado, la cual generalmente se parece a: + +```js +while(queue.waitForMessage()){ + queue.processNextMessage(); +} +``` + +`queue.waitForMessage` espera de manera síncrona a que llegue un mensaje si no hay ninguno actualmente. + +### "Ejecutar-hasta-completar" + +Cada mensaje es procesado completamente antes que cualquier otro mensaje sea procesado. Esto ofrece algunas propiedades convenientes al momento de pensar en un programa, incluido el hecho de que cada vez que una función se ejecuta, ésta no puede ser terminada y se ejecutará totalmente antes de que cualquier otro código se ejecute (y de este modo pueda modificar la información que la función manipula). Esto es diferente de C, por ejemplo, donde si una función se ejecuta en un hilo, esta puede ser detenida en cualquier punto para ejecutar código en otro hilo. + +Una desventaja de este modelo es que, si un mensaje toma mucho tiempo en completarse, la aplicación es incapaz de procesar las interacciones de usuario, tales como clicks o scrolling. El navegador mitiga esta desventaja con el mensaje "un script esta tomando mucho tiempo en ejecutarse". Una buena práctica es hacer que el procesamiento del mensaje sea corto y, si es posible, dividir une mensaje en varios más. + +### Añadiendo mensajes + +En los navegadores web, los mensajes son añadidos cada vez que un evento ocurre y hay un escuchador de eventos asociado a él. Si no hay un escuchador, el evento se pierde. De este modo, al hacer click en un elemento con un manejador de eventos tipo click, se añadirá un mensaje. Lo mismo sucede en otros tipos de eventos. + +Al llamar [`setTimeout`](/es/docs/Web/API/setTimeout "/es/docs/Web/API/setTimeout") se añadirá un mensaje a la cola después de el tiempo especificado como segundo parámetro. Si no hay ningún otro mensaje en la cola, el mensaje es procesado en el momento; sin embargo, si hay mensajes en la cola, el mensaje de `setTimeout `tendrá que esperar a que los otros mensajes sean procesados. Por esta razón el segundo parámetro indica el tiempo mínimo tiempo esperado y no es una garantía. + +### Cero retraso + +Cero retraso no significa que una llamada a una función (call back) se disparará después de cero milisegundos. Al llamar {{domxref("WindowTimers.setTimeout", "setTimeout")}} con un retraso de 0 (cero) milisegundos, no se ejecuta la llamada de la función después de el intervado dado. La ejecución depende del número de tareas en espera en la cola. En el ejemplo de abajo el mensaje "this is just a message" será escrito en la terminal antes de que el mensaje de la llamada a la función sea procesado, esto es por que el retraso es el tiempo mínimo requerido para que el programa procese la petición, pero no es un tiempo garantizado. + +```js +(function () { + + console.log('this is the start'); + + setTimeout(function cb() { + console.log('this is a msg from call back'); + }); + + console.log('this is just a message'); + + setTimeout(function cb1() { + console.log('this is a msg from call back1'); + }, 0); + + console.log('this is the end'); + +})(); + +// "this is the start" +// "this is just a message" +// "this is the end" +// cabe notar que la función retorna en este punto (undefined) +// "this is a msg from call back" +// "this is a msg from call back1" +``` + +### Varios programas comunicandose al mismo tiempo + +Un web worker o cross-origin `iframe` tiene su propia pila, montículo y cola de mensajes. Dos programas diferentes solo se pueden comunicar enviando mensajes a través del método [`postMessage`](/es/docs/Web/API/Window/postMessage "/es/docs/Web/API/Window/postMessage"). Este método añade un mensaje al otro programa si éste último escucha eventos de tipo `message`. + +## Nunca se interrumpe + +Una propiedad muy interesante del modelo de loop de eventos es que JavaScript, a diferencia de otros lenguajes, nunca interrumpe otros programas en ejecución. Manejar operaciones de I/O (entrada/salida) es normalmente hecho a través de eventos y llamadas a función, de modo que cuando la aplicación, por ejemplo, está esperando por el retorno de una consulta [IndexedDB](/es/docs/Web/API/IndexedDB_API "/es/docs/IndexedDB") o una petición [XHR](/es/docs/Web/API/XMLHttpRequest "/es/docs/DOM/XMLHttpRequest"), ésta puede continuar procesando otras cosas como interacciones con el usuario (e.g. clicks). + +Excepciones a esta regla existe en versiones anteriores del lenguaje, tales como `alert` o XHR síncrono, pero es considerada una buena práctica evitar su uso. Finalmente, hay que estar conscientes que hay [excepciones](https://stackoverflow.com/questions/2734025/is-javascript-guaranteed-to-be-single-threaded/2734311#2734311) a las excepciones (pero son usualmente errores de implementación mas que otra cosa). diff --git a/files/es/web/javascript/inheritance_and_the_prototype_chain/index.html b/files/es/web/javascript/inheritance_and_the_prototype_chain/index.html deleted file mode 100644 index e221b48a052057..00000000000000 --- a/files/es/web/javascript/inheritance_and_the_prototype_chain/index.html +++ /dev/null @@ -1,411 +0,0 @@ ---- -title: Herencia y la cadena de prototipos -slug: Web/JavaScript/Inheritance_and_the_prototype_chain -tags: - - Herencia - - Intermedio - - JavaScript - - Programación orientada a objetos -translation_of: Web/JavaScript/Inheritance_and_the_prototype_chain -original_slug: Web/JavaScript/Herencia_y_la_cadena_de_protipos ---- -
{{jsSidebar("Advanced")}}
- -

JavaScript provoca cierta confusión en desarrolladores con experiencia en lenguajes basados en clases (como Java o C++), por ser dinámico y no proporcionar una implementación de clases en sí mismo (la palabra clave class se introdujo en ES2015, pero sólo para endulzar la sintaxis, ya que JavaScript sigue estando basado en prototipos).

- -

En lo que a herencia se refiere, JavaScript sólo tiene una estructura: objetos. Cada objeto tiene una propiedad privada (referida como su [[Prototype]]) que mantiene un enlace a otro objeto llamado su prototipo. Ese objeto prototipo tiene su propio prototipo, y así sucesivamente hasta que se alcanza un objeto cuyo prototipo es null. Por definición, null no tiene prototipo, y actúa como el enlace final de esta cadena de prototipos.

- -

Casi todos los objetos en JavaScript son instancias de {{jsxref("Object")}} que se sitúa a la cabeza de la cadena de prototipos.

- -

A pesar de que a menudo esto se considera como una de las principales debilidades de JavaScript, el modelo de herencia de prototipos es de hecho más potente que el modelo clásico. Por ejemplo, es bastante simple construir un modelo clásico a partir de un modelo de prototipos.

- -

Herencia con la cadena de prototipos

- -

Heredando propiedades

- -

Los objetos en JavaScript son "contenedores" dinámicos de propiedades (referidas como sus propiedades particulares). Los objetos en JavaScript poseen un enlace a un objeto prototipo. Cuando intentamos acceder a una propiedad de un objeto, la propiedad no sólo se busca en el propio objeto sino también en el prototipo del objeto, en el prototipo del prototipo, y así sucesivamente hasta que se encuentre una propiedad que coincida con el nombre o se alcance el final de la cadena de prototipos.

- -
-

Siguiendo el estándar ECMAScript, la notación algunObjeto.[[Prototype]] se usa para designar el prototipo de algunObjeto. A partir de ECMAScript 2015, se accede al [[Prototype]] utilizando los accesores {{jsxref("Object.getPrototypeOf()")}} y {{jsxref("Object.setPrototypeOf()")}}. Esto es equivalente a la propiedad JavaScript __proto__ que no es estándar pero es el de-facto implementado por los navegadores.

- -

No debe confundirse con la propiedad de las funciones func.prototype, que en cambio especifican el [[Prototype]] a asignar a todas las instancias de los objetos creados por la función dada cuando se utiliza como un constructor. La propiedad Object.prototype representa el prototipo del objeto {{jsxref("Object")}}.

-
- -

Esto es lo que ocurre cuando intentamos acceder a una propiedad:

- -
// Supongamos que tenemos un objeto o, con propiedades a y b:
-// {a: 1, b: 2}
-// o.[[Prototype]] tiene propiedades b y c:
-// {b: 3, c: 4}
-// Finalmente, o.[[Prototype]].[[Prototype]] es null.
-// Este es el final de la cadena de prototipos, ya que null,
-// por definición, no tiene [[Prototype]].
-// Por tanto, la cadena completa de prototipos se vería como:
-// {a:1, b:2} ---> {b:3, c:4} ---> null
-
-console.log(o.a); // 1
-// ¿Hay una propiedad 'a' en o? Sí, y su valor es 1.
-
-console.log(o.b); // 2
-// ¿Hay una propiedad 'b' en o? Sí, y su valor es 2.
-// El prototipo también tiene una propiedad 'b', pero no se ha visitado.
-// Esto se llama "solapamiento de propiedades"
-
-console.log(o.c); // 4
-// ¿Hay una propiedad 'c' en o? No, comprobamos su prototipo.
-// ¿Hay una propiedad 'c' en o.[[Prototype]]? Sí, y su valor es 4.
-
-console.log(o.d); // undefined
-// ¿Hay una propiedad 'd' en o? No, comprobamos su prototipo.
-//  ¿Hay una propiedad 'd' en o.[[Prototype]]? No, comprobamos su prototipo.
-// o.[[Prototype]].[[Prototype]] es null, paramos de buscar.
-// No se encontró la propiedad, se devuelve undefined
-
- -

Dar valor a una propiedad de un objeto crea una propiedad. La única excepción a las reglas de funcionamiento de obtener y dar valores ocurre cuando hay una propiedad heredada con un getter o un setter.

- -

Heredando "métodos"

- -

JavaScript no tiene "métodos" en la forma que los lenguajes basados en clases los define. En JavaScript, cualquier función puede añadirse a un objeto como una propiedad. Una función heredada se comporta como cualquier otra propiedad, viéndose afectada por el solapamiento de propiedades como se muestra anteriormente (siendo, en este caso, una especie de redefinición de métodos).

- -

Cuando una función heredada se ejecuta, el valor de this apunta al objeto que hereda, no al prototipo en el que la función es una propiedad.

- -
var o = {
-  a: 2,
-  m: function(b){
-    return this.a + 1;
-  }
-};
-
-console.log(o.m()); // 3
-// Cuando en este caso se llama a o.m, 'this' se refiere a o
-
-var p = Object.create(o);
-// p es un objeto que hereda de o
-
-p.a = 12; // crea una propiedad 'a' en p
-console.log(p.m()); // 13
-// cuando se llama a p.m, 'this' se refiere a p.
-// De esta manera, cuando p hereda la función m de o,
-// 'this.a' significa p.a, la propiedad 'a' de p
-
- -

Usando prototipos en JavaScript

- -

Veamos lo que sucede detrás de escena detalladamente.

- -

En JavaScript, como se mencionó anteriormente, las funciones pueden tener propiedades. Todas las funciones tienen una propiedad especial llamada prototype. Por favor ten en cuenta que el siguiente código es autónomo (es seguro asumir que no hay otro JavaScript en la página web mas que el siguiente). Para la mejor experiencia de aprendizaje, es altamente recomendable que abras una consola (la cual, en Chrome y Firefox, se puede abrir presionando Ctrl+Shift+I), navegando hasta la pestaña "console", copiando y pegando el siguiente código JavaScript, y ejecutándolo presionando la tecla Enter/Return.

- -
function hacerAlgo(){}
-console.log( hacerAlgo.prototype );
-// No importa cómo declares la función, una
-// función en JavaScript siempre tendrá una
-// propiedad prototype predeterminada.
-var hacerAlgo = function(){};
-console.log( hacerAlgo.prototype );
- -

Como acabamos de ver, hacerAlgo() tiene una propiedad prototype predeterminada, como lo demuestra la consola. Después de ejecutar este código, la consola debería haber mostrado un parecido a esto.

- -
{
-    constructor: ƒ hacerAlgo(),
-    __proto__: {
-        constructor: ƒ Object(),
-        hasOwnProperty: ƒ hasOwnProperty(),
-        isPrototypeOf: ƒ isPrototypeOf(),
-        propertyIsEnumerable: ƒ propertyIsEnumerable(),
-        toLocaleString: ƒ toLocaleString(),
-        toString: ƒ toString(),
-        valueOf: ƒ valueOf()
-    }
-}
- -

Podemos añadir propiedades al prototipo de hacerAlgo(), como se muestra a continuación.

- -
function hacerAlgo(){}
-hacerAlgo.prototype.foo = "bar";
-console.log( hacerAlgo.prototype );
- -

El resultado:

- -
{
-    foo: "bar",
-    constructor: ƒ hacerAlgo(),
-    __proto__: {
-        constructor: ƒ Object(),
-        hasOwnProperty: ƒ hasOwnProperty(),
-        isPrototypeOf: ƒ isPrototypeOf(),
-        propertyIsEnumerable: ƒ propertyIsEnumerable(),
-        toLocaleString: ƒ toLocaleString(),
-        toString: ƒ toString(),
-        valueOf: ƒ valueOf()
-    }
-}
- -

Ahora podemos usar el operador new para crear una instancia de hacerAlgo() basado en este prototipo. Para usar el operador new, llama la función normalmente pero añadiendo el prefijo new. Llamar a la función con el operador new devuelve un objeto que es una instancia de la función. Entonces las propiedades pueden ser añadidas a este objeto.

- -

Intenta el siguiente código:

- -
function hacerAlgo(){}
-hacerAlgo.prototype.foo = "bar"; // añadir una propiedad al prototipo
-var hacerUnaInstancia = new hacerAlgo();
-hacerUnaInstancia.prop = "un valor"; // añadir una propiedad al objeto
-console.log( hacerUnaInstancia );
- -

El resultado es similar a lo siguiente:

- -
{
-    prop: "un valor",
-    __proto__: {
-        foo: "bar",
-        constructor: ƒ hacerAlgo(),
-        __proto__: {
-            constructor: ƒ Object(),
-            hasOwnProperty: ƒ hasOwnProperty(),
-            isPrototypeOf: ƒ isPrototypeOf(),
-            propertyIsEnumerable: ƒ propertyIsEnumerable(),
-            toLocaleString: ƒ toLocaleString(),
-            toString: ƒ toString(),
-            valueOf: ƒ valueOf()
-        }
-    }
-}
- -

Como acabamos de ver, el __proto__ de hacerUnaInstancia es hacerAlgo.prototype. Pero, ¿qué hace esto? Cuando accedes a la propiedad de hacerUnaInstancia, el navegador primero revisa si hacerUnaInstancia tiene esa propiedad.

- -

Si hacerUnaInstancia no tiene la propiedad, entonces el navegador busca por la propiedad en el __proto__ de hacerUnaInstancia (también conocido como hacerAlgo.prototype). Si el __proto__ de hacerUnaInstancia tiene la propiedad buscada, entonces la propiedad en el __proto__ de hacerUnaInstancia es usada.

- -

De otra manera, si el __proto__ de hacerUnaInstancia no tiene la propiedad, entonces el __proto__ de __proto__ de hacerUnaInstancia es revisado para la propiedad. Por defecto, el __proto__ de la propieda prototype de cualquier función es window.Object.prototype. Entonces, el __proto__ de el __proto__ de hacerUnaInstancia (conocido como el __proto__ de hacerAlgo.prototype (conocido como Object.prototype)) es entonces revisado por la propiedad que se está buscando.

- -

Si la propiedad no es encontrada en el __proto__ de el __proto__ de hacerUnaInstancia, entonces el __proto__ de el __proto__ de el __proto__ de hacerUnaInstancia es revisado. Sin embargo, hay un problema: el __proto__ de el __proto__ de el __proto__ de el __proto__ de hacerUnaInstancia no existe. Entonces y sólo entonces, despues de que toda la cadena de prototipos de __proto__'s es revisada, y no haya mas __proto__s el navegador afirma que la propiedad no existe y concluye que el valor de la propiedad es undefined.

- -

Vamos a intentar introduciendo más código en la consola:

- -
function hacerAlgo(){}
-hacerAlgo.prototype.foo = "bar";
-var hacerUnaInstancia = new hacerAlgo();
-hacerUnaInstancia.prop = "un valor";
-console.log("hacerUnaInstancia.prop:      " + hacerUnaInstancia.prop);
-console.log("hacerUnaInstancia.foo:       " + hacerUnaInstancia.foo);
-console.log("hacerAlgo.prop:           " + hacerAlgo.prop);
-console.log("hacerAlgo.foo:            " + hacerAlgo.foo);
-console.log("hacerAlgo.prototype.prop: " + hacerAlgo.prototype.prop);
-console.log("hacerAlgo.prototype.foo:  " + hacerAlgo.prototype.foo);
- -

El resultado es el siguiente:

- -
hacerUnaInstancia.prop:      un valor
-hacerUnaInstancia.foo:       bar
-hacerAlgo.prop:              undefined
-hacerAlgo.foo:               undefined
-hacerAlgo.prototype.prop:    undefined
-hacerAlgo.prototype.foo:     bar
- -

Maneras diferentes de crear objetos y la cadena de prototipos resultante

- -

Objetos creados mediante estructuras sintácticas

- -
var o = {a: 1};
-
-// El objeto recién creado o tiene Object.prototype como su [[Prototype]]
-// o no tiene ninguna propiedad llamada 'hasOwnProperty'
-// hasOwnProperty es una propiedad propia de Object.prototype.
-// Entonces o hereda hasOwnProperty de Object.prototype
-// Object.prototype es null como su prototype.
-// o ---> Object.prototype ---> null
-
-var a = ["yo", "whadup", "?"];
-
-// Arrays hereda de Array.prototype
-// (que tiene métodos como indexOf, forEach, etc.)
-// La cadena de prototipados sería:
-// a ---> Array.prototype ---> Object.prototype ---> null
-
-function f(){
-  return 2;
-}
-
-// Las funciones heredan de Function.prototype
-// (que tiene métodos como call, bind, etc.)
-// f ---> Function.prototype ---> Object.prototype ---> null
-
- -

Con un constructor

- -

Un "constructor" en JavaScript es "solo" una función que pasa a ser llamada con el operador new.

- -
function Graph() {
-  this.vertices = [];
-  this.edges = [];
-}
-
-Graph.prototype = {
-  addVertex: function(v){
-    this.vertices.push(v);
-  }
-};
-
-var g = new Graph();
-// g es un objeto con las propiedades 'vértices' y 'edges'.
-// g.[[Prototype]] es el valor de Graph.prototype cuando new Graph() es ejecutado.
-
- -

Con Object.create

- -

ECMAScript 5 Introdujo un nuevo método: {{jsxref("Object.create()")}}. Llamando este método creas un nuevo objeto. El prototype de este objeto es el primer argumento de la función:

- -
var a = {a: 1};
-// a ---> Object.prototype ---> null
-
-var b = Object.create(a);
-// b ---> a ---> Object.prototype ---> null
-console.log(b.a); // 1 (heredado)
-
-var c = Object.create(b);
-// c ---> b ---> a ---> Object.prototype ---> null
-
-var d = Object.create(null);
-// d ---> null
-console.log(d.hasOwnProperty);
-// undefined, por que d no hereda de Object.prototype
-
- -
-

Con la palabra reservada class

- -

ECMAScript 2015 introduce un nuevo set de palabras reservadas que implementan clases. Aunque estos constructores lucen más familiares para los desarrolladores de lenguajes basados en clases, aun así no son clases. JavaScript sigue estando basado en prototipos. Los nuevos keywords incluyen {{jsxref("Statements/class", "class")}}, {{jsxref("Classes/constructor", "constructor")}}, {{jsxref("Classes/static", "static")}}, {{jsxref("Classes/extends", "extends")}}, and {{jsxref("Operators/super", "super")}}.

- -
"use strict";
-
-class Polygon {
-  constructor(height, width) {
-    this.height = height;
-    this.width = width;
-  }
-}
-
-class Square extends Polygon {
-  constructor(sideLength) {
-    super(sideLength, sideLength);
-  }
-  get area() {
-    return this.height * this.width;
-  }
-  set sideLength(newLength) {
-    this.height = newLength;
-    this.width = newLength;
-  }
-}
-
-var square = new Square(2);
-
- -

Rendimiento

- -

El tiempo de búsqueda para las propiedades que están en lo alto de la cadena de prototipo puede tener un impacto negativo en el rendimiento, y esto puede ser significativo en el código donde el rendimiento es crítico. Además, tratar de acceder a las propiedades inexistentes siempre atravesara la cadena de prototipos completamente.

- -

También, cuando iteramos sobre las propiedades de un objeto, cada propiedad enumerable que se encuentra en la cadena de prototipo será enumerada.

- -

Para comprobar si un objeto tiene una propiedad definida en sí mismo y no en alguna parte de su cadena de prototipo, Es necesario usar para esto el método hasOwnProperty que todos los objetos heredan de Object.prototype.

- -

hasOwnProperty es la única cosa en JavaScript que se ocupa de las propiedades y no atraviesa la cadena de prototipos.

- -

Nota: Esto no es suficiente para chequear si una propiedad esta undefined. la propiedad podría existir, pero el valor justamente sucede que esta seteado como undefined.

-
- -

Malas practicas: Extensión de prototipos nativos

- -

Una mala característica que a menudo se usa, es extender Object.prototype o uno de los otros pre-incorporados prototypes.

- -

Esta técnica se llama monkey patching y rompe la encapsulación. Si bien, es utilizado por librerías como Prototype.js, no hay una buena razón para saturar los tipos pre-incorporados con funcionalidades adicionales no estándar.

- -

La única buena razón para extender los pre-incorporados prototipos es modificar las funcionalidades nuevas de los motores de JavaScript; por ejemplo:

- -

Array.forEach, etc.

- -

Ejemplo

- -

B heredará de A:

- -
function A(a){
-  this.varA = a;
-}
-
-// Cual es el propósito de incluir varA en el prototipo si A.prototype.varA siempre va a ser la sombra de
-// this.varA, dada la definición de la función A arriba?
-A.prototype = {
-  varA : null,  // No deberíamos atacar varA desde el prototipo como haciendo nada?
-      // Tal vez intentando una optimización al asignar espacios ocultos en las clases?
-      // https://developers.google.com/speed/articles/optimizing-javascript#Initializing instanciar variables
-      // podría ser válido si varA no fuera inicializado únicamente por cada instancia.
-  doSomething : function(){
-    // ...
-  }
-}
-
-function B(a, b){
-  A.call(this, a);
-  this.varB = b;
-}
-B.prototype = Object.create(A.prototype, {
-  varB : {
-    value: null,
-    enumerable: true,
-    configurable: true,
-    writable: true
-  },
-  doSomething : {
-    value: function(){ // override
-      A.prototype.doSomething.apply(this, arguments); // call super
-      // ...
-    },
-    enumerable: true,
-    configurable: true,
-    writable: true
-  }
-});
-B.prototype.constructor = B;
-
-var b = new B();
-b.doSomething();
-
- -

Las partes importantes son:

- - - -

prototype y Object.getPrototypeOf

- -

JavaScript es un poco confuso para desarrolladores que vienen de lenguajes como Java o C++, ya que todo es dinámico, en todo momento de la ejecución, y no tiene clases en lo absoluto. Todo es solamente instancias (objetos). Incluso las "clases" que creamos, son solo funciones (objetos).

- -

Probablemente notaste que nuestra función A tiene una propiedad especial llamada prototype. Esta propiedad especial funciona con el operador de JavaScript new. La referencia al prototipo objeto es copiada al interno [[Prototype]] propiedad de la instancia new. Por ejemplo, cuando creas una variable var a1 = new A(), JavaScript (después de haber creado el objeto en memoria y antes de correr function A() con this definido a él) setea a1.[[Prototype]] = A.prototype. Cuando a continuación accedes a las propiedades de la instancia, JavaScript primero chequea si existen en el objeto directamente, y si no, mira en el [[Prototype]]. Esto significa que todo lo que definas en el prototipo es efectivamente compartido a todas las instancias, e incluso después puedes cambiar partes del prototipo y que todos los cambios se hagan en todas las instancias.

- -

Si, en el ejemplo de arriba, pones var a1 = new A(); var a2 = new A(); entonces a1.doSomething se referiría a Object.getPrototypeOf(a1).doSomething, que seria lo mismo que A.prototype.doSomething que definiste, i.e. Object.getPrototypeOf(a1).doSomething == Object.getPrototypeOf(a2).doSomething == A.prototype.doSomething.

- -

resumiendo, prototype es para tipos, mientras que Object.getPrototypeOf() es lo mismo para instancias.

- -

[[Prototype]] es visto como recursivo, i.e. a1.doSomething, Object.getPrototypeOf(a1).doSomething, Object.getPrototypeOf(Object.getPrototypeOf(a1)).doSomething etc., hasta que se encuentra o Object.getPrototypeOf retornará null.

- -

Entonces, cuando llamas

- -
var o = new Foo();
- -

JavaScript en realidad hace

- -
var o = new Object();
-o.[[Prototype]] = Foo.prototype;
-Foo.call(o);
- -

(o algo similar) y cuando después haces

- -
o.someProp;
- -

chequea si o tiene una propiedad someProp. Si no, busca en Object.getPrototypeOf(o).someProp y si ahí no existe, busca en Object.getPrototypeOf(Object.getPrototypeOf(o)).someProp y así sucesivamente.

- -
-

En conclusión

- -

Es esencial entender el modelo de prototipado por instancias antes de escribir código complejo que hace uso de esto. También, sé consciente del largo de la cadena de prototipado en tu código y romperlo si es necesario para evitar posibles problemas de rendimiento. Adicionalmente, el prototipo nativo nunca debería ser extendido a menos que esto sea por motivo de compatibilidad con nuevas versiones de JavaScript.

-
diff --git a/files/es/web/javascript/inheritance_and_the_prototype_chain/index.md b/files/es/web/javascript/inheritance_and_the_prototype_chain/index.md new file mode 100644 index 00000000000000..ce58c0ea6a8d91 --- /dev/null +++ b/files/es/web/javascript/inheritance_and_the_prototype_chain/index.md @@ -0,0 +1,430 @@ +--- +title: Herencia y la cadena de prototipos +slug: Web/JavaScript/Inheritance_and_the_prototype_chain +tags: + - Herencia + - Intermedio + - JavaScript + - Programación orientada a objetos +translation_of: Web/JavaScript/Inheritance_and_the_prototype_chain +original_slug: Web/JavaScript/Herencia_y_la_cadena_de_protipos +--- +{{jsSidebar("Advanced")}} + +JavaScript provoca cierta confusión en desarrolladores con experiencia en lenguajes basados en clases (como Java o C++), por ser dinámico y no proporcionar una implementación de clases en sí mismo (la palabra clave `class` se introdujo en ES2015, pero sólo para endulzar la sintaxis, ya que JavaScript sigue estando basado en prototipos). + +En lo que a herencia se refiere, JavaScript sólo tiene una estructura: objetos. Cada objeto tiene una propiedad privada (referida como su [[Prototype]]) que mantiene un enlace a otro objeto llamado su **prototipo**. Ese objeto prototipo tiene su propio prototipo, y así sucesivamente hasta que se alcanza un objeto cuyo prototipo es `null`. Por definición, `null` no tiene prototipo, y actúa como el enlace final de esta **cadena de prototipos**. + +Casi todos los objetos en JavaScript son instancias de {{jsxref("Object")}} que se sitúa a la cabeza de la cadena de prototipos. + +A pesar de que a menudo esto se considera como una de las principales debilidades de JavaScript, el modelo de herencia de prototipos es de hecho más potente que el modelo clásico. Por ejemplo, es bastante simple construir un modelo clásico a partir de un modelo de prototipos. + +## Herencia con la cadena de prototipos + +### Heredando propiedades + +Los objetos en JavaScript son "contenedores" dinámicos de propiedades (referidas como sus **propiedades particulares**). Los objetos en JavaScript poseen un enlace a un objeto prototipo. Cuando intentamos acceder a una propiedad de un objeto, la propiedad no sólo se busca en el propio objeto sino también en el prototipo del objeto, en el prototipo del prototipo, y así sucesivamente hasta que se encuentre una propiedad que coincida con el nombre o se alcance el final de la cadena de prototipos. + +> **Nota:** Siguiendo el estándar ECMAScript, la notación `algunObjeto.[[Prototype]]` se usa para designar el prototipo de `algunObjeto.` A partir de ECMAScript 2015, se accede al `[[Prototype]]` utilizando los accesores {{jsxref("Object.getPrototypeOf()")}} y {{jsxref("Object.setPrototypeOf()")}}. Esto es equivalente a la propiedad JavaScript `__proto__` que no es estándar pero es el de-facto implementado por los navegadores. +> +> No debe confundirse con la propiedad de las funciones `func.prototype`, que en cambio especifican el `[[Prototype]]` a asignar a todas las instancias de los objetos creados por la función dada cuando se utiliza como un constructor. La propiedad **`Object.prototype`** representa el prototipo del objeto {{jsxref("Object")}}. + +Esto es lo que ocurre cuando intentamos acceder a una propiedad: + +```js +// Supongamos que tenemos un objeto o, con propiedades a y b: +// {a: 1, b: 2} +// o.[[Prototype]] tiene propiedades b y c: +// {b: 3, c: 4} +// Finalmente, o.[[Prototype]].[[Prototype]] es null. +// Este es el final de la cadena de prototipos, ya que null, +// por definición, no tiene [[Prototype]]. +// Por tanto, la cadena completa de prototipos se vería como: +// {a:1, b:2} ---> {b:3, c:4} ---> null + +console.log(o.a); // 1 +// ¿Hay una propiedad 'a' en o? Sí, y su valor es 1. + +console.log(o.b); // 2 +// ¿Hay una propiedad 'b' en o? Sí, y su valor es 2. +// El prototipo también tiene una propiedad 'b', pero no se ha visitado. +// Esto se llama "solapamiento de propiedades" + +console.log(o.c); // 4 +// ¿Hay una propiedad 'c' en o? No, comprobamos su prototipo. +// ¿Hay una propiedad 'c' en o.[[Prototype]]? Sí, y su valor es 4. + +console.log(o.d); // undefined +// ¿Hay una propiedad 'd' en o? No, comprobamos su prototipo. +// ¿Hay una propiedad 'd' en o.[[Prototype]]? No, comprobamos su prototipo. +// o.[[Prototype]].[[Prototype]] es null, paramos de buscar. +// No se encontró la propiedad, se devuelve undefined +``` + +Dar valor a una propiedad de un objeto crea una propiedad. La única excepción a las reglas de funcionamiento de obtener y dar valores ocurre cuando hay una propiedad heredada con un [getter o un setter](/es/docs/Web/JavaScript/Guide/Working_with_Objects#Defining_getters_and_setters "Defining Getters and Setters"). + +### Heredando "métodos" + +JavaScript no tiene "métodos" en la forma que los lenguajes basados en clases los define. En JavaScript, cualquier función puede añadirse a un objeto como una propiedad. Una función heredada se comporta como cualquier otra propiedad, viéndose afectada por el solapamiento de propiedades como se muestra anteriormente (siendo, en este caso, una especie de _redefinición de métodos_). + +Cuando una función heredada se ejecuta, el valor de [`this`](/es/docs/Web/JavaScript/Reference/Operators/this "this") apunta al objeto que hereda, no al prototipo en el que la función es una propiedad. + +```js +var o = { + a: 2, + m: function(b){ + return this.a + 1; + } +}; + +console.log(o.m()); // 3 +// Cuando en este caso se llama a o.m, 'this' se refiere a o + +var p = Object.create(o); +// p es un objeto que hereda de o + +p.a = 12; // crea una propiedad 'a' en p +console.log(p.m()); // 13 +// cuando se llama a p.m, 'this' se refiere a p. +// De esta manera, cuando p hereda la función m de o, +// 'this.a' significa p.a, la propiedad 'a' de p +``` + +## Usando prototipos en JavaScript + +Veamos lo que sucede detrás de escena detalladamente. + +En JavaScript, como se mencionó anteriormente, las funciones pueden tener propiedades. Todas las funciones tienen una propiedad especial llamada `prototype`. Por favor ten en cuenta que el siguiente código es autónomo (es seguro asumir que no hay otro JavaScript en la página web mas que el siguiente). Para la mejor experiencia de aprendizaje, es altamente recomendable que abras una consola (la cual, en Chrome y Firefox, se puede abrir presionando Ctrl+Shift+I), navegando hasta la pestaña "console", copiando y pegando el siguiente código JavaScript, y ejecutándolo presionando la tecla Enter/Return. + +```js +function hacerAlgo(){} +console.log( hacerAlgo.prototype ); +// No importa cómo declares la función, una +// función en JavaScript siempre tendrá una +// propiedad prototype predeterminada. +var hacerAlgo = function(){}; +console.log( hacerAlgo.prototype ); +``` + +Como acabamos de ver, `hacerAlgo()` tiene una propiedad `prototype` predeterminada, como lo demuestra la consola. Después de ejecutar este código, la consola debería haber mostrado un parecido a esto. + +```js +{ + constructor: ƒ hacerAlgo(), + __proto__: { + constructor: ƒ Object(), + hasOwnProperty: ƒ hasOwnProperty(), + isPrototypeOf: ƒ isPrototypeOf(), + propertyIsEnumerable: ƒ propertyIsEnumerable(), + toLocaleString: ƒ toLocaleString(), + toString: ƒ toString(), + valueOf: ƒ valueOf() + } +} +``` + +Podemos añadir propiedades al prototipo de `hacerAlgo()`, como se muestra a continuación. + +```js +function hacerAlgo(){} +hacerAlgo.prototype.foo = "bar"; +console.log( hacerAlgo.prototype ); +``` + +El resultado: + + { + foo: "bar", + constructor: ƒ hacerAlgo(), + __proto__: { + constructor: ƒ Object(), + hasOwnProperty: ƒ hasOwnProperty(), + isPrototypeOf: ƒ isPrototypeOf(), + propertyIsEnumerable: ƒ propertyIsEnumerable(), + toLocaleString: ƒ toLocaleString(), + toString: ƒ toString(), + valueOf: ƒ valueOf() + } + } + +Ahora podemos usar el operador `new` para crear una instancia de `hacerAlgo()` basado en este prototipo. Para usar el operador `new`, llama la función normalmente pero añadiendo el prefijo `new`. Llamar a la función con el operador `new` devuelve un objeto que es una instancia de la función. Entonces las propiedades pueden ser añadidas a este objeto. + +Intenta el siguiente código: + +```js +function hacerAlgo(){} +hacerAlgo.prototype.foo = "bar"; // añadir una propiedad al prototipo +var hacerUnaInstancia = new hacerAlgo(); +hacerUnaInstancia.prop = "un valor"; // añadir una propiedad al objeto +console.log( hacerUnaInstancia ); +``` + +El resultado es similar a lo siguiente: + +```js +{ + prop: "un valor", + __proto__: { + foo: "bar", + constructor: ƒ hacerAlgo(), + __proto__: { + constructor: ƒ Object(), + hasOwnProperty: ƒ hasOwnProperty(), + isPrototypeOf: ƒ isPrototypeOf(), + propertyIsEnumerable: ƒ propertyIsEnumerable(), + toLocaleString: ƒ toLocaleString(), + toString: ƒ toString(), + valueOf: ƒ valueOf() + } + } +} +``` + +Como acabamos de ver, el `__proto__` de `hacerUnaInstancia` es `hacerAlgo.prototype`. Pero, ¿qué hace esto? Cuando accedes a la propiedad de `hacerUnaInstancia`, el navegador primero revisa si `hacerUnaInstancia` tiene esa propiedad. + +Si `hacerUnaInstancia` no tiene la propiedad, entonces el navegador busca por la propiedad en el `__proto__` de `hacerUnaInstancia` (también conocido como `hacerAlgo.prototype`). Si el `__proto__` de `hacerUnaInstancia` tiene la propiedad buscada, entonces la propiedad en el `__proto__` de `hacerUnaInstancia` es usada. + +De otra manera, si el `__proto__` de `hacerUnaInstancia` no tiene la propiedad, entonces el `__proto__` de `__proto__` de `hacerUnaInstancia` es revisado para la propiedad. Por defecto, el `__proto__` de la propieda `prototype` de cualquier función es `window.Object.prototype`. Entonces, el `__proto__` de el `__proto__` de `hacerUnaInstancia` (conocido como el `__proto__` de `hacerAlgo.prototype` (conocido como `Object.prototype`)) es entonces revisado por la propiedad que se está buscando. + +Si la propiedad no es encontrada en el `__proto__` de el `__proto__` de `hacerUnaInstancia`, entonces el `__proto__` de el `__proto__` de el `__proto__` de `hacerUnaInstancia` es revisado. Sin embargo, hay un problema: el `__proto__` de el `__proto__` de el `__proto__` de el `__proto__` de `hacerUnaInstancia` no existe. Entonces y sólo entonces, despues de que toda la cadena de prototipos de `__proto__`'s es revisada, y no haya mas `__proto__`s el navegador afirma que la propiedad no existe y concluye que el valor de la propiedad es `undefined`. + +Vamos a intentar introduciendo más código en la consola: + +```js +function hacerAlgo(){} +hacerAlgo.prototype.foo = "bar"; +var hacerUnaInstancia = new hacerAlgo(); +hacerUnaInstancia.prop = "un valor"; +console.log("hacerUnaInstancia.prop: " + hacerUnaInstancia.prop); +console.log("hacerUnaInstancia.foo: " + hacerUnaInstancia.foo); +console.log("hacerAlgo.prop: " + hacerAlgo.prop); +console.log("hacerAlgo.foo: " + hacerAlgo.foo); +console.log("hacerAlgo.prototype.prop: " + hacerAlgo.prototype.prop); +console.log("hacerAlgo.prototype.foo: " + hacerAlgo.prototype.foo); +``` + +El resultado es el siguiente: + +```js +hacerUnaInstancia.prop: un valor +hacerUnaInstancia.foo: bar +hacerAlgo.prop: undefined +hacerAlgo.foo: undefined +hacerAlgo.prototype.prop: undefined +hacerAlgo.prototype.foo: bar +``` + +## Maneras diferentes de crear objetos y la cadena de prototipos resultante + +### Objetos creados mediante estructuras sintácticas + +```js +var o = {a: 1}; + +// El objeto recién creado o tiene Object.prototype como su [[Prototype]] +// o no tiene ninguna propiedad llamada 'hasOwnProperty' +// hasOwnProperty es una propiedad propia de Object.prototype. +// Entonces o hereda hasOwnProperty de Object.prototype +// Object.prototype es null como su prototype. +// o ---> Object.prototype ---> null + +var a = ["yo", "whadup", "?"]; + +// Arrays hereda de Array.prototype +// (que tiene métodos como indexOf, forEach, etc.) +// La cadena de prototipados sería: +// a ---> Array.prototype ---> Object.prototype ---> null + +function f(){ + return 2; +} + +// Las funciones heredan de Function.prototype +// (que tiene métodos como call, bind, etc.) +// f ---> Function.prototype ---> Object.prototype ---> null +``` + +### Con un constructor + +Un "constructor" en JavaScript es "solo" una función que pasa a ser llamada con el [operador new](/es/docs/Web/JavaScript/Reference/Operators/new "new"). + +```js +function Graph() { + this.vertices = []; + this.edges = []; +} + +Graph.prototype = { + addVertex: function(v){ + this.vertices.push(v); + } +}; + +var g = new Graph(); +// g es un objeto con las propiedades 'vértices' y 'edges'. +// g.[[Prototype]] es el valor de Graph.prototype cuando new Graph() es ejecutado. +``` + +### Con `Object.create` + +ECMAScript 5 Introdujo un nuevo método: {{jsxref("Object.create()")}}. Llamando este método creas un nuevo objeto. El prototype de este objeto es el primer argumento de la función: + +```js +var a = {a: 1}; +// a ---> Object.prototype ---> null + +var b = Object.create(a); +// b ---> a ---> Object.prototype ---> null +console.log(b.a); // 1 (heredado) + +var c = Object.create(b); +// c ---> b ---> a ---> Object.prototype ---> null + +var d = Object.create(null); +// d ---> null +console.log(d.hasOwnProperty); +// undefined, por que d no hereda de Object.prototype +``` + +### Con la palabra reservada `class` + +ECMAScript 2015 introduce un nuevo set de palabras reservadas que implementan [clases](/es/docs/Web/JavaScript/Reference/Classes). Aunque estos constructores lucen más familiares para los desarrolladores de lenguajes basados en clases, aun así no son clases. JavaScript sigue estando basado en prototipos. Los nuevos keywords incluyen {{jsxref("Statements/class", "class")}}, {{jsxref("Classes/constructor", "constructor")}}, {{jsxref("Classes/static", "static")}}, {{jsxref("Classes/extends", "extends")}}, and {{jsxref("Operators/super", "super")}}. + +```js +"use strict"; + +class Polygon { + constructor(height, width) { + this.height = height; + this.width = width; + } +} + +class Square extends Polygon { + constructor(sideLength) { + super(sideLength, sideLength); + } + get area() { + return this.height * this.width; + } + set sideLength(newLength) { + this.height = newLength; + this.width = newLength; + } +} + +var square = new Square(2); +``` + +### Rendimiento + +El tiempo de búsqueda para las propiedades que están en lo alto de la cadena de prototipo puede tener un impacto negativo en el rendimiento, y esto puede ser significativo en el código donde el rendimiento es crítico. Además, tratar de acceder a las propiedades inexistentes siempre atravesara la cadena de prototipos completamente. + +También, cuando iteramos sobre las propiedades de un objeto, cada propiedad enumerable que se encuentra en la cadena de prototipo será enumerada. + +Para comprobar si un objeto tiene una propiedad definida en sí mismo y no en alguna parte de su cadena de prototipo, Es necesario usar para esto el método [`hasOwnProperty`](/es/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty "/ru/docs/JavaScript/Reference/Global_Objects/Object/hasOwnProperty") que todos los objetos heredan de` Object.prototype`. + +[`hasOwnProperty`](/es/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty "/ru/docs/JavaScript/Reference/Global_Objects/Object/hasOwnProperty") es la única cosa en JavaScript que se ocupa de las propiedades y no atraviesa la cadena de prototipos. + +Nota: Esto **no** es suficiente para chequear si una propiedad esta [`undefined`](/es/docs/Web/JavaScript/Reference/Global_Objects/undefined). la propiedad podría existir, pero el valor justamente sucede que esta seteado como `undefined`. + +### Malas practicas: Extensión de prototipos nativos + +Una mala característica que a menudo se usa, es extender `Object.prototype` o uno de los otros pre-incorporados prototypes. + +Esta técnica se llama _monkey patching_ y rompe la _encapsulación_. Si bien, es utilizado por librerías como _Prototype.js_, no hay una buena razón para saturar los tipos pre-incorporados con funcionalidades adicionales _no estándar_. + +La **única** buena razón para extender los pre-incorporados prototipos es modificar las funcionalidades nuevas de los motores de JavaScript; por ejemplo: + +`Array.forEach`, etc. + +## Ejemplo + +`B` heredará de `A`: + +```js +function A(a){ + this.varA = a; +} + +// Cual es el propósito de incluir varA en el prototipo si A.prototype.varA siempre va a ser la sombra de +// this.varA, dada la definición de la función A arriba? +A.prototype = { + varA : null, // No deberíamos atacar varA desde el prototipo como haciendo nada? + // Tal vez intentando una optimización al asignar espacios ocultos en las clases? + // https://developers.google.com/speed/articles/optimizing-javascript#Initializing instanciar variables + // podría ser válido si varA no fuera inicializado únicamente por cada instancia. + doSomething : function(){ + // ... + } +} + +function B(a, b){ + A.call(this, a); + this.varB = b; +} +B.prototype = Object.create(A.prototype, { + varB : { + value: null, + enumerable: true, + configurable: true, + writable: true + }, + doSomething : { + value: function(){ // override + A.prototype.doSomething.apply(this, arguments); // call super + // ... + }, + enumerable: true, + configurable: true, + writable: true + } +}); +B.prototype.constructor = B; + +var b = new B(); +b.doSomething(); +``` + +Las partes importantes son: + +- Los tipos son definidos en `.prototype` +- Usar `Object.create()` para heredar + +## `prototype` y `Object.getPrototypeOf` + +JavaScript es un poco confuso para desarrolladores que vienen de lenguajes como Java o C++, ya que todo es dinámico, en todo momento de la ejecución, y no tiene clases en lo absoluto. Todo es solamente instancias (objetos). Incluso las "clases" que creamos, son solo funciones (objetos). + +Probablemente notaste que nuestra `función A` tiene una propiedad especial llamada `prototype`. Esta propiedad especial funciona con el operador de JavaScript `new`. La referencia al prototipo objeto es copiada al interno `[[Prototype]]` propiedad de la instancia _new_. Por ejemplo, cuando creas una variable `var a1 = new A()`, JavaScript (después de haber creado el objeto en memoria y antes de correr function `A()` con `this` definido a él) setea `a1.[[Prototype]] = A.prototype`. Cuando a continuación accedes a las propiedades de la instancia, JavaScript primero chequea si existen en el objeto directamente, y si no, mira en el `[[Prototype]]`. Esto significa que todo lo que definas en el `prototipo` es efectivamente compartido a todas las instancias, e incluso después puedes cambiar partes del `prototipo` y que todos los cambios se hagan en todas las instancias. + +Si, en el ejemplo de arriba, pones `var a1 = new A(); var a2 = new A();` entonces `a1.doSomething` se referiría a `Object.getPrototypeOf(a1).doSomething`, que seria lo mismo que `A.prototype.doSomething` que definiste, i.e. `Object.getPrototypeOf(a1).doSomething == Object.getPrototypeOf(a2).doSomething == A.prototype.doSomething`. + +resumiendo, `prototype` es para tipos, mientras que `Object.getPrototypeOf()` es lo mismo para instancias. + +`[[Prototype]]` es visto como _recursivo_, i.e. `a1.doSomething`, `Object.getPrototypeOf(a1).doSomething`, `Object.getPrototypeOf(Object.getPrototypeOf(a1)).doSomething` etc., hasta que se encuentra o `Object.getPrototypeOf `retornará null. + +Entonces, cuando llamas + +```js +var o = new Foo(); +``` + +JavaScript en realidad hace + +```js +var o = new Object(); +o.[[Prototype]] = Foo.prototype; +Foo.call(o); +``` + +(o algo similar) y cuando después haces + +```js +o.someProp; +``` + +chequea si `o` tiene una propiedad `someProp`. Si no, busca en `Object.getPrototypeOf(o).someProp` y si ahí no existe, busca en `Object.getPrototypeOf(Object.getPrototypeOf(o)).someProp` y así sucesivamente. + +## En conclusión + +Es **esencial** entender el modelo de prototipado por instancias antes de escribir código complejo que hace uso de esto. También, sé consciente del largo de la cadena de prototipado en tu código y romperlo si es necesario para evitar posibles problemas de rendimiento. Adicionalmente, el prototipo nativo **nunca** debería ser extendido a menos que esto sea por motivo de compatibilidad con nuevas versiones de JavaScript. diff --git a/files/es/web/javascript/javascript_technologies_overview/index.html b/files/es/web/javascript/javascript_technologies_overview/index.html deleted file mode 100644 index 452d5ce37a14ea..00000000000000 --- a/files/es/web/javascript/javascript_technologies_overview/index.html +++ /dev/null @@ -1,88 +0,0 @@ ---- -title: Descripción de las tecnologías JavaScript -slug: Web/JavaScript/JavaScript_technologies_overview -translation_of: Web/JavaScript/JavaScript_technologies_overview -original_slug: Web/JavaScript/Descripción_de_las_tecnologías_JavaScript ---- -
{{JsSidebar("Introductory")}}
- -

Introducción

- -

Mientras que HTML define la estructura y el contenido de una página web y el CSS define el formato y la apariencia, JavaScript agrega interactividad a una página web y crea aplicaciones web enriquecidas.

- -

Sin embargo, el término paraguas "JavaScript", tal como se entiende en un contexto de navegador web, contiene varios elementos muy diferentes. Uno de ellos es el lenguaje principal (ECMAScript), otro es la colección de las APIs Web, incluyendo el DOM (Document Object Model).

- -

JavaScript, el núcleo del lenguaje (ECMAScript)

- -

EL núcleo del lenguaje JavaScript está estandarizado por el Comité ECMA TC39 como un lenguaje llamado ECMAScript. La última versión de la especificación es ECMAScript 6.0.

- -

Éste núcleo del lenguaje es también usado en ambientes No-Navegadores, por ejemplo en node.js.

- -

Qué considera ECMAScript scope?

- -

Entre otras cosas, ECMAScript define:

- - - - - -

A partir de Octubre del 2016, la gran mayoria de los navegadores soportan ECMAScript 5.1 y ECMAScript 2015 aka ES6, pero versiones antiguas (aún en uso) implementan ECMAScript 5 solamente.

- -

Futuro

- -

La 6ta edición principal de ECMAScript fue oficialmente aprobada y publicada como un estándar el 17 de Junio del 2015 por la ECMA General Assembly. Desde entonces las ediciones de ECMAScript son publicadas anualmente.

- -

Internacionalización de la API

- -

La ECMAScript Internationalization API Specification es una adición para la especificación del lenguaje ECMAScript, también estandarizada por Ecma TC39. La internacionalización de la API provee intercalación --collation-- (comparación de string), formateo de números, y fomateo de fechas/tiempo para aplicaciones JavaScript, permitiendo que las aplicaciones elijan el idioma y adapten la funcionalidad a sus necesidades. El estandar inicial fue aprobado en Diciembre del 2012; el estado de la implementación en navegadores es rastreado en la documentación de la Intl object. Las especificaciones de la internacionalización son actualmente ratificadas cada año y los navegadores constantemente mejoran su implementación.

- -

DOM APIs

- -

WebIDL

- -

Las especificaciones de la WebIDL proporcionan el vínculo de las tecnologías DOM y ECMAScript.

- -

El núcleo del DOM

- -

El Document Object Model (DOM) es una convención multiplataforma e independiente del lenguaje para representar e interactuar con objetos en documentos HTML, XHTML y XML. Los objetos en el árbol DOM se pueden abordar y manipular mediante el uso de métodos en los objetos de otras tecnologías. El {{Glossary("W3C")}} estandariza el Modelo de Objetos del Documento Central, que define interfaces independientes del lenguaje que abstraen documentos HTML y XML como objetos, y también define mecanismos para manipular esta abstracción. Entre las cosas definidas por el DOM, podemos encontrar:

- - - -

Desde el punto de vista ECMAScript, los objetos definidos en la especificación DOM se denominan "objetos host".

- -

HTML DOM

- -

HTML, el lenguaje de marcado de la Web, se especifica en términos del DOM. Por encima de los conceptos abstractos definidos en DOM Core, HTML también define el significado de los elementos. El DOM HTML incluye cosas como el className propiedad en elementos HTML o API como {{domxref ("document.body")}}.

- -

La especificación HTML también define restricciones en los documentos; por ejemplo, requiere que todos los elementos secundarios de un elemento ul que representa una lista desordenada, sean elementos li, ya que representan elementos de la lista. En general, también prohíbe el uso de elementos y atributos que no están definidos en un estándar.

- -

¿Busca Document object, Window object, y los otros elementos DOM? ? Lee la documentación DOM documentation.

- -

Otras API notables

- - - -

Soporte del navegador

- -

Como todos los desarrolladores web han experimentado, el DOM es un desastre. La uniformidad del soporte del navegador varía mucho de una característica a otra, principalmente porque muchas características DOM importantes tienen especificaciones muy poco claras (si las hay), y diferentes navegadores web agregan características incompatibles para casos de uso superpuestos (como el modelo de evento de Internet Explorer). A partir de junio de 2011, el W3C y particularmente el WHATWG están definiendo características antiguas en detalle para mejorar la interoperabilidad, y los navegadores a su vez pueden mejorar sus implementaciones basadas en estas especificaciones.

- -

Un enfoque común, aunque quizás no el más confiable, para la compatibilidad entre navegadores es usar bibliotecas de JavaScript, que resumen las funciones DOM y mantienen sus API funcionando igual en diferentes navegadores. Algunos de los frameworks más utilizados son jQuery, prototype, y YUI.

diff --git a/files/es/web/javascript/javascript_technologies_overview/index.md b/files/es/web/javascript/javascript_technologies_overview/index.md new file mode 100644 index 00000000000000..73ea244c7e26a5 --- /dev/null +++ b/files/es/web/javascript/javascript_technologies_overview/index.md @@ -0,0 +1,82 @@ +--- +title: Descripción de las tecnologías JavaScript +slug: Web/JavaScript/JavaScript_technologies_overview +translation_of: Web/JavaScript/JavaScript_technologies_overview +original_slug: Web/JavaScript/Descripción_de_las_tecnologías_JavaScript +--- +{{JsSidebar("Introductory")}} + +## Introducción + +Mientras que HTML define la estructura y el contenido de una página web y el CSS define el formato y la apariencia, JavaScript agrega interactividad a una página web y crea aplicaciones web enriquecidas. + +Sin embargo, el término paraguas "JavaScript", tal como se entiende en un contexto de navegador web, contiene varios elementos muy diferentes. Uno de ellos es el lenguaje principal (ECMAScript), otro es la colección de las APIs Web, incluyendo el DOM (Document Object Model). + +## JavaScript, el núcleo del lenguaje (ECMAScript) + +EL núcleo del lenguaje JavaScript está estandarizado por el Comité ECMA TC39 como un lenguaje llamado [ECMAScript](/es/docs/JavaScript/Language_Resources). La última versión de la especificación es [ECMAScript 6.0](http://www.ecma-international.org/ecma-262/6.0/). + +Éste núcleo del lenguaje es también usado en ambientes No-Navegadores, por ejemplo en [node.js](http://nodejs.org/). + +### Qué considera ECMAScript scope? + +Entre otras cosas, ECMAScript define: + +- Sintaxis (reglas de análisis, palabras clave, flujos de control, inicialización literal de objetos...). +- Mecanismos de control de errores (throw, try/catch, habilidad para crear tipos de Errores definidos por el usuario). +- Tipos (boolean, number, string, function, object...). +- Los objetos globales. En un navegador, estos objetos globales son los objetos de la ventana, pero ECMAScript solo define APIs no especificas para navegadores, ej. `parseInt`, `parseFloat`, `decodeURI`, `encodeURI`... +- Mecanismo de herencia basada en prototipos. +- Objetos y funciones incorporadas (`JSON`, `Math`, `Array.prototype` methods, Object introspection methods...) +- Modo estricto. + +### Navegadores soportados + +A partir de Octubre del 2016, la gran mayoria de los navegadores soportan [ECMAScript 5.1](/es/docs/Web/JavaScript/New_in_JavaScript/ECMAScript_5_support_in_Mozilla) y [ECMAScript 2015 aka ES6](/es/docs/Web/JavaScript/New_in_JavaScript/ECMAScript_6_support_in_Mozilla), pero versiones antiguas (aún en uso) implementan ECMAScript 5 solamente. + +### Futuro + +La 6ta edición principal de ECMAScript fue oficialmente aprobada y publicada como un estándar el 17 de Junio del 2015 por la ECMA General Assembly. Desde entonces las ediciones de ECMAScript son publicadas anualmente. + +### Internacionalización de la API + +La [ECMAScript Internationalization API Specification](http://ecma-international.org/ecma-402/1.0/) es una adición para la especificación del lenguaje ECMAScript, también estandarizada por Ecma TC39. La internacionalización de la API provee intercalación --collation-- (comparación de string), formateo de números, y fomateo de fechas/tiempo para aplicaciones JavaScript, permitiendo que las aplicaciones elijan el idioma y adapten la funcionalidad a sus necesidades. El estandar inicial fue aprobado en Diciembre del 2012; el estado de la implementación en navegadores es rastreado en la documentación de la [`Intl` object](/es/docs/JavaScript/Reference/Global_Objects/Intl). Las especificaciones de la internacionalización son actualmente ratificadas cada año y los navegadores constantemente mejoran su implementación. + +## DOM APIs + +### WebIDL + +Las especificaciones de la [WebIDL](http://www.w3.org/TR/WebIDL/) proporcionan el vínculo de las tecnologías DOM y ECMAScript. + +### El núcleo del DOM + +El Document Object Model (DOM) es una **convención multiplataforma e independiente** del lenguaje para representar e interactuar con objetos en documentos HTML, XHTML y XML. Los objetos en el **árbol DOM** se pueden abordar y manipular mediante el uso de métodos en los objetos de otras tecnologías. El {{Glossary("W3C")}} estandariza el Modelo de Objetos del Documento Central, que define interfaces independientes del lenguaje que abstraen documentos HTML y XML como objetos, y también define mecanismos para manipular esta abstracción. Entre las cosas definidas por el DOM, podemos encontrar: + +- La estructura del documento, un modelo de árbol, y la arquitectura de eventos DOM en el nucleo del DOM: `Node`, `Element`, `DocumentFragment`, `Document`, `DOMImplementation`, `Event`, `EventTarget`, … +- Una definición menos rigurosa de la arquitectura de eventos DOM, así como eventos específicos en eventos DOM. +- Otras cosas como DOM Traversal y el DOM Range. + +Desde el punto de vista ECMAScript, los objetos definidos en la especificación DOM se denominan "objetos host". + +### HTML DOM + +HTML, el lenguaje de marcado de la Web, se especifica en términos del DOM. Por encima de los conceptos abstractos definidos en DOM Core, HTML también define el significado de los elementos. El DOM HTML incluye cosas como el `className` propiedad en elementos HTML o API como {{domxref ("document.body")}}. + +La especificación HTML también define restricciones en los documentos; por ejemplo, requiere que todos los elementos secundarios de un elemento `ul` que representa una lista desordenada, sean elementos `li`, ya que representan elementos de la lista. En general, también prohíbe el uso de elementos y atributos que no están definidos en un estándar. + +¿Busca [`Document` object](/es/docs/DOM/document), [`Window` object](/es/docs/DOM/window), y los otros elementos DOM? ? Lee la documentación [DOM documentation](/es/docs/Web/API/Document_Object_Model). + +## Otras API notables + +- Las funciones `setTimeout` `ysetInterval` se especificaron por primera vez en la interfaz de [`Window`](http://www.whatwg.org/html/#window) en HTML Standard. +- [XMLHttpRequest](https://dvcs.w3.org/hg/xhr/raw-file/tip/Overview.html) hace posible enviar solicitudes HTTP asincrónicas. +- EL [Modelo de Objetos CSS](http://dev.w3.org/csswg/cssom/) esume las reglas CSS como objetos. +- [WebWorkers ](http://www.whatwg.org/specs/web-workers/current-work/)permite el cálculo paralelo. +- [WebSockets](http://www.whatwg.org/C/#network) permite la comunicación bidireccional de bajo nivel. +- [Canvas 2D Context](http://www.whatwg.org/html/#2dcontext) es una API de dibujo para {{htmlelement("canvas")}}. + +### Soporte del navegador + +Como todos los desarrolladores web han experimentado, [el DOM es un desastre](http://ejohn.org/blog/the-dom-is-a-mess/). La uniformidad del soporte del navegador varía mucho de una característica a otra, principalmente porque muchas características DOM importantes tienen especificaciones muy poco claras (si las hay), y diferentes navegadores web agregan características incompatibles para casos de uso superpuestos (como el modelo de evento de Internet Explorer). A partir de junio de 2011, el W3C y particularmente el WHATWG están definiendo características antiguas en detalle para mejorar la interoperabilidad, y los navegadores a su vez pueden mejorar sus implementaciones basadas en estas especificaciones. + +Un enfoque común, aunque quizás no el más confiable, para la compatibilidad entre navegadores es usar bibliotecas de JavaScript, que resumen las funciones DOM y mantienen sus API funcionando igual en diferentes navegadores. Algunos de los frameworks más utilizados son [jQuery](http://jquery.com/), [prototype](http://www.prototypejs.org/), y [YUI](http://developer.yahoo.com/yui/). diff --git a/files/es/web/javascript/memory_management/index.html b/files/es/web/javascript/memory_management/index.html deleted file mode 100644 index bc58509017161e..00000000000000 --- a/files/es/web/javascript/memory_management/index.html +++ /dev/null @@ -1,205 +0,0 @@ ---- -title: Gestión de Memoria -slug: Web/JavaScript/Memory_Management -tags: - - Advanced - - JavaScript - - Performance - - Reference - - Referencia - - Rendimiento - - memoria -translation_of: Web/JavaScript/Memory_Management -original_slug: Web/JavaScript/Gestion_de_Memoria ---- -
{{JsSidebar("Advanced")}}
- -

Introducción

- -

Los lenguajes de bajo nivel, como C, tienen primitivos de bajo nivel como malloc() y free() para la gestión de memoria. Por otro lado, para los valores en JavaScript se reserva memoria cuando"cosas" (objetos, strings, etc.) son creados y "automáticamente" liberados cuando ya no son utilizados. El proceso anterior es conocido como Recolección de basura (garbage collection). Su forma "automática" es fuente de confusión, y da la impresión a los desarrolladores de JavaScript (y de otros lenguajes de alto nivel) de poder ignorar el proceso de gestión de memoria. Esto es erróneo.

- -

Ciclo de vida de memoria

- -

Sin importar el lenguaje de programación, el ciclo de memoria es casi siempre parecido al siguiente:

- -
    -
  1. Reservar la memoria necesaria
  2. -
  3. Utilizarla (lectura, escritura)
  4. -
  5. Liberar la memoria una vez ya no es necesaria.
  6. -
- -

El primer y el segundo paso son explícitos en todos los lenguajes. El último paso es explícito en lenguajes de bajo nivel, pero es mayormente implícito en lenguajes de alto nivel como JavaScript

- -

Reserva de memoria en JavaScript

- -

Inicialización de valores

- -

Para no agobiar al programador con reservas de memoria, JavaScript las realiza al mismo tiempo que la declaración de los valores.

- -
var n = 123; // reserva memoria para un número
-var s = "azerty"; // reserva memoria para un string
-
-var o = {
-  a: 1,
-  b: null
-}; // reserva memoria para un objeto y los valores que
-   // contiene
-
-// (similar a objeto) reserva memoria para el arreglo y
-// los valores que contiene
-var a = [1, null, "abra"];
-
-function f(a){
-  return a + 2;
-} // reserva memoria para una funcion (la cual es un objeto)
-
-// las expresiones de función tambien reservan memoria para un objeto
-someElement.addEventListener('click', function(){
-  someElement.style.backgroundColor = 'blue';
-}, false);
-
- -

Reserva de memoria al llamar una función

- -

En ocasiones al llamar a una función se reserva memoria para un objeto.

- -
var d = new Date();
-// reserva memoria para un elemento del DOM
-var e = document.createElement('div');
-
- -

Algunos métodos reservan memoria para nuevos valores u objetos:

- -
var s = "azerty";
-var s2 = s.substr(0, 3); // s2 es un nuevo string
-// Como los strings son valores inmutables,
-// JavaScript puede NO reservar memoria para este,
-// y solo almacenar el rango [0, 3].
-
-var a = ["ouais ouais", "nan nan"];
-var a2 = ["generation", "nan nan"];
-var a3 = a.concat(a2);
-// nuevo arreglo con 4 elementos resultado de
-// concatenar los elementos de a y a2
-
- -

Usando valores

- -

Usar un valor es simplemente leerlo o escribirlo en memoria reservada. Esto puede ocurrir al leer o escribir el valor de una variable o de una propiedad de un objeto, inclusive pasando un argumento a una función.

- -

Liberar la memoria cuando ya no es necesaria

- -

En este punto ocurren la mayoria de los inconvenientes con la gestión de memoria. Lo más díficil es encontrar el cuándo la "memoria ya no es necesaria". En algunas ocasiones, es necesario que el desarrollador determine en qué parte de un programa esa parte de memoria ya no es necesaria y la libere.

- -

Los lenguajes de alto nivel incluyen una herramienta de software conocida como "recolector de basura" (garbage collector), cuya función es rastrear las reservas de memoria y su utilización, para así encontrar cuándo cierta parte de la memoria ya no es necesaria, y en su momento liberarla automáticamente. Este proceso es sólo una aproximación al problema general de saber cuándo una parte de la memoria ya no es necesaria, ya que éste es indecidible (no puede ser resuelto por un algoritmo).

- -

Recolección de basura (Garbage collection)

- -

Como antes se mencionaba el problema general de encontrar automáticamente cuando la memoria "ya no es necesaria" es indecidible. Como consecuencia, las recolecciones de basura implementan sólo una restricción a la solución del problema general. En esta sección se explicarán las nociones necesarias para entender los principales algoritmos de recolección de basura y sus limitaciones.

- -

Referencias

- -

La noción principal de los algoritmos de recolección se basan en la noción de referencia. Dentro del contexto de gestión de memoria, se dice que un objeto hace referencia a otro si el primero tiene acceso al segundo (ya sea de forma implícita o explícita). Por ejemplo, un objeto de JavaScript guarda una referencia a su prototipo (referencia implícita) y a cualquiera de los valores de sus propiedades (referencia explícita)

- -

Hay que mencionar que en este contexto la noción de "objeto" se refiere a algo más amplio que los objetos normales de JavaScript y que también incluye al ámbito de la función (o ámbito de léxico global).

- -

Recolección de basura a través de conteo de referencias

- -

Éste es el algoritmo de recolección más simple. Este algoritmo reduce la definición de "un objejo ya no es necesario" a "un objeto ya no tiene ningún otro objeto que lo referencíe". Un objeto es considerado recolectable si existen cero referencias hacia él.

- -

Ejemplo

- -
var o = {
-  a: {
-    b:2
-  }
-};
-// Se crean dos objetos. Uno es referenciado por el otro como
-// una de sus propiedades.
-// El otro es referenciado al ser asignado a la variable "o"
-// Ninguno puede ser recolectado.
-
-
-var o2 = o; // la variable "o2" es lo segundo en tener una
-            // referencia al objeto.
-o = 1;      // ahora el objeto solo tiene una referencia mediante
-            // la variable "o2"
-
-var oa = o2.a; // referencia a la propiedad "a" del objeto.
-               // ahora el objeto posee dos referencias, una como propiedad
-               // la otra como la variable "oa"
-
-o2 = "yo"; // el objeto original "o" ahora ya no tiene
-           // referencias a él. Podría ser recolectado.
-           // Sin embargo lo que había en la propiedad "a" aún
-           // esta refernciado en la variable "oa";
-           // no puede ser recolectado aún
-
-oa = null; // lo que estaba en la propiedad "a" del objeto original "o"
-           // ahora ya no tiene ninguna referencia.Puede ser recolectado.
-
- -

Limitación : ciclos

- -

Existe una limitación cuando se trata de ciclos. En el siguiente ejemplo dos objetos son creados y se referencían entre ellos -por lo que se crea un ciclo. Ellos no saldrán del ámbito de la función después del llamado de la función, con lo que serían efectivamente "ya no son necesarios" y por lo cual ser liberados. Sin embargo, el algoritmo de conteo de referencias considera que ya que cada uno de los dos objetos está referenciado por lo menos una vez, ninguno podra ser recolectado. Este simple algoritmo tiene la limitación de que si un grupo de objetos se referencian a sí mismos (y forman un ciclo), nunca pasarán a "ya no ser necesitados" y no podrán ser recolectados nunca.

- -
function f(){
-  var o = {};
-  var o2 = {};
-  o.a = o2; // o referencía o2
-  o2.a = o; // o2 referencía o
-
-  return "azerty";
-}
-
-f();
-// Dos objetos son creados y se referencían uno al otro creando un ciclo
-// Estan atrapados en el scope de la funcion después de la llamada
-// por lo que son inútiles fuera de la función y podrían ser recolectados.
-// Sin embargo, el algoritmo de conteo de referencias considera que como
-// ambos objetos estan referenciados (aunque sean a si mismos) ambos
-// siguen en uso y por lo tanto no pueden ser recolectados.
-
-
- -

Ejemplo real aplicado

- -

Internet Explorer 6 y 7 son conocidos por tener recolectores de basura por conteo de referencias para los objetos del DOM. Los Ciclos son un error común que pueden generar fugas de memoria (memory leaks) (art. en inglés):

- -
var div;
-window.onload = function(){
-  div = document.getElementById("miDiv");
-  div.referenciaCircular = div;
-  div.muchosDatos = new Array(10000).join("*");
-};
-
- -

En el ejemplo anterior, el elemento del DOM "miDiv" posée una referencia circular a sí mismo en la propiedad "referenciaCircular". Si la propiedad no es explícitamente removida o asignada con el valor null, un algoritmo de conteo de referencias siempre va a dejar por lo menos una referencia intacta y va a mantener el elemento del DOM activo en memoria incluso cuando es removido del DOM. Si el objeto del DOM contiene una gran cantidad de datos (ejemplificado en la propiedad "muchosDatos"), la memoria consumida por estos datos nunca será liberada.

- -

Algoritmo Mark-and-sweep (Marcado y barrido)

- -

Este algoritmo reduce la definición de "un objeto ya no es necesitado" a "un objeto es inalcanzable"

- -

Este algoritmo asume la noción de un grupo de objetos llamados objetos raíz (en JavaScript la raíz es el objeto global). Periódicamente el recolector empieza por estas raíces, encuentra todos los objetos que están referenciados por estas raíces, y luego todos los objetos referenciados de estos, etc. Empezando por las raíces, el recolector de esta forma encontrará todos los objetos que son alcanzables y recolectará los objetos inalcanzables.

- -

Este algoritmo es mejor que el anterior ya que "un objeto tiene cero referencias" equivale al "objeto es inalcanzable". Esto no sucedía asi en el algoritmo anterior cuando se trataba de un ciclo.

- -

Desde el 2012, todos los navegadores incluyen un recolector de basura basado en mark-and-sweep. Todas las mejoras realizadas en el campo de Recolección de basura en JavaScript (recolección generacional/incremental/concurrida/paralela) en los ultimos años son mejoras a la implementación del algoritmo, pero no mejoras sobre el algoritmo de recolección ni a la reducción de la definicion de cuando"un objeto ya no es necesario".

- -

Los ciclos son problema del pasado

- -

En el primer ejemplo, después de que la llamada a una función termina, los dos objetos ya no son referenciados por nada alcanzable desde el objeto global. De esta forma serán identificados como inalcanzables por el recolector de basura.

- -

Lo mismo ocurre en el segundo ejemplo. Una vez que el elemento div y sus métodos se hacen inalcanzable desde los objetos raíz, ambos pueden ser recolectados a pesar de que estén referenciados los unos de los otros.

- -

Limitación: los objetos necesarios se hacen inalcanzables de forma explícita.

- -

Aunque esto está marcado como una limitación, se puede encontrar muy poco en la práctica. Ésta es la razón por la cuál la recolección de basura es poco tomada en cuenta.

- -

Véase también

- - diff --git a/files/es/web/javascript/memory_management/index.md b/files/es/web/javascript/memory_management/index.md new file mode 100644 index 00000000000000..17e9299a039bf3 --- /dev/null +++ b/files/es/web/javascript/memory_management/index.md @@ -0,0 +1,206 @@ +--- +title: Gestión de Memoria +slug: Web/JavaScript/Memory_Management +tags: + - Advanced + - JavaScript + - Performance + - Reference + - Referencia + - Rendimiento + - memoria +translation_of: Web/JavaScript/Memory_Management +original_slug: Web/JavaScript/Gestion_de_Memoria +--- +{{JsSidebar("Advanced")}} + +## Introducción + +Los lenguajes de bajo nivel, como C, tienen primitivos de bajo nivel como `malloc() `y `free() `para la gestión de memoria. Por otro lado, para los valores en JavaScript se reserva memoria cuando"cosas" (objetos, strings, etc.) son creados y "automáticamente" liberados cuando ya no son utilizados. El proceso anterior es conocido como _Recolección de basura (garbage collection)._ Su forma "automática" es fuente de confusión, y da la impresión a los desarrolladores de JavaScript (y de otros lenguajes de alto nivel) de poder ignorar el proceso de gestión de memoria. Esto es erróneo. + +## Ciclo de vida de memoria + +Sin importar el lenguaje de programación, el ciclo de memoria es casi siempre parecido al siguiente: + +1. Reservar la memoria necesaria +2. Utilizarla (lectura, escritura) +3. Liberar la memoria una vez ya no es necesaria. + +El primer y el segundo paso son explícitos en todos los lenguajes. El último paso es explícito en lenguajes de bajo nivel, pero es mayormente implícito en lenguajes de alto nivel como JavaScript + +### Reserva de memoria en JavaScript + +#### Inicialización de valores + +Para no agobiar al programador con reservas de memoria, JavaScript las realiza al mismo tiempo que la declaración de los valores. + +```js +var n = 123; // reserva memoria para un número +var s = "azerty"; // reserva memoria para un string + +var o = { + a: 1, + b: null +}; // reserva memoria para un objeto y los valores que + // contiene + +// (similar a objeto) reserva memoria para el arreglo y +// los valores que contiene +var a = [1, null, "abra"]; + +function f(a){ + return a + 2; +} // reserva memoria para una funcion (la cual es un objeto) + +// las expresiones de función tambien reservan memoria para un objeto +someElement.addEventListener('click', function(){ + someElement.style.backgroundColor = 'blue'; +}, false); +``` + +#### Reserva de memoria al llamar una función + +En ocasiones al llamar a una función se reserva memoria para un objeto. + +```js +var d = new Date(); +// reserva memoria para un elemento del DOM +var e = document.createElement('div'); +``` + +Algunos métodos reservan memoria para nuevos valores u objetos: + +```js +var s = "azerty"; +var s2 = s.substr(0, 3); // s2 es un nuevo string +// Como los strings son valores inmutables, +// JavaScript puede NO reservar memoria para este, +// y solo almacenar el rango [0, 3]. + +var a = ["ouais ouais", "nan nan"]; +var a2 = ["generation", "nan nan"]; +var a3 = a.concat(a2); +// nuevo arreglo con 4 elementos resultado de +// concatenar los elementos de a y a2 +``` + +### Usando valores + +Usar un valor es simplemente leerlo o escribirlo en memoria reservada. Esto puede ocurrir al leer o escribir el valor de una variable o de una propiedad de un objeto, inclusive pasando un argumento a una función. + +### Liberar la memoria cuando ya no es necesaria + +En este punto ocurren la mayoria de los inconvenientes con la gestión de memoria. Lo más díficil es encontrar el cuándo la "memoria ya no es necesaria". En algunas ocasiones, es necesario que el desarrollador determine en qué parte de un programa esa parte de memoria ya no es necesaria y la libere. + +Los lenguajes de alto nivel incluyen una herramienta de software conocida como "recolector de basura" _(garbage collector),_ cuya función es rastrear las reservas de memoria y su utilización, para así encontrar cuándo cierta parte de la memoria ya no es necesaria, y en su momento liberarla automáticamente. Este proceso es sólo una aproximación al problema general de saber cuándo una parte de la memoria ya no es necesaria, ya que éste es [indecidible](http://es.wikipedia.org/wiki/Problema_indecidible) (no puede ser resuelto por un algoritmo). + +## Recolección de basura (Garbage collection) + +Como antes se mencionaba el problema general de encontrar automáticamente cuando la memoria "ya no es necesaria" es indecidible. Como consecuencia, las recolecciones de basura implementan sólo una restricción a la solución del problema general. En esta sección se explicarán las nociones necesarias para entender los principales algoritmos de recolección de basura y sus limitaciones. + +### Referencias + +La noción principal de los algoritmos de recolección se basan en la noción de _referencia_. Dentro del contexto de gestión de memoria, se dice que un objeto hace referencia a otro si el primero tiene acceso al segundo (ya sea de forma implícita o explícita). Por ejemplo, un objeto de JavaScript guarda una referencia a su [prototipo](http://es.wikipedia.org/wiki/JavaScript#Protot.C3.ADpico) (referencia implícita) y a cualquiera de los valores de sus propiedades (referencia explícita) + +Hay que mencionar que en este contexto la noción de "objeto" se refiere a algo más amplio que los objetos normales de JavaScript y que también incluye al ámbito de la función (o ámbito de léxico global). + +### Recolección de basura a través de conteo de referencias + +Éste es el algoritmo de recolección más simple. Este algoritmo reduce la definición de "un objejo ya no es necesario" a "un objeto ya no tiene ningún otro objeto que lo referencíe". Un objeto es considerado recolectable si existen cero referencias hacia él. + +#### Ejemplo + +```js +var o = { + a: { + b:2 + } +}; +// Se crean dos objetos. Uno es referenciado por el otro como +// una de sus propiedades. +// El otro es referenciado al ser asignado a la variable "o" +// Ninguno puede ser recolectado. + + +var o2 = o; // la variable "o2" es lo segundo en tener una + // referencia al objeto. +o = 1; // ahora el objeto solo tiene una referencia mediante + // la variable "o2" + +var oa = o2.a; // referencia a la propiedad "a" del objeto. + // ahora el objeto posee dos referencias, una como propiedad + // la otra como la variable "oa" + +o2 = "yo"; // el objeto original "o" ahora ya no tiene + // referencias a él. Podría ser recolectado. + // Sin embargo lo que había en la propiedad "a" aún + // esta refernciado en la variable "oa"; + // no puede ser recolectado aún + +oa = null; // lo que estaba en la propiedad "a" del objeto original "o" + // ahora ya no tiene ninguna referencia.Puede ser recolectado. +``` + +#### Limitación : ciclos + +Existe una limitación cuando se trata de ciclos. En el siguiente ejemplo dos objetos son creados y se referencían entre ellos -por lo que se crea un ciclo. Ellos no saldrán del ámbito de la función después del llamado de la función, con lo que serían efectivamente "ya no son necesarios" y por lo cual ser liberados. Sin embargo, el algoritmo de conteo de referencias considera que ya que cada uno de los dos objetos está referenciado por lo menos una vez, ninguno podra ser recolectado. Este simple algoritmo tiene la limitación de que si un grupo de objetos se referencian a sí mismos (y forman un ciclo), nunca pasarán a "ya no ser necesitados" y no podrán ser recolectados nunca. + +```js +function f(){ + var o = {}; + var o2 = {}; + o.a = o2; // o referencía o2 + o2.a = o; // o2 referencía o + + return "azerty"; +} + +f(); +// Dos objetos son creados y se referencían uno al otro creando un ciclo +// Estan atrapados en el scope de la funcion después de la llamada +// por lo que son inútiles fuera de la función y podrían ser recolectados. +// Sin embargo, el algoritmo de conteo de referencias considera que como +// ambos objetos estan referenciados (aunque sean a si mismos) ambos +// siguen en uso y por lo tanto no pueden ser recolectados. +``` + +#### Ejemplo real aplicado + +Internet Explorer 6 y 7 son conocidos por tener recolectores de basura por conteo de referencias para los objetos del DOM. Los Ciclos son un error común que pueden generar _fugas de memoria_ (_[memory leaks) (art. en inglés)](http://es.wikipedia.org/wiki/Fuga_de_memoria):_ + +```js +var div; +window.onload = function(){ + div = document.getElementById("miDiv"); + div.referenciaCircular = div; + div.muchosDatos = new Array(10000).join("*"); +}; +``` + +En el ejemplo anterior, el elemento del DOM "miDiv" posée una referencia circular a sí mismo en la propiedad "referenciaCircular". Si la propiedad no es explícitamente removida o asignada con el valor _null_, un algoritmo de conteo de referencias siempre va a dejar por lo menos una referencia intacta y va a mantener el elemento del DOM activo en memoria incluso cuando es removido del DOM. Si el objeto del DOM contiene una gran cantidad de datos (ejemplificado en la propiedad "muchosDatos"), la memoria consumida por estos datos nunca será liberada. + +### Algoritmo Mark-and-sweep (Marcado y barrido) + +Este algoritmo reduce la definición de "un objeto ya no es necesitado" a "un objeto es inalcanzable" + +Este algoritmo asume la noción de un grupo de objetos llamados _objetos raíz_ (en JavaScript la raíz es el objeto global). Periódicamente el recolector empieza por estas raíces, encuentra todos los objetos que están referenciados por estas raíces, y luego todos los objetos referenciados de estos, etc. Empezando por las raíces, el recolector de esta forma encontrará todos los objetos que son _alcanzables_ y recolectará los objetos inalcanzables. + +Este algoritmo es mejor que el anterior ya que "un objeto tiene cero referencias" equivale al "objeto es inalcanzable". Esto no sucedía asi en el algoritmo anterior cuando se trataba de un ciclo. + +Desde el 2012, todos los navegadores incluyen un recolector de basura basado en mark-and-sweep. Todas las mejoras realizadas en el campo de Recolección de basura en JavaScript (recolección generacional/incremental/concurrida/paralela) en los ultimos años son mejoras a la implementación del algoritmo, pero no mejoras sobre el algoritmo de recolección ni a la reducción de la definicion de cuando"un objeto ya no es necesario". + +#### Los ciclos son problema del pasado + +En el primer ejemplo, después de que la llamada a una función termina, los dos objetos ya no son referenciados por nada alcanzable desde el objeto global. De esta forma serán identificados como inalcanzables por el recolector de basura. + +Lo mismo ocurre en el segundo ejemplo. Una vez que el elemento div y sus métodos se hacen inalcanzable desde los objetos raíz, ambos pueden ser recolectados a pesar de que estén referenciados los unos de los otros. + +#### Limitación: los objetos necesarios se hacen inalcanzables de forma explícita. + +Aunque esto está marcado como una limitación, se puede encontrar muy poco en la práctica. Ésta es la razón por la cuál la recolección de basura es poco tomada en cuenta. + +## Véase también + +- [IBM article on "Memory leak patterns in JavaScript" (2007) (art. en inglés)](http://www.ibm.com/developerworks/web/library/wa-memleak/) +- [Kangax article on how to register event handler and avoid memory leaks (2010) (art. en inglés)](http://msdn.microsoft.com/en-us/magazine/ff728624.aspx) +- [Performance (art. en inglés)](/es/docs/Mozilla/Performance "https://developer.mozilla.org/en-US/docs/Mozilla/Performance") diff --git a/files/es/web/javascript/typed_arrays/index.html b/files/es/web/javascript/typed_arrays/index.html deleted file mode 100644 index 8a88723a99ee0b..00000000000000 --- a/files/es/web/javascript/typed_arrays/index.html +++ /dev/null @@ -1,273 +0,0 @@ ---- -title: Arreglos tipados de JavaScript -slug: Web/JavaScript/Typed_arrays -tags: - - Arreglo tipado - - Guía - - JavaScript -translation_of: Web/JavaScript/Typed_arrays -original_slug: Web/JavaScript/Vectores_tipados ---- -
{{JsSidebar("Advanced")}}
- -

Los arreglos tipados en JavaScript son objetos similares a arreglos que proporcionan un mecanismo para leer y escribir datos binarios sin procesar en búferes de memoria. Como ya sabrás, los objetos {{jsxref("Array", "Arreglo")}} crecen y se encogen dinámicamente y pueden tener cualquier valor de JavaScript. Los motores de JavaScript realizan optimizaciones para que estos arreglos sean rápidos.

- -

Sin embargo, a medida que las aplicaciones web se vuelven cada vez más poderosas, agregando características como manipulación de audio y video, acceso a datos sin procesar usando WebSockets, etc., ha quedado claro que hay momentos en los que sería útil que el código JavaScript pudiera manipular rápida y fácilmente datos binarios sin procesar. Aquí es donde entran en juego los arreglos tipados. Cada entrada en un arreglo tipado de JavaScript es un valor binario sin procesar en uno de los formatos admitidos, desde números enteros de 8 bits hasta números de punto flotante de 64 bits.

- -

Sin embargo, los arreglos tipados no se deben confundir con los arreglos normales, ya que llamar a {{JSxRef("Array.isArray()")}} en un arreglo tipado devuelve false. Además, no todos los métodos disponibles para arreglos normales son compatibles con arreglos tipados (por ejemplo, push y pop).

- -

Búferes y vistas: arquitectura de los arreglos tipados

- -

Para lograr la máxima flexibilidad y eficiencia, los arreglos de JavaScript dividen la implementación en búferes y vistas. Un búfer (implementado por el objeto {{jsxref("ArrayBuffer")}} es un objeto que representa una porción de datos; no tiene ningún formato del que hablar y no ofrece ningún mecanismo para acceder a su contenido. Para acceder a la memoria contenida en un búfer, necesitas usar una vista. Una vista proporciona un contexto — es decir, un tipo de dato, un desplazamiento inicial y el número de elementos — que convierte los datos en un arreglo tipado.

- -

Arreglos tipados en ArrayBuffer

- -

ArrayBuffer

- -

{{jsxref("ArrayBuffer")}} es un tipo de dato que se utiliza para representar un búfer de datos binarios genérico de longitud fija. No puedes manipular directamente el contenido de un ArrayBuffer; en su lugar, crea una vista de arreglo tipado o un {{JSxRef("DataView")}} que representa el búfer en un formato específico, y lo usa para leer y escribir el contenido del búfer.

- -

Vistas de arreglos tipados

- -

Las vistas de arreglos tipados tienen nombres autodescriptivos y proporcionan vistas para todos los tipos numéricos habituales tal como Int8, Uint32, Float64 y así sucesivamente. Hay una vista de arreglo tipado especial, la Uint8ClampedArray. Esta fija los valores entre 0 y 255. {{JSxRef("../Data_structures", "Tipos de datos JavaScript")}}

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TipoIntervalo de valoresTamaño en bytesDescripciónTipo de IDL webTipo C equivalente
{{JSxRef("Int8Array")}}-128 a 1271Dos enteros complementarios de 8 bits con signobyteint8_t
{{JSxRef("Uint8Array")}}0 a 2551Entero de 8-bit sin signooctetuint8_t
{{JSxRef("Uint8ClampedArray")}}0 a 2551Entero de 8 bits sin signo (sujeto)octetuint8_t
{{JSxRef("Int16Array")}}-32768 a 327672Dos enteros complementarios de 16 bits con signoshortint16_t
{{JSxRef("Uint16Array")}}0 a 655352Entero de 16 bits sin signoShort sin signouint16_t
{{JSxRef("Int32Array")}}-2147483648 a 21474836474dos enteros complementarios de 32 bits con signolongint32_t
{{JSxRef("Uint32Array")}}0 a 42949672954Enteros de 32 bits sin signolong sin signouint32_t
{{JSxRef("Float32Array")}}1.2×10-38 a 3.4×10384Número de coma flotante IEEE de 32 bits (7 dígitos significativos, p. ej., 1.1234567)float sin restriccionesfloat
{{JSxRef("Float64Array")}}1.2×10-38 a 3.4×103088Número de coma flotante IEEE de 64 bits (16 dígitos significativos, p. ej., 1.123...15)doble sin restriccionesdouble
{{JSxRef("BigInt64Array")}}-263 a 263-18Dos enteros complementarios de 64 bits con signobigintint64_t (long long con signo)
{{JSxRef("BigUint64Array")}}0 a 264-18Entero de 64 bits sin signobigintuint64_t (long long sin signo)
- -

DataView

- -

{{jsxref("DataView")}} es una interfaz de bajo nivel que proporciona una API captadora (getter)/(setter) establecedora para leer y escribir datos arbitrarios en el búfer. Esto es útil cuando se trata de diferentes tipos de datos, por ejemplo. Las vistas de arreglos tipados están en el orden de bytes nativo (consulta {{Glossary("Endianness")}} de tu plataforma. Con un DataView puedes controlar el orden de bytes. Es big-endian de manera predeterminada y se puede establecer en little-endian en los métodos captadores/establecedores.

- -

APIs web que utilizan arreglos tipados

- -

Estos son algunos ejemplos de APIs que utilizan arreglos tipados; hay otras, y todo el tiempo surgen más.

- -
-
FileReader.prototype.readAsArrayBuffer()
-
El método FileReader.prototype.readAsArrayBuffer() comienza a leer el contenido del Blob o File.
-
XMLHttpRequest.prototype.send()
-
El método send() de instancias de XMLHttpRequest ahora admiten arreglos tipados y objetos {{JSxRef("ArrayBuffer")}} como argumento.
-
ImageData.data
-
Es un {{JSxRef("Uint8ClampedArray")}} que representa un arreglo unidimensional que contiene los datos en el orden RGBA, con valores enteros entre 0 y 255 inclusive.
-
- -

Ejemplos

- -

Usar vistas con búferes

- -

En primer lugar, necesitaremos crear un búfer, aquí con una longitud fija de 16 bytes:

- -
let buffer = new ArrayBuffer(16);
-
- -

En este punto, tenemos una porción de memoria cuyos bytes están todos preiniciados a 0. Sin embargo, no hay mucho que podamos hacer con él. Podemos confirmar que de hecho tiene 16 bytes de longitud, y eso es todo:

- -
if (buffer.byteLength === 16) {
-  console.log("Sí, son 16 bytes");
-} else {
-  console.log("¡Oh no, es del tamaño incorrecto!");
-}
-
- -

Antes de que podamos trabajar realmente con este búfer, necesitamos crear una vista. Creemos una vista que trate los datos en el búfer como un arreglo de enteros de 32 bits con signo:

- -
let int32View = new Int32Array(buffer);
-
- -

Ahora podemos acceder a los campos del arreglo como un arreglo normal:

- -
for (let i = 0; i < int32View.length; i++) {
-  int32View[i] = i * 2;
-}
-
- -

Esto completa las 4 entradas en el arreglo (4 entradas de 4 bytes cada una suman 16 bytes en total) con los valores 0, 2, 4 y 6.

- -

Múltiples vistas sobre los mismos datos

- -

Las cosas comienzan a ponerse realmente interesantes cuando consideras que puedes crear múltiples vistas sobre los mismos datos. Por ejemplo, dado el código anterior, podemos continuar así:

- -
let int16View = new Int16Array(buffer);
-
-for (let i = 0; i < int16View.length; i++) {
-  console.log('Entrada ' + i + ': ' + int16View[i]);
-}
-
- -

Aquí creamos una vista entera de 16 bits que comparte el mismo búfer que la vista existente de 32 bits y sacamos todos los valores en el búfer como enteros de 16 bits. Ahora obtenemos la salida 0, 0, 2, 0, 4, 0, 6, 0.

- -

Sin embargo, puedes dar un paso más. Considera esto:

- -
int16View[0] = 32;
-console.log('La entrada 0 en el arreglo de 32 bits ahora es ' + int32View[0]);
-
- -

La salida de esto es "La entrada 0 en el arreglo de 32 bits ahora es 32".

- -

En otras palabras, los dos arreglos se ven simplemente en el mismo búfer de datos, tratándolo como formatos diferentes. Lo puedes hacer con cualquier {{JSxRef("Global_Objects/TypedArray", "tipo de vista", "#TypedArray_objects")}}.

- -

Trabajar con complejas estructuras de datos

- -

Al combinar un solo búfer con múltiples vistas de diferentes tipos, comenzando con diferentes desplazamientos en el búfer, puedes interactuar con objetos de datos que contienen múltiples tipos de datos. Esto te permite, por ejemplo, interactuar con complejas estructuras de datos WebGL, archivos de datos o estructuras C que necesitas utilizar mientras usas js-ctypes.

- -

Considera esta estructura C:

- -
struct someStruct {
-  unsigned long id;
-  char username[16];
-  float amountDue;
-};
- -

Puedes acceder a un búfer que contiene datos en un formato como este:

- -
let buffer = new ArrayBuffer(24);
-
-// ... lee los datos en el búfer ...
-
-let idView = new Uint32Array(buffer, 0, 1);
-let usernameView = new Uint8Array(buffer, 4, 16);
-let amountDueView = new Float32Array(buffer, 20, 1);
- -

Luego puedes acceder, por ejemplo, al monto adeudado con amountDueView[0].

- -
Nota: La {{interwiki("wikipedia", "Data_structure_alignment")}} en una estructura C depende de la plataforma. Toma precauciones y consideraciones para estas diferencias de relleno.
- -

Conversión a arreglos normales

- -

Después de procesar un arreglo con tipo, a veces es útil volver a convertirla en un arreglo normal para beneficiarse del prototipo {{jsxref("Array")}}. Esto se puede hacer usando {{JSxRef("Array.from()")}}, o usando el siguiente código donde Array.from() no es compatible.

- -
let typedArray = new Uint8Array([1, 2, 3, 4]),
-    normalArray = Array.prototype.slice.call(typedArray);
-normalArray.length === 4;
-normalArray.constructor === Array;
-
- -

Especificaciones

- - - - - - - - - - - - -
Especificación
{{SpecName('ESDraft', '#sec-typedarray-objects', 'TypedArray Objects')}}
- -

Compatibilidad del navegador

- - - -

{{Compat("javascript.builtins.Int8Array")}}

- -

Ve también

- - diff --git a/files/es/web/javascript/typed_arrays/index.md b/files/es/web/javascript/typed_arrays/index.md new file mode 100644 index 00000000000000..1a459dc5ad5a09 --- /dev/null +++ b/files/es/web/javascript/typed_arrays/index.md @@ -0,0 +1,180 @@ +--- +title: Arreglos tipados de JavaScript +slug: Web/JavaScript/Typed_arrays +tags: + - Arreglo tipado + - Guía + - JavaScript +translation_of: Web/JavaScript/Typed_arrays +original_slug: Web/JavaScript/Vectores_tipados +--- +{{JsSidebar("Advanced")}} + +**Los arreglos tipados en JavaScript** son objetos similares a arreglos que proporcionan un mecanismo para leer y escribir datos binarios sin procesar en búferes de memoria. Como ya sabrás, los objetos {{jsxref("Array", "Arreglo")}} crecen y se encogen dinámicamente y pueden tener cualquier valor de JavaScript. Los motores de JavaScript realizan optimizaciones para que estos arreglos sean rápidos. + +Sin embargo, a medida que las aplicaciones web se vuelven cada vez más poderosas, agregando características como manipulación de audio y video, acceso a datos sin procesar usando `WebSockets`, etc., ha quedado claro que hay momentos en los que sería útil que el código JavaScript pudiera manipular rápida y fácilmente datos binarios sin procesar. Aquí es donde entran en juego los arreglos tipados. Cada entrada en un arreglo tipado de JavaScript es un valor binario sin procesar en uno de los formatos admitidos, desde números enteros de 8 bits hasta números de punto flotante de 64 bits. + +Sin embargo, los arreglos tipados _no_ se deben confundir con los arreglos normales, ya que llamar a {{JSxRef("Array.isArray()")}} en un arreglo tipado devuelve `false`. Además, no todos los métodos disponibles para arreglos normales son compatibles con arreglos tipados (por ejemplo, `push` y `pop`). + +## Búferes y vistas: arquitectura de los arreglos tipados + +Para lograr la máxima flexibilidad y eficiencia, los arreglos de JavaScript dividen la implementación en _búferes_ y _vistas_. Un búfer (implementado por el objeto {{jsxref("ArrayBuffer")}} es un objeto que representa una porción de datos; no tiene ningún formato del que hablar y no ofrece ningún mecanismo para acceder a su contenido. Para acceder a la memoria contenida en un búfer, necesitas usar una vista. Una vista proporciona un contexto — es decir, un tipo de dato, un desplazamiento inicial y el número de elementos — que convierte los datos en un arreglo tipado. + +![Arreglos tipados en ArrayBuffer](https://mdn.mozillademos.org/files/8629/typed_arrays.png) + +### `ArrayBuffer` + +{{jsxref("ArrayBuffer")}} es un tipo de dato que se utiliza para representar un búfer de datos binarios genérico de longitud fija. No puedes manipular directamente el contenido de un `ArrayBuffer`; en su lugar, crea una vista de arreglo tipado o un {{JSxRef("DataView")}} que representa el búfer en un formato específico, y lo usa para leer y escribir el contenido del búfer. + +### Vistas de arreglos tipados + +Las vistas de arreglos tipados tienen nombres autodescriptivos y proporcionan vistas para todos los tipos numéricos habituales tal como `Int8`, `Uint32`, `Float64` y así sucesivamente. Hay una vista de arreglo tipado especial, la `Uint8ClampedArray`. Esta fija los valores entre 0 y 255. {{JSxRef("../Data_structures", "Tipos de datos JavaScript")}} + +| Tipo | Intervalo de valores | Tamaño en bytes | Descripción | Tipo de IDL web | Tipo C equivalente | +| ---------------------------------------- | ----------------------------- | --------------- | ----------------------------------------------------------------------------------------- | ------------------------- | -------------------------------- | +| {{JSxRef("Int8Array")}} | `-128` a `127` | 1 | Dos enteros complementarios de 8 bits con signo | `byte` | `int8_t` | +| {{JSxRef("Uint8Array")}} | `0` a `255` | 1 | Entero de 8-bit sin signo | `octet` | `uint8_t` | +| {{JSxRef("Uint8ClampedArray")}} | `0` a `255` | 1 | Entero de 8 bits sin signo (sujeto) | `octet` | `uint8_t` | +| {{JSxRef("Int16Array")}} | `-32768` a `32767` | 2 | Dos enteros complementarios de 16 bits con signo | `short` | `int16_t` | +| {{JSxRef("Uint16Array")}} | `0` a `65535` | 2 | Entero de 16 bits sin signo | `Short sin signo` | `uint16_t` | +| {{JSxRef("Int32Array")}} | `-2147483648` a `2147483647` | 4 | dos enteros complementarios de 32 bits con signo | `long` | `int32_t` | +| {{JSxRef("Uint32Array")}} | `0` a `4294967295` | 4 | Enteros de 32 bits sin signo | `long sin signo` | `uint32_t` | +| {{JSxRef("Float32Array")}} | `1.2`×`10-38` a `3.4`×`1038` | 4 | Número de coma flotante IEEE de 32 bits (7 dígitos significativos, p. ej., `1.1234567`) | `float sin restricciones` | `float` | +| {{JSxRef("Float64Array")}} | `1.2`×`10-38` a `3.4`×`10308` | 8 | Número de coma flotante IEEE de 64 bits (16 dígitos significativos, p. ej., `1.123...15`) | `doble sin restricciones` | `double` | +| {{JSxRef("BigInt64Array")}} | `-263` a `263-1` | 8 | Dos enteros complementarios de 64 bits con signo | `bigint` | `int64_t (long long con signo)` | +| {{JSxRef("BigUint64Array")}} | `0` a `264-1` | 8 | Entero de 64 bits sin signo | `bigint` | `uint64_t (long long sin signo)` | + +### `DataView` + +{{jsxref("DataView")}} es una interfaz de bajo nivel que proporciona una API captadora (`getter`)/(`setter`) establecedora para leer y escribir datos arbitrarios en el búfer. Esto es útil cuando se trata de diferentes tipos de datos, por ejemplo. Las vistas de arreglos tipados están en el orden de bytes nativo (consulta {{Glossary("Endianness")}} de tu plataforma. Con un `DataView` puedes controlar el orden de bytes. Es `big-endian` de manera predeterminada y se puede establecer en `little-endian` en los métodos captadores/establecedores. + +## APIs web que utilizan arreglos tipados + +Estos son algunos ejemplos de APIs que utilizan arreglos tipados; hay otras, y todo el tiempo surgen más. + +- {{Link("/en-US/docs/Web/API/FileReader", "FileReader.prototype.readAsArrayBuffer()", "#readAsArrayBuffer()")}} + - : El método `FileReader.prototype.readAsArrayBuffer()` comienza a leer el contenido del {{Link("/es/docs/Web/API/Blob", "Blob")}} o {{Link("/es/docs/Web/API/File", "File")}}. +- [`XMLHttpRequest.prototype.send()`]() + - : El método `send()` de instancias de `XMLHttpRequest` ahora admiten arreglos tipados y objetos {{JSxRef("ArrayBuffer")}} como argumento. +- {{Link("/en-US/docs/Web/API/ImageData", "ImageData.data")}} + - : Es un {{JSxRef("Uint8ClampedArray")}} que representa un arreglo unidimensional que contiene los datos en el orden RGBA, con valores enteros entre `0` y `255` inclusive. + +## Ejemplos + +### Usar vistas con búferes + +En primer lugar, necesitaremos crear un búfer, aquí con una longitud fija de 16 bytes: + +```js +let buffer = new ArrayBuffer(16); +``` + +En este punto, tenemos una porción de memoria cuyos bytes están todos preiniciados a 0. Sin embargo, no hay mucho que podamos hacer con él. Podemos confirmar que de hecho tiene 16 bytes de longitud, y eso es todo: + +```js +if (buffer.byteLength === 16) { + console.log("Sí, son 16 bytes"); +} else { + console.log("¡Oh no, es del tamaño incorrecto!"); +} +``` + +Antes de que podamos trabajar realmente con este búfer, necesitamos crear una vista. Creemos una vista que trate los datos en el búfer como un arreglo de enteros de 32 bits con signo: + +```js +let int32View = new Int32Array(buffer); +``` + +Ahora podemos acceder a los campos del arreglo como un arreglo normal: + +```js +for (let i = 0; i < int32View.length; i++) { + int32View[i] = i * 2; +} +``` + +Esto completa las 4 entradas en el arreglo (4 entradas de 4 bytes cada una suman 16 bytes en total) con los valores `0`, `2`, `4` y `6`. + +### Múltiples vistas sobre los mismos datos + +Las cosas comienzan a ponerse realmente interesantes cuando consideras que puedes crear múltiples vistas sobre los mismos datos. Por ejemplo, dado el código anterior, podemos continuar así: + +```js +let int16View = new Int16Array(buffer); + +for (let i = 0; i < int16View.length; i++) { + console.log('Entrada ' + i + ': ' + int16View[i]); +} +``` + +Aquí creamos una vista entera de 16 bits que comparte el mismo búfer que la vista existente de 32 bits y sacamos todos los valores en el búfer como enteros de 16 bits. Ahora obtenemos la salida `0`, `0`, `2`, `0`, `4`, `0`, `6`, `0`. + +Sin embargo, puedes dar un paso más. Considera esto: + +```js +int16View[0] = 32; +console.log('La entrada 0 en el arreglo de 32 bits ahora es ' + int32View[0]); +``` + +La salida de esto es `"La entrada 0 en el arreglo de 32 bits ahora es 32"`. + +En otras palabras, los dos arreglos se ven simplemente en el mismo búfer de datos, tratándolo como formatos diferentes. Lo puedes hacer con cualquier {{JSxRef("Global_Objects/TypedArray", "tipo de vista", "#TypedArray_objects")}}. + +### Trabajar con complejas estructuras de datos + +Al combinar un solo búfer con múltiples vistas de diferentes tipos, comenzando con diferentes desplazamientos en el búfer, puedes interactuar con objetos de datos que contienen múltiples tipos de datos. Esto te permite, por ejemplo, interactuar con complejas estructuras de datos {{Link("/es/docs/Web/WebGL", "WebGL")}}, archivos de datos o estructuras C que necesitas utilizar mientras usas {{Link("/en-US/docs/Mozilla/js-ctypes", "js-ctypes")}}. + +Considera esta estructura C: + +```cpp +struct someStruct { + unsigned long id; + char username[16]; + float amountDue; +}; +``` + +Puedes acceder a un búfer que contiene datos en un formato como este: + +```js +let buffer = new ArrayBuffer(24); + +// ... lee los datos en el búfer ... + +let idView = new Uint32Array(buffer, 0, 1); +let usernameView = new Uint8Array(buffer, 4, 16); +let amountDueView = new Float32Array(buffer, 20, 1); +``` + +Luego puedes acceder, por ejemplo, al monto adeudado con `amountDueView[0]`. + +> **Nota:** La {{interwiki("wikipedia", "Data_structure_alignment")}} en una estructura C depende de la plataforma. Toma precauciones y consideraciones para estas diferencias de relleno. + +### Conversión a arreglos normales + +Después de procesar un arreglo con tipo, a veces es útil volver a convertirla en un arreglo normal para beneficiarse del prototipo {{jsxref("Array")}}. Esto se puede hacer usando {{JSxRef("Array.from()")}}, o usando el siguiente código donde `Array.from()` no es compatible. + +```js +let typedArray = new Uint8Array([1, 2, 3, 4]), + normalArray = Array.prototype.slice.call(typedArray); +normalArray.length === 4; +normalArray.constructor === Array; +``` + +## Especificaciones + +| Especificación | +| ------------------------------------------------------------------------------------------------ | +| {{SpecName('ESDraft', '#sec-typedarray-objects', 'TypedArray Objects')}} | + +## Compatibilidad del navegador + +{{Compat("javascript.builtins.Int8Array")}} + +## Ve también + +- [Conseguir ArrayBuffers o arreglos tipados a partir de cadenas codificadas en Base64](/es/docs/Glossary/Base64#appendix.3a_decode_a_base64_string_to_uint8array_or_arraybuffer) +- [StringView: una representación en C de cadenas basadas en arreglos tipados](/es/docs/Code_snippets/StringView "This is a link to an unwritten page") +- [Rápida manipulación de píxeles en canvas con arreglos tipados](https://hacks.mozilla.org/2011/12/faster-canvas-pixel-manipulation-with-typed-arrays) +- [Arreglos tipados: Datos binarios en el navegador](http://www.html5rocks.com/en/tutorials/webgl/typed_arrays) +- {{Glossary("Endianness")}}