Modificação de Objetos ECMAScript

Usando ECMAScript, não só é possível criar objetos, mas também modificar o comportamento dos objetos existentes.

A propriedade 'prototype' pode não só definir atributos e métodos do construtor, mas também adicionar atributos e métodos ao objeto local.

Criar novos métodos

Criar novos métodos através de métodos existentes

Pode-se usar a propriedade prototype para definir novos métodos para qualquer classe existente, como se estivesse lidando com sua própria classe. Por exemplo, lembra-se do método toString() da classe Number? Se passar o parâmetro 16, ele será output como uma string hexadecimal. Se o parâmetro do método for 2, ele será output como uma string binária. Podemos criar um método que converte diretamente um objeto número em uma string hexadecimal. Criar esse método é muito simples:

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

Neste ambiente, a palavra-chave this aponta para uma instância de Number, portanto, pode acessar todos os métodos de Number. Com esse código, podemos realizar a seguinte operação:

var iNum = 15;
alert(iNum.toHexString());		//Saída "F"

TIY

Como o número 15 é F em hexadecimal, o aviso será exibido como "F".

Renomear métodos existentes

Também podemos renomear métodos existentes para nomes mais compreensíveis. Por exemplo, podemos adicionar dois métodos enqueue() e dequeue() à classe Array, que chamam repetidamente os métodos push() e shift() existentes:

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

TIY

Adicionar métodos não relacionados aos existentes

Claro, também podemos adicionar métodos que não sejam relacionados aos métodos existentes. Por exemplo, suponha que você precise determinar a posição de um item em um array, não há método local que faça isso. Podemos facilmente criar o seguinte 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() mantém a consistência com o método同名方法 da classe String, pesquisando cada item do array até encontrar o item que foi passado. Se encontrar o item correspondente, retorna a posição do item, caso contrário, retorna -1. Com essa definição, podemos escrever o seguinte código:

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

TIY

Adicionar novos métodos para objetos locais

Por fim, se você quiser adicionar um novo método a todos os objetos locais do ECMAScript, você deve defini-lo na propriedade prototype do objeto Object. Como mencionado nos capítulos anteriores, todos os objetos locais herdam o objeto Object, então qualquer mudança feita no objeto Object será refletida em todos os objetos locais. Por exemplo, se você quiser adicionar um método que imprime o valor atual do objeto usando um alerta, você pode usar o seguinte código:

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

TIY

Aqui, os objetos String e Number herdam o método showValue() do objeto Object, chamando esse método em seus próprios objetos, mostrando "hello" e "25".

Redefinir métodos existentes

Da mesma forma que você pode definir novos métodos para classes existentes, você também pode redefinir métodos existentes. Como mencionado nos capítulos anteriores, o nome da função é apenas um ponteiro para a função, portanto, pode ser facilmente apontado para outra função. O que acontece se você modificar um método local, como toString()?

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

O código anterior é completamente válido e o resultado da execução é completamente conforme esperado:

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

TIY

Talvez você ainda se lembre, no capítulo sobre o objeto Function, foi introduzido o método toString() do Function, que geralmente retorna o código-fonte da função. Sobrescrevendo esse método, você pode retornar outra string (neste exemplo, pode retornar "Function code hidden"). No entanto, o que acontece com a função original para a qual toString() aponta? Ela será recolhida pelo programa de recolha de unidades de armazenamento inúteis, pois foi completamente abandonada. Não há maneira de restaurar a função original, então é mais seguro armazenar seu ponteiro antes de sobrescrever o método original, para que ele possa ser usado no futuro. Às vezes, você até pode chamar o método original no novo método:

Function.prototype.originalToString = Function.prototype.toString;
Function.prototype.toString = function() {
  if (this.originalToString().length > 100) {
    return "A função é muito longa para ser exibida.";
  } else {
    return this.originalToString();
  }
};

TIY

Neste código, a primeira linha armazena a referência ao método toString() atual na propriedade originalToString. Em seguida, um método personalizado substitui o método toString(). O novo método verifica se o comprimento do código-fonte da função é maior que 100. Se for, retorna uma mensagem de erro indicando que o código da função é muito longo, caso contrário, chama o método originalToString() e retorna o código-fonte da função.

Ligação muito tardia (Very Late Binding)

Técnicamente, não existe a ligação muito tardia. Este livro usa o termo para descrever um fenômeno no ECMAScript, que permite definir métodos após a instânciação do objeto. Por exemplo:

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

TIY

Em muitos linguagens de programação, é necessário definir os métodos do objeto antes da instânciação do objeto. Aqui, o método sayHi() é adicionado após a criação de uma instância da classe Object. Em linguagens tradicionais, não se conhece essa operação nem o método que automaticamente atribui ao objeto da instância de Object e pode ser usado imediatamente (na próxima linha).

Atenção:Não é recomendável usar o método de ligação muito tardia, pois é difícil rastrear e registrar. No entanto, ainda deve-se estar ciente dessa possibilidade.