Prototipos en JavaScript

Cuando define una función dentro de JavaScript, viene con algunas propiedades predefinidas; Uno de ellos es el prototipo ilusorio. En este artículo, detallaré qué es y por qué debería usarlo en sus proyectos..


Que es prototipo?

La propiedad prototipo es inicialmente un objeto vacío y se le pueden agregar miembros, como cualquier otro objeto..

var myObject = function (name) this.name = name; devuelve esto ; console.log (typeof myObject.prototype); // object myObject.prototype.getName = function () return this.name; ;

En el fragmento anterior, hemos creado una función, pero si llamamos myObject (), simplemente devolverá el ventana Objeto, porque se definió dentro del ámbito global.. esta por lo tanto, devolverá el objeto global, ya que aún no se ha creado una instancia (más sobre esto más adelante).

console.log (myObject () === ventana); // cierto

El enlace secreto

Cada objeto dentro de JavaScript tiene una propiedad "secreta".

Antes de continuar, me gustaría hablar sobre el enlace "secreto" que hace que el prototipo funcione como lo hace..

Cada objeto dentro de JavaScript tiene una propiedad "secreta" agregada cuando se define o crea una instancia, llamada __proto__; Así es como se accede a la cadena de prototipos. Sin embargo, no es una buena idea acceder __proto__ dentro de su aplicación, ya que no está disponible en todos los navegadores.

los __proto__ la propiedad no debe confundirse con el prototipo de un objeto, ya que son dos propiedades separadas; Dicho esto, van de la mano. ¡Es importante hacer esta distinción, ya que puede ser bastante confuso al principio! Que significa exactamente? Dejame explicar. Cuando creamos el miObjeto Función, estábamos definiendo un objeto de tipo. Función.

console.log (typeof myObject); // función

Para aquellos que desconocen, Función es un objeto predefinido en JavaScript y, como resultado, tiene sus propias propiedades (por ejemplo,. longitud y argumentos) y métodos (por ejemplo,. llamada y aplicar). Y sí, también tiene su propio objeto prototipo, así como el secreto __proto__ enlazar. Esto significa que, en algún lugar dentro del motor de JavaScript, hay un poco de código que podría ser similar al siguiente:

 Function.prototype = argumentos: nulo, longitud: 0, call: function () // código secreto, apply: function () // código secreto ...

En verdad, probablemente no sería tan simplista; esto es simplemente para ilustrar cómo funciona la cadena prototipo.

Así lo hemos definido. miObjeto como una función y le da un argumento, nombre; pero nunca establecemos ninguna propiedad, como longitud o métodos, tales como llamada. Entonces, ¿por qué funciona lo siguiente??

console.log (myObject.length); // 1 (siendo la cantidad de argumentos disponibles)

Esto es porque, cuando definimos miObjeto, creó un __proto__ propiedad y establecer su valor en Función.prototipo (ilustrado en el código de arriba). Entonces, cuando accedemos myObject.length, busca una propiedad de miObjeto llamado longitud y no encuentra uno; luego viaja por la cadena, a través de la __proto__ enlace, encuentra la propiedad y la devuelve.

Usted podría preguntarse por qué longitud se establece en 1 y no 0 - o cualquier otro número por ese hecho. Esto es porque miObjeto es de hecho una instancia de Función.

console.log (myObject instanceof Function); // true console.log (myObject === Function); // falso

Cuando se crea una instancia de un objeto, el __proto__ La propiedad se actualiza para que apunte al prototipo del constructor, que, en este caso, es Función.

console.log (myObject .__ proto__ === Function.prototype) // true

Además, cuando creas una nueva Función objeto, el código nativo dentro de la Función El constructor contará el número de argumentos y la actualización. este.longitud en consecuencia, que, en este caso, es 1.

Sin embargo, si creamos una nueva instancia de miObjeto utilizando la nuevo palabra clave, __proto__ apuntará a myObject.prototype como miObjeto Es el constructor de nuestra nueva instancia..

 var myInstance = new myObject ("foo"); console.log (myInstance .__ proto__ === myObject.prototype); // cierto

Además de tener acceso a los métodos nativos dentro de la Función.prototipo, como llamada y aplicar, ahora tenemos acceso a miObjetométodo de, getName.

 console.log (myInstance.getName ()); // foo var mySecondInstance = new myObject ("bar"); console.log (mySecondInstance.getName ()); // bar console.log (myInstance.getName ()); // foo

Como puede imaginar, esto es muy útil, ya que se puede utilizar para diseñar un objeto y crear tantas instancias como sea necesario, lo que me lleva al siguiente tema!


¿Por qué está usando prototipo mejor??

Digamos, por ejemplo, que estamos desarrollando un juego de lienzos y necesitamos varios objetos (posiblemente cientos) en la pantalla a la vez. Cada objeto requiere sus propias propiedades, tales como X y y coordenadas, anchura,altura, y muchos otros.

