Podemos facilmente criar esses objetos usando a sintaxe literal do objeto. Se parece com isso:

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());//3const 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());//3const 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.