JavaScript Create Object – Como definir objetos em JS

const product = {
  name: 'apple',
  category: 'fruits',
  price: 1.99
}
  
console.log(product);

Objetos em JavaScript são coleções dinâmicas de pares de valores-chave. A chave é sempre uma string e deve ser exclusiva na coleção. O valor pode ser um primitivo, um objeto ou até uma função.

Podemos acessar uma propriedade usando o ponto ou a notação quadrada.

console.log(product.name);
//"apple"

console.log(product["name"]);
//"apple"

Aqui está um exemplo em que o valor é outro objeto.

const product = {
  name: 'apple',
  category: 'fruits',
  price: 1.99,
  nutrients : {
   carbs: 0.95,
   fats: 0.3,
   protein: 0.2
 }
}

O valor do carbs A propriedade é um novo objeto. Aqui está como podemos acessar o carbs propriedade.

console.log(product.nutrients.carbs);
//0.95

Nomes de propriedade abreviados

Considere o caso em que temos os valores de nossas propriedades armazenados em variáveis.

const name="apple";
const category = 'fruits';
const price = 1.99;
const product = {
  name: name,
  category: category,
  price: price
}

O JavaScript suporta o que é chamado de nomes abreviados de propriedades. Isso nos permite criar um objeto usando apenas o nome da variável. Ele criará uma propriedade com o mesmo nome. O próximo literal de objeto é equivalente ao anterior.

const name="apple";
const category = 'fruits';
const price = 1.99;
const product = {
  name,
  category,
  price
}

Object.create

A seguir, vejamos como implementar objetos com comportamento, objetos orientados a objetos.

O JavaScript possui o que é chamado de sistema de protótipo que permite compartilhar o comportamento entre objetos. A idéia principal é criar um objeto chamado protótipo com um comportamento comum e usá-lo ao criar novos objetos.

O sistema de protótipo nos permite criar objetos que herdam o comportamento de outros objetos.

Vamos criar um objeto protótipo que nos permita adicionar produtos e obter o preço total de um carrinho de compras.

const cartPrototype = {
  addProduct: function(product){
    if(!this.products){
     this.products = [product]
    } else {
     this.products.push(product);
    }
  },
  getTotalPrice: function(){
    return this.products.reduce((total, p) => total + p.price, 0);
  }
}

Observe que desta vez o valor da propriedade addProduct é uma função. Também podemos escrever o objeto anterior usando um formulário mais curto chamado sintaxe do método abreviado.

const cartPrototype = {
  addProduct(product){/*code*/},
  getTotalPrice(){/*code*/}
}

o cartPrototype é o objeto de protótipo que mantém o comportamento comum representado por dois métodos, addProduct e getTotalPrice. Ele pode ser usado para criar outros objetos que herdam esse comportamento.

const cart = Object.create(cartPrototype);
cart.addProduct({name: 'orange', price: 1.25});
cart.addProduct({name: 'lemon', price: 1.75});

console.log(cart.getTotalPrice());
//3

o cart objeto tem cartPrototype como seu protótipo. Ele herda o comportamento dele. cart possui uma propriedade oculta que aponta para o objeto de protótipo.

Quando usamos um método em um objeto, esse método é pesquisado primeiro no próprio objeto, e não no seu protótipo.

isto

Observe que estamos usando uma palavra-chave especial chamada this para acessar e modificar os dados no objeto.

Lembre-se de que funções são unidades independentes de comportamento em JavaScript. Eles não são necessariamente parte de um objeto. Quando estão, precisamos ter uma referência que permita que a função acesse outros membros no mesmo objeto. this é o contexto da função. Dá acesso a outras propriedades.

Dados

Você pode se perguntar por que não definimos e inicializamos o products propriedade no próprio objeto protótipo.

Não devemos fazer isso. Protótipos devem ser usados ​​para compartilhar comportamento, não dados. O compartilhamento de dados levará a ter os mesmos produtos em vários objetos do carrinho. Considere o código abaixo:

const cartPrototype = {
  products:[],
  addProduct: function(product){
      this.products.push(product);
  },
  getTotalPrice: function(){}
}

const cart1 = Object.create(cartPrototype);
cart1.addProduct({name: 'orange', price: 1.25});
cart1.addProduct({name: 'lemon', price: 1.75});
console.log(cart1.getTotalPrice());
//3

const cart2 = Object.create(cartPrototype);
console.log(cart2.getTotalPrice());
//3

Tanto o cart1 e cart2 objetos que herdam o comportamento comum do cartPrototype também compartilham os mesmos dados. Nós não queremos isso. Protótipos devem ser usados ​​para compartilhar comportamento, não dados.

Classe

O sistema de protótipo não é uma maneira comum de construir objetos. Os desenvolvedores estão mais familiarizados com a construção de objetos fora das classes.

A sintaxe da classe permite uma maneira mais familiar de criar objetos que compartilham um comportamento comum. Ele ainda cria o mesmo protótipo nos bastidores, mas a sintaxe é mais clara e também evitamos o problema anterior relacionado a dados. A classe oferece um local específico para definir os dados distintos para cada objeto.

Aqui está o mesmo objeto criado usando a sintaxe da classe sugar:

class Cart{
  constructor(){
    this.products = [];
  }
  
  addProduct(product){
      this.products.push(product);
  }
  
  getTotalPrice(){
    return this.products.reduce((total, p) => total + p.price, 0);
  }
}

const cart = new Cart();
cart.addProduct({name: 'orange', price: 1.25});
cart.addProduct({name: 'lemon', price: 1.75});
console.log(cart.getTotalPrice());
//3

const cart2 = new Cart();
console.log(cart2.getTotalPrice());
//0

Observe que a classe possui um método construtor que inicializou esses dados distintos para cada novo objeto. Os dados no construtor não são compartilhados entre instâncias. Para criar uma nova instância, usamos o new palavra-chave

Eu acho que a sintaxe da classe é mais clara e familiar para a maioria dos desenvolvedores. No entanto, faz uma coisa semelhante, cria um protótipo com todos os métodos e o utiliza para definir novos objetos. O protótipo pode ser acessado com Cart.prototype.

Acontece que o sistema de protótipo é flexível o suficiente para permitir a sintaxe da classe. Portanto, o sistema de classes pode ser simulado usando o sistema de protótipo.

Propriedades Privadas

A única coisa é que o products A propriedade no novo objeto é pública por padrão.

console.log(cart.products);
//[{name: "orange", price: 1.25}
// {name: "lemon", price: 1.75}]

Podemos torná-lo privado usando o hash # prefixo.

Propriedades privadas são declaradas com #name sintaxe. # faz parte do próprio nome da propriedade e deve ser usado para declarar e acessar a propriedade. Aqui está um exemplo de declaração products como propriedade privada:

class Cart{
  #products
  constructor(){
    this.#products = [];
  }
  
  addProduct(product){
    this.#products.push(product);
  }
  
  getTotalPrice(){
    return this.#products.reduce((total, p) => total + p.price, 0);
  }
}

console.log(cart.#products);
//Uncaught SyntaxError: Private field '#products' must be declared in an enclosing class

Funções de fábrica

Outra opção é criar objetos como coleções de fechamentos.

Encerramento é a capacidade de uma função acessar variáveis ​​e parâmetros da outra função, mesmo após a execução da função externa. Dê uma olhada no cart objeto construído com o que é chamado de função de fábrica.

function Cart() {
  const products = [];
  
  function addProduct(product){
    products.push(product);
  }
  
  function getTotalPrice(){
    return products.reduce((total, p) => total + p.price, 0);
  }
  
  return {
   addProduct,
   getTotalPrice
  }
}

const cart = Cart();
cart.addProduct({name: 'orange', price: 1.25});
cart.addProduct({name: 'lemon', price: 1.75});
console.log(cart.getTotalPrice());
//3

addProduct e getTotalPrice são duas funções internas acessando a variável products de seus pais. Eles têm acesso ao products evento variável após o pai Cart foi executado. addProduct e getTotalPrice Existem dois fechamentos que compartilham a mesma variável privada.

Cart é uma função de fábrica.

O novo objeto cart criado com a função de fábrica tem o products variável privada. Não pode ser acessado de fora.

console.log(cart.products);
//undefined

As funções de fábrica não precisam do new palavra-chave, mas você pode usá-lo se quiser. Ele retornará o mesmo objeto, independentemente de você usá-lo ou não.

Recapitular

Normalmente, trabalhamos com dois tipos de objetos, estruturas de dados que possuem dados públicos e nenhum comportamento e objetos orientados a objetos que possuem dados privados e comportamento público.

As estruturas de dados podem ser facilmente construídas usando a sintaxe literal do objeto.

O JavaScript oferece duas maneiras inovadoras de criar objetos orientados a objetos. O primeiro é usar um objeto protótipo para compartilhar o comportamento comum. Objetos herdam de outros objetos. As aulas oferecem uma boa sintaxe de açúcar para criar esses objetos.

A outra opção é definir objetos são coleções de fechamentos.

Para saber mais sobre fechamentos e técnicas de programação de funções, confira minha série de livros Programação Funcional com JavaScript e React.

o Programação Funcional em JavaScript livro está saindo.