Podríamos hacerlo de la siguiente manera:

 var GameObject1 = x: Math.floor ((Math.random () * myCanvasWidth) + 1), y: Math.floor ((Math.random () * myCanvasHeight) + 1), ancho: 10, altura: 10, draw: function () myCanvasContext.fillRect (this.x, this.y, this.width, this.height); …; var GameObject2 = x: Math.floor ((Math.random () * myCanvasWidth) + 1), y: Math.floor ((Math.random () * myCanvasHeight) + 1), ancho: 10, altura: 10 draw: function () myCanvasContext.fillRect (this.x, this.y, this.width, this.height); …;

... haz esto 98 veces más ...

Lo que esto hará es crear todos estos objetos dentro de la memoria, todos con definiciones separadas para métodos, como dibujar y cualquier otro método que se requiera. Esto ciertamente no es ideal, ya que el juego inflará la memoria de JavaScript asignada a los navegadores y lo hará funcionar muy lentamente ... o incluso dejará de responder.

Si bien esto probablemente no sucederá con solo 100 objetos, todavía puede ser un gran éxito de rendimiento, ya que tendrá que buscar cien objetos diferentes, en lugar de solo uno. prototipo objeto.


Cómo usar prototipo

Para hacer que la aplicación se ejecute más rápido (y seguir las mejores prácticas), podemos (re) definir la propiedad prototipo de GameObject; cada instancia de GameObject a continuación, hará referencia a los métodos dentro de GameObject.prototype Como si fueran sus propios métodos..

 // define la función constructora GameObject var GameObject = function (width, height) this.x = Math.floor ((Math.random () * myCanvasWidth) + 1); this.y = Math.floor ((Math.random () * myCanvasHeight) + 1); this.width = ancho; this.height = altura; devuelve esto ; // (re) defina el objeto prototipo GameObject GameObject.prototype = x: 0, y: 0, width: 5, width: 5, draw: function () myCanvasContext.fillRect (this.x, this.y, this .width, this.height); ;

Luego podemos instanciar el GameObject 100 veces.

 var x = 100, arrayOfGameObjects = []; do arrayOfGameObjects.push (nuevo GameObject (10, 10));  while (x--);

Ahora tenemos una serie de 100 GameObjects, que comparten el mismo prototipo y definición del mismo. dibujar Método, que ahorra drásticamente la memoria dentro de la aplicación.

Cuando llamamos al dibujar Método, hará referencia a la misma función exacta..

 var GameLoop = function () for (gameObject in arrayOfGameObjects) gameObject.draw (); ;

El prototipo es un objeto vivo

El prototipo de un objeto es un objeto vivo, por así decirlo. Esto simplemente significa que, si, después de crear todas nuestras instancias de GameObject, decidimos que, en lugar de dibujar un rectángulo, queremos dibujar un círculo, podemos actualizar nuestra GameObject.prototype.draw método en consecuencia.

 GameObject.prototype.draw = function () myCanvasContext.arc (this.x, this.y, this.width, 0, Math.PI * 2, true); 

Y ahora, todas las instancias anteriores de GameObject y cualquier caso futuro dibujará un círculo.


Actualización de prototipos de objetos nativos

Sí, esto es posible. Puede estar familiarizado con las bibliotecas de JavaScript, como Prototype, que aprovechan este método..

Vamos a usar un ejemplo simple:

 String.prototype.trim = function () return this.replace (/ ^ \ s + | \ s + $ / g, ");;

Ahora podemos acceder a esto como un método de cualquier cadena:

“Foo bar” .trim (); // “foo bar”

Sin embargo, hay un inconveniente menor en esto. Por ejemplo, puedes usar esto en tu aplicación; pero uno o dos años más tarde, un navegador puede implementar una versión actualizada de JavaScript que incluye una versión nativa recortar método dentro del Cuerdaprototipo de. Esto significa que su definición de recortar ¡Anulará la versión nativa! ¡Ay! Para superar esto, podemos agregar una comprobación simple antes de definir la función.

 if (! String.prototype.trim) String.prototype.trim = function () devuelve this.replace (/ ^ \ s + | \ s + $ / g, ");;

Ahora, si existe, utilizará la versión nativa del recortar método.

Como regla general, generalmente se considera una mejor práctica para evitar extender objetos nativos. Pero, como con cualquier cosa, las reglas pueden romperse, si es necesario.


Conclusión

Afortunadamente, este artículo ha arrojado algo de luz sobre la columna vertebral de JavaScript que es un prototipo. Ahora debería estar en el camino para crear aplicaciones más eficientes..

Si tiene alguna pregunta sobre el prototipo, hágamelo saber en los comentarios y haré todo lo posible para responderlos..