Modificación de objetos ECMAScript

Al usar ECMAScript, no solo se pueden crear objetos, sino que también se puede modificar el comportamiento de los objetos existentes.

La propiedad 'prototype' no solo puede definir las propiedades y métodos del constructor, sino que también puede agregar propiedades y métodos a los objetos locales.

Crear nuevos métodos

Crear nuevos métodos a través de métodos existentes

Se puede definir nuevos métodos para cualquier clase existente utilizando la propiedad prototype, como si se estuviera tratando de su propia clase. Por ejemplo, ¿recuerdas el método toString() de la clase Number? Si se le pasa un parámetro de 16, devuelve una cadena hexadecimal. Si el parámetro de este método es 2, devuelve una cadena binaria. Podemos crear un método que convierta directamente un objeto número a una cadena hexadecimal. Crear este método es muy simple:

Number.prototype.toHexString = function() {
  return this.toString(16);
};

En este entorno, la palabra clave this apunta a una instancia de Number, por lo que se puede acceder completamente a todos los métodos de Number. Con este código, se puede realizar la siguiente operación:

var iNum = 15;
alert(iNum.toHexString());		//Mostrar "F"

TIY

Dado que el número 15 es F en hexadecimal, el aviso se mostrará como "F".

Renombrar métodos existentes

También podemos renombrar métodos existentes para que sean más comprensibles. Por ejemplo, podemos agregar a la clase Array los métodos enqueue() y dequeue(), que simplemente llaman a los métodos push() y shift() existentes:

Array.prototype.enqueue = function(vItem) {
  this.push(vItem);
};
Array.prototype.dequeue = function() {
  return this.shift();
};

TIY

Agregar métodos no relacionados con métodos existentes

Por supuesto, también se pueden agregar métodos que no estén relacionados con los métodos existentes. Por ejemplo, supongamos que queremos determinar la posición de un elemento en un array sin que exista un método local para hacer esto. Podemos crear fácilmente el siguiente método:

Array.prototype.indexOf = function (vItem) {
  for (var i=0; i<this.length; i++) {
    if (vItem == this[i]) {
	  return i;
	}
  }
  return -1;
}

Este método indexOf() es consistente con el método homónimo de la clase String, que busca cada elemento del array hasta que encuentra el elemento que se pasa como argumento. Si encuentra el elemento, devuelve la posición del elemento; de lo contrario, devuelve -1. Con esta definición, podemos escribir el siguiente código:

var aColors = new Array("red","green","blue");
alert(aColors.indexOf("green")); // salida: "1"

TIY

Agregar nuevos métodos a los objetos locales

Finalmente, si se desea agregar un nuevo método a cada objeto local de ECMAScript, debe definirse en el atributo prototype del objeto Object. Como se explicó en los capítulos anteriores, todos los objetos locales heredan del objeto Object, por lo que cualquier cambio en el objeto Object se reflejará en todos los objetos locales. Por ejemplo, si se desea agregar un método que muestre el valor actual del objeto con un aviso, se puede usar el siguiente código:

Object.prototype.showValue = function () {
  alert(this.valueOf());
};
var str = "hello";
var iNum = 25;
str.showValue(); // salida: "hello"
iNum.showValue(); // salida: "25"

TIY

Aquí, los objetos String y Number heredan el método showValue() del objeto Object, se llama a este método en sus objetos respectivos y se muestra "hello" y "25".

Redefinir métodos existentes

Al igual que se puede definir un nuevo método para una clase existente, también se puede redefinecir un método existente. Como se explicó en los capítulos anteriores, el nombre de la función es solo un puntero que apunta a la función, por lo que se puede fácilmente apuntar a otra función. ¿Qué sucederá si se modifica un método local, como toString()?

Function.prototype.toString = function() {
  return "Function code hidden";
}

El código anterior es completamente válido y el resultado de ejecución es completamente conforme a lo esperado:

function sayHi() {
  alert("hi");
}
alert(sayHi.toString()); // salida: "Function code hidden"

TIY

Tal vez recuerdes que en el capítulo sobre el objeto Function se presentó el método toString() del objeto Function, que generalmente devuelve el código fuente de la función. Al sobrescribir este método, se puede devolver otra cadena (en este ejemplo, "Function code hidden"). Sin embargo, ¿qué pasa con la función original a la que apunta toString()? Será reciclada por el programa de recolección de basura de almacenamiento inútil, ya que ha sido completamente desechada. No hay un método para restaurar la función original, por lo que es más seguro almacenar su puntero antes de sobrescribir el método original, para su uso futuro. A veces, incluso puedes llamar al método original en el nuevo método:

Function.prototype.originalToString = Function.prototype.toString;
Function.prototype.toString = function() {
  if (this.originalToString().length > 100) {
    return "La función es demasiado larga para mostrar.";
  } else {
    return this.originalToString();
  }
};

TIY

En este código, la primera línea guarda una referencia a la método toString() actual en el atributo originalToString. Luego, se sobrescribe el método toString() con un método personalizado. El nuevo método verifica si la longitud del código fuente de la función es mayor de 100. Si es así, devuelve un mensaje de error indicando que el código de la función es demasiado largo, de lo contrario, llama al método originalToString() y devuelve el código fuente de la función.

Enlace muy tardío (Very Late Binding)

Técnicamente, no existe un enlace muy tardío. Este libro utiliza este término para describir un fenómeno en ECMAScript, que permite definir métodos de un objeto después de que se ha instanciado. Por ejemplo:

var o = new Object();
Object.prototype.sayHi = function () {
  alert("hi");
};
o.sayHi();

TIY

En la mayoría de los lenguajes de programación, es necesario definir los métodos del objeto antes de instanciar el objeto. Aquí, el método sayHi() se agrega después de crear una instancia de la clase Object. En los lenguajes tradicionales, no se ha escuchado esta operación ni se ha escuchado que este método se asigna automáticamente a la instancia del objeto Object y se puede usar inmediatamente (en la siguiente línea).

Nota:No se recomienda usar métodos de enlace muy tardío, ya que es difícil rastrearlos y registrarlos. Sin embargo, aún se debe entender esta posibilidad.