Trabalhando com objetos

A linguagem JavaScript é projetada com base em um simples paradigma orientado a objeto. Um objeto é uma coleção de propriedades, e uma propriedade é uma associação entre um nome e um valor. Um valor de propriedade pode ser uma função, que é então considerada um método do objeto. Além dos objetos que são pré-definidos no browser, você pode definir seus próprios objetos.

Este capítulo descreve como usar objetos, propriedades, funções, e métodos, e como criar seus próprios objetos.

Visão geral de objetos

Objetos em JavaScript, exatamente como em muitas outras linguagens de programação, podem ser comparados com objetos na vida real. O conceito de objetos em JavaScript pode ser entendido com objetos tangíveis da vida real.

Em JavaScript, um objeto é uma a entidade independente, com propriedades e tipo. Compare-o com uma xícara, por exemplo. Uma xícara é um objeto, com propriedades. Uma xícara tem uma cor, uma forma, peso, um material de composição, etc. De mesma forma, objetos em JavaScript podem ter propriedades, que definem suas características.

Objetos e propriedades

Um objeto em JavaScript tem propriedades associadas a ele. Uma propriedade de um objeto pode ser explicada como uma variável que é ligada ao objeto. Propriedades de objetos são basicamente as mesmas que variáveis normais em JavaScript, exceto pelo fato de estarem ligadas a objetos. As propriedades de um objeto definem as características do objeto. Você acessa as propriedades de um objeto com uma simples notação de ponto:

nomeDoObjeto.nomeDaPropriedade

Como as variáveis em JavaScript, o nome do objeto (que poderia ser uma variável normal) e um nome de propriedade diferem em maiúsculas/minúsculas (por exemplo, cor e Cor são propriedades diferentes). Você pode definir uma propriedade atribuindo um valor a ela. Por exemplo, vamos criar um objeto chamado meuCarro e dar a ele propriedades chamadas make, modelo, e ano, conforme mostrado a seguir:

var meuCarro = new Object();
meuCarro.make = "Ford";
meuCarro.modelo = "Mustang";
meuCarro.ano = 1969;

Propriedades de objetos em JavaScript podem também ser acessada ou alterada usando-se notação de colchete. Objetos são às vezes chamados de arrays associativos, uma vez que cada propriedade é associada com um valor de string que pode ser usado para acessá-la. Então, por exemplo, você poderia acessar as propriedades do objeto meuCarro como se segue:

meuCarro["make"] = "Ford";
meuCarro["modelo"] = "Mustang";
meuCarro["ano"] = 1969;

Um nome de propriedade de um objeto pode ser qualquer string JavaScript válida, ou qualquer coisa que pode ser convertida em uma string, incluindo a string vazia. No entanto, qualquer nome e propriedade que não é um identificador JavaScript válido (por exemplo, um nome de propriedade que tem um espaço ou um hífen, ou que começa com um número) só pode ser acessado(a) usando-se a notação de colchetes. Essa notação é também muito útil quando nomes de propriedades devem ser determinados dinamicamente (quando o nome da propriedade não é determinado até o momento de execução). Exemplos são mostrados a seguir:

var meuObj = new Object(),
    str = "minhaString",
    aleat = Math.random(),
    obj = new Object();

meuObj.tipo              = "Sintaxe de ponto";
meuObj["data de criacao"]   = "String com espaco";
meuObj[str]              = "valor de String";
meuObj[aleat]             = "Numero Aleatorio";
meuObj[obj]              = "Objeto";
meuObj[""]               = "Mesmo uma string vazia";

console.log(meuObj);

Você pode também acessar propriedades usando um valor de string que está armazenado em uma variável:

var nomeDaPropriedade = "make";
meuCarro[nomeDaPropriedade] = "Ford";

nomeDaPropriedade = "modelo";
myCar[nomeDaPropriedade] = "Mustang";

Você pode usar a notação de colchetes com o comando for...in para iterar por todas as propriedades enumeráveis de um objeto. Para ilustrar como isso funciona, a seguinte função mostra as propriedades de um objeto quando você passa o objeto e o nome do objeto como argumentos para a função:

function mostrarProps(obj, nomeDoObj) {
  var resultado = "";
  for (var i in obj) {
    if (obj.hasOwnProperty(i)) {
        resultado += nomeDoObj + "." + i + " = " + obj[i] + "\n";
    }
  }
  return resultado;
}

Então, a chamada de função showProps(myCar, "myCar") retornaria o seguinte:

meuCarro.make = Ford
meuCarro.modelo = Mustang
meuCarro.ano = 1969

Objetos: tudo

Em JavaScript, quase tudo é um objeto. Todos os tipos primitivos - com exceção de null e undefined - são tratados como objetos. Eles podem receber propriedades (propriedades atribuídas de alguns tipos não são persistentes), e possuem todas as características de objetos.

Enumerando todas as propriedades de um objeto

Começando com a ECMAScript 5, há três formas nativas de se listar (ou "caminhar por") as propriedades de um objeto:

  • for...in loops
    Esse método caminha por todas as propriedades enumeráveis de um objeto e sua cadeia de protótipos
  • Object.keys(o)
    Esse método retorna um array com todos os nomes ("chaves") de propriedades próprios de um objeto o (mas não na cadeia de protótipos).
  • Object.getOwnPropertyNames(o)
    Esse método retorna um array contendo todos os nomes de propriedades próprios (enumeráveis ou não) de um objeto o.

Na ECMAScript 5, não existe uma forma nativa de se listar todas as propriedades de um objeto. No entanto, isso pode ser feito com a seguinte função:

function listAllProperties(o){     
	var objectToInspect;     
	var result = [];
	
	for(objectToInspect = o; objectToInspect !== null; objectToInspect = Object.getPrototypeOf(objectToInspect)){  
		result = result.concat(Object.getOwnPropertyNames(objectToInspect));  
	}
	
	return result; 
}

Isso pode ser útil para revelar propriedades "escondidads" (propriedades na cadeia de protótipos que não são acessíveis através do objeto, porque outra propriedade possui o mesmo nome anteriormente na cadeia de protótipos). A listagem de propriedades acessíveis só pode ser facilmente feita através da remoção de valores duplicados no array.

Criando novos objetos

JavaScript possui um número de objetos pré-definidos. Além disso, você pode criar seus próprios objetos. Em JavaScript 1.2 e nas versões posteriores, você pode criar um objeto usando um inicializador de objeto. Alternativamente, você pode primeiro criar uma função construtora e depois instanciar um objeto usando aquela função e o operador new.

Usando inicializadores de objeto

Além de criar objetos usando uma função construtora, você pode criar objetos usando um inicializador de objeto. O uso de inicializadores de objeto é às vezes conhecido como criar objetos com notação literal. O termo "inicializador de objeto" é consistente com a terminologia usada por C++.

A sintaxe para um objeto usando-se um inicializador de objeto é:

var obj = { propriedade_1:   valor_1,   // propriedade_# pode ser um identificador...
            2:            valor_2,   // ou um numero...
            // ...,
            "propriedade n": valor_n }; // ou uma string

onde obj é o nome do novo objeto, cada propriedade_i é um identificador (um nome, um número, ou uma string literal), e cada valor_i é uma expressão cujo valor é atribuído à propriedade_i. O obj e a atribuição são opcionais; se você não precisa fazer referência a esse objeto em nenhum outro local, você não precisa atribuí-lo a uma variável. (Note que você pode precisar colocar a literal de objeto entre parentêses se o objeto aparece onde um comando é experado, de modo a não ter a literal confundidade com um bloco de comandos.)

Se um objeto é criado com um inicializador de objeto em um script de alto nível, JavaScript interpreta o objeto a cada vez que avalia uma expressão contendo a literal de objeto. Além disso, um inicializador usado em uma função é criado toda vez que a função é chamada.

O seguinte comando cria um objeto e o atribui à variável x se e somente se a expressão cond é verdadeira.

if (cond) var x = {hi: "there"};

O seguinte exemplo cria minhaHonda com três propriedades. Note que a propriedade motor é também um objeto com suas próprias propriedades.

var minhaHonda = {cor: "vermelho", rodas: 4, motor: {cilindros: 4, tamanho: 2.2}};

Você pode também usar inicializadores de objeto para criar arrays. Veja array literals.

Em JavaScript 1.1 e anteriormente, você não pode usar inicializadores de objeto. Você pode criar objetos apenas suas funções construtoras ou usando uma função fornecida por algum outro objeto para esse propósito. Veja Usando uma função construtora.

Usando uma função construtora

Alternativamente, você pode criar um objeto com estes dois passos:

  1. Defina o tipo de objeto escrendo uma função construtora. Há uma forte convenção, e com boa razão, de se usar uma letra inicial maiúscula.
  2. Crie uma instância do objeto com new.

Para definir um tipo de objeto, crie uma função para o tipo de objeto que especifique seu nome, suas propriedades e seus métodos. Por exemplo, suponha que você criar um tipo objeto para carros. Você quer que esse tipo de objeto seja chamado carro, e você quer ele tenha propriedades de marca, modelo, e ano. Para fazer isto, você escreveria a seguinte função:

function Carro(marca, modelo, ano) {
  this.marca = marca;
  this.modelo = modelo;
  this.ano = ano;
}

Note o uso de this para atribuir valores às propriedades do objeto com base nos valores passados para a função.

Agora você pode criar um objeto chamado meucarro como se segue:

var meucarro = new Carro("Eagle", "Talon TSi", 1993);

Esse comando cria meucarro e atribui a ele valores especificados para suas propriedade. Então o vaor de meucarro.marca é a string "Eagle", meucarro.ano é o inteiro 1993, e assim por diante.

Você pode criar qualquer número de objetos carro com o uso de new. Exemplo,

var carroDeKen = new Carro("Nissan", "300ZX", 1992);
var carroDeVPG = new Carro("Mazda", "Miata", 1990);

Um objeto pode ter uma propriedade que por si só também é um objeto. Por exemplo, suponha que você define um objeto chamado pessoa como se segue:

function Pessoa(nome, idade, sexo) {
  this.nome = nome;
  this.idade = idade;
  this.sexo = sexo;
}

e então você instancia dois novos objetos pessoa da seguinte forma:

var rand = new Pessoa("Rand McKinnon", 33, "M");
var ken = new Pessoa("Ken Jones", 39, "M");

Então, você pode reescrever a definição de carro de modo a incluir uma propriedade dono que recebe um objeto pessoa, como se segue:

function Car(marca, modelo, ano, dono) {
  this.marca = marca;
  this.modelo = modelo;
  this.ano = ano;
  this.dono = dono;
}

Para instanciar os novos objetos, você então usa o seguinte:

var carro1 = new Carro("Eagle", "Talon TSi", 1993, rand);
var carro2 = new Carro("Nissan", "300ZX", 1992, ken);

Perceba que ao invés de passar uma string literal ou um valor inteiro na hora de criar os novos objetos, os comandos acima passam os objetos rand e ken como os argumentos para os donos. Então se você quiser descobrir o nome do dono de carro2, você pode acessar a seguinte propriedade:

carro2.dono.nome

Note que você pode sempre adicionar uma propriedade a um objeto definido anteriormente. Por exemplo, o comando

carro1.cor = "preto";

adiciona uma propriedade cor ao carro1, e dá a ele o valor "black." No entanto, isso não afeta nenhum outro objeto. Para adicionar a nova propriedade a todos os objetos do mesmo tipo, você deve adicionar a propriedade na definição do tipo de objeto carro.

Usando o método Object.create

Objetos podem também ser criados usando-se o método Object.create. Esse método pode ser muito útil, pois permite que você escolha o objeto protótipo para o objeto que você quer criar, sem a necessidade de se definir uma função construtora. Para informações mais detalhadas sobre o método e sobre como usá-lo, veja Object.create method

Herança

Todos os objetos em JavaScript herdam de pelo menos um outro objeto. O objeto "pai" é conhecido como o protótipo, e as propriedades herdadas podem ser encontradas no objeto prototype do construtor.

Indexing object properties

In JavaScript 1.0, you can refer to a property of an object either by its property name or by its ordinal index. In JavaScript 1.1 and later, however, if you initially define a property by its name, you must always refer to it by its name, and if you initially define a property by an index, you must always refer to it by its index.

This restriction applies when you create an object and its properties with a constructor function (as we did previously with the Car object type) and when you define individual properties explicitly (for example, myCar.color = "red"). If you initially define an object property with an index, such as myCar[5] = "25 mpg", you can subsequently refer to the property only as myCar[5].

The exception to this rule is objects reflected from HTML, such as the forms array. You can always refer to objects in these arrays by either their ordinal number (based on where they appear in the document) or their name (if defined). For example, if the second <FORM> tag in a document has a NAME attribute of "myForm", you can refer to the form as document.forms[1] or document.forms["myForm"] or document.myForm.

Defining properties for an object type

You can add a property to a previously defined object type by using the prototype property. This defines a property that is shared by all objects of the specified type, rather than by just one instance of the object. The following code adds a color property to all objects of type car, and then assigns a value to the color property of the object car1.

Car.prototype.color = null;
car1.color = "black";

See the prototype property of the Function object in the JavaScript Reference for more information.

Defining methods

A method is a function associated with an object, or, simply put, a method is a property of an object that is a function. Methods are defined the way normal functions are defined, except that they have to be assigned as the property of an object. Examples are:

objectName.methodname = function_name;

var myObj = {
  myMethod: function(params) {
    // ...do something
  }
};

where objectName is an existing object, methodname is the name you are assigning to the method, and function_name is the name of the function.

You can then call the method in the context of the object as follows:

object.methodname(params);

You can define methods for an object type by including a method definition in the object constructor function. For example, you could define a function that would format and display the properties of the previously-defined car objects; for example,

function displayCar() {
  var result = "A Beautiful " + this.year + " " + this.make
    + " " + this.model;
  pretty_print(result);
}

where pretty_print is a function to display a horizontal rule and a string. Notice the use of this to refer to the object to which the method belongs.

You can make this function a method of car by adding the statement

this.displayCar = displayCar;

to the object definition. So, the full definition of car would now look like

function Car(make, model, year, owner) {
  this.make = make;
  this.model = model;
  this.year = year;
  this.owner = owner;
  this.displayCar = displayCar;
}

Then you can call the displayCar method for each of the objects as follows:

car1.displayCar();
car2.displayCar();

This produces the output shown in the following figure.

Image:obja.gif

Figure 7.1: Displaying method output.

Using this for object references

JavaScript has a special keyword, this, that you can use within a method to refer to the current object. For example, suppose you have a function called validate that validates an object's value property, given the object and the high and low values:

function validate(obj, lowval, hival) {
  if ((obj.value < lowval) || (obj.value > hival))
    alert("Invalid Value!");
}

Then, you could call validate in each form element's onchange event handler, using this to pass it the element, as in the following example:

<input type="text" name="age" size="3"
  onChange="validate(this, 18, 99)">

In general, this refers to the calling object in a method.

When combined with the form property, this can refer to the current object's parent form. In the following example, the form myForm contains a Text object and a button. When the user clicks the button, the value of the Text object is set to the form's name. The button's onclick event handler uses this.form to refer to the parent form, myForm.

<form name="myForm">
<p><label>Form name:<input type="text" name="text1" value="Beluga"></label>
<p><input name="button1" type="button" value="Show Form Name"
     onclick="this.form.text1.value = this.form.name">
</p>
</form>

Defining getters and setters

A getter is a method that gets the value of a specific property. A setter is a method that sets the value of a specific property. You can define getters and setters on any predefined core object or user-defined object that supports the addition of new properties. The syntax for defining getters and setters uses the object literal syntax.

JavaScript 1.8.1 note

Starting in JavaScript 1.8.1, setters are no longer called when setting properties in object and array initializers.

The following JS shell session illustrates how getters and setters could work for a user-defined object o. The JS shell is an application that allows developers to test JavaScript code in batch mode or interactively. In Firefox you can get a JS shell by pressing Ctrl+Shift+K.

js> var o = {a: 7, get b() {return this.a + 1;}, set c(x) {this.a = x / 2}};
[object Object]
js> o.a;
7
js> o.b;
8
js> o.c = 50;
js> o.a;
25

The o object's properties are:

  • o.a — a number
  • o.b — a getter that returns o.a plus 1
  • o.c — a setter that sets the value of o.a to half of the value o.c is being set to

Please note that function names of getters and setters defined in an object literal using "[gs]et property()" (as opposed to __define[GS]etter__ ) are not the names of the getters themselves, even though the [gs]et propertyName(){ } syntax may mislead you to think otherwise. To name a function in a getter or setter using the "[gs]et property()" syntax, define an explicitly named function programmatically using Object.defineProperty (or the Object.prototype.__defineGetter__ legacy fallback).

This JavaScript shell session illustrates how getters and setters can extend the Date prototype to add a year property to all instances of the predefined Date class. It uses the Date class's existing getFullYear and setFullYear methods to support the year property's getter and setter.

These statements define a getter and setter for the year property:

js> var d = Date.prototype;
js> Object.defineProperty(d, "year", {
    get: function() {return this.getFullYear() },
    set: function(y) { this.setFullYear(y) }
});

These statements use the getter and setter in a Date object:

js> var now = new Date;
js> print(now.year);
2000
js> now.year = 2001;
987617605170
js> print(now);
Wed Apr 18 11:13:25 GMT-0700 (Pacific Daylight Time) 2001

Obsolete syntaxes

In the past, JavaScript supported several other syntaxes for defining getters and setters. None of these syntaxes were supported by other engines, and support has been removed in recent versions of JavaScript. See this dissection of the removed syntaxes for further details on what was removed and how to adapt to those removals.

Summary

In principle, getters and setters can be either

  • defined using object initializers, or
  • added later to any object at any time using a getter or setter adding method.

When defining getters and setters using object initializers all you need to do is to prefix a getter method with get and a setter method with set. Of course, the getter method must not expect a parameter, while the setter method expects exactly one parameter (the new value to set). For instance:

var o = {
  a: 7,
  get b() { return this.a + 1; },
  set c(x) { this.a = x / 2; }
};

Getters and setters can also be added to an object at any time after creation using the Object.defineProperties method. This method's first parameter is the object on which you want to define the getter or setter. The second parameter is an object whose property names are the getter or setter names, and whose property values are objects for defining the getter or setter functions. Here's an example that defines the same getter and getter used in the previous example:
 

var o = { a:0 }

Object.defineProperties(o, {
    "b": { get: function () { return this.a + 1; } },
    "c": { set: function (x) { this.a = x / 2; } }
});

o.c = 10 // Runs the setter, which assigns 10 / 2 (5) to the 'a' property
console.log(o.b) // Runs the getter, which yields a + 1 or 6

Which of the two forms to choose depends on your programming style and task at hand. If you already go for the object initializer when defining a prototype you will probably most of the time choose the first form. This form is more compact and natural. However, if you need to add getters and setters later — because you did not write the prototype or particular object — then the second form is the only possible form. The second form probably best represents the dynamic nature of JavaScript — but it can make the code hard to read and understand.

Prior to Firefox 3.0, getter and setter are not supported for DOM Elements. Older versions of Firefox silently fail. If exceptions are needed for those, changing the prototype of HTMLElement (HTMLElement.prototype.__define[SG]etter__) and throwing an exception is a workaround.

With Firefox 3.0, defining getter or setter on an already-defined property will throw an exception. The property must be deleted beforehand, which is not the case for older versions of Firefox.

See also

Deleting properties

You can remove a non-inherited property by using the delete operator. The following code shows how to remove a property.

//Creates a new object, myobj, with two properties, a and b.
var myobj = new Object;
myobj.a = 5;
myobj.b = 12;

//Removes the a property, leaving myobj with only the b property.
delete myobj.a;
console.log ("a" in myobj) // yields "false"

You can also use delete to delete a global variable if the var keyword was not used to declare the variable:

g = 17;
delete g;

See delete for more information.

See also

Anexos

Arquivo Tamanho Data Anexado por
obja.gif
3284 bytes 2005-04-22 08:49:02 JdeValk
understanding-underlines-figure06.gif
2038 bytes 2005-05-02 06:15:07 CitizenK

Document Tags and Contributors

Contributors to this page: fernando.karpinski
Última atualização por: fernando.karpinski,