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..
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
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 miObjeto
mé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!
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.
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 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.
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 Cuerda
prototipo 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.
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..