Obiectele sunt unitatea principală de încapsulare în programarea orientată pe obiecte. În acest articol, voi descrie mai multe moduri de a construi obiecte în JavaScript. Sunt:

  • Obiect literal
  • Object.create ()
  • Clase
  • Funcții din fabrică

Obiect Literal

În primul rând, trebuie să facem o distincție între structurile de date și obiectele orientate obiect. Structurile de date au date publice și nu au comportament. Asta înseamnă că nu au metode.

Putem crea cu ușurință astfel de obiecte folosind sintaxa literală a obiectului. Arată așa:

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

Obiectele din JavaScript sunt colecții dinamice de perechi cheie-valoare. Cheia este întotdeauna un șir și trebuie să fie unică în colecție. Valoarea poate fi o primitivă, un obiect sau chiar o funcție.

Putem accesa o proprietate folosind notația punct sau pătrat.

ad-banner
console.log(product.name);
//"apple"

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

Iată un exemplu în care valoarea este un alt obiect.

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

Valoarea carbs proprietatea este un obiect nou. Iată cum putem accesa carbs proprietate.

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

Denumiri de proprietăți stenografice

Luați în considerare cazul în care avem valorile proprietăților noastre stocate în variabile.

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

JavaScript acceptă ceea ce se numește denumirea proprietăților. Ne permite să creăm un obiect folosind doar numele variabilei. Se va crea o proprietate cu același nume. Următorul obiect literal este echivalent cu cel anterior.

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

Obiect.create

În continuare, să vedem cum să implementăm obiecte cu comportament, obiecte orientate pe obiecte.

JavaScript are ceea ce se numește sistemul prototip care permite partajarea comportamentului între obiecte. Ideea principală este să creezi un obiect numit prototip cu un comportament comun și apoi să îl folosești atunci când creezi obiecte noi.

Sistemul prototip ne permite să creăm obiecte care moștenesc comportamentul de la alte obiecte.

Să creăm un obiect prototip care ne permite să adăugăm produse și să obținem prețul total dintr-un coș de cumpărături.

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);
  }
}

Observați că de data aceasta valoarea proprietății addProduct este o funcție. De asemenea, putem scrie obiectul anterior folosind o formă mai scurtă numită sintaxă a metodei de prescurtare.

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

cartPrototype este obiectul prototip care păstrează comportamentul comun reprezentat de două metode, addProduct și getTotalPrice. Poate fi folosit pentru a construi alte obiecte care moștenesc acest comportament.

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

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

cart obiect are cartPrototype ca prototip al acestuia. Moștenește comportamentul de la el. cart are o proprietate ascunsă care indică obiectul prototip.

Când folosim o metodă pe un obiect, acea metodă este mai întâi căutată pe obiectul în sine decât pe prototipul său.

acest

Rețineți că folosim un cuvânt cheie special numit this pentru a accesa și modifica datele de pe obiect.

Amintiți-vă că funcțiile sunt unități independente de comportament în JavaScript. Ele nu fac neapărat parte dintr-un obiect. Când sunt, trebuie să avem o referință care să permită funcției să acceseze alți membri pe același obiect. this este contextul funcției. Oferă acces la alte proprietăți.

Date

Vă puteți întreba de ce nu am definit și inițializat products proprietate asupra obiectului prototip în sine.

Nu ar trebui să facem asta. Prototipurile ar trebui utilizate pentru a împărtăși comportamentul, nu datele. Partajarea datelor va duce la aceeași produse pe mai multe obiecte de coș. Luați în considerare codul de mai jos:

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

Amandoua cart1 și cart2 obiecte care moștenesc comportamentul comun de la cartPrototype partajați, de asemenea, aceleași date. Nu vrem asta. Prototipurile ar trebui utilizate pentru a împărtăși comportamentul, nu datele.

Clasă

Sistemul prototip nu este un mod obișnuit de a construi obiecte. Dezvoltatorii sunt mai familiarizați cu construirea obiectelor în afara claselor.

Sintaxa clasei permite un mod mai familiar de a crea obiecte care împart un comportament comun. Încă creează același prototip în spatele scenei, dar sintaxa este mai clară și evităm și problema anterioară legată de date. Clasa oferă un loc specific pentru a defini datele distincte pentru fiecare obiect.

Iată același obiect creat folosind sintaxa clasei zahăr:

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

Observați că clasa are o metodă constructor care a inițializat acele date distincte pentru fiecare obiect nou. Datele din constructor nu sunt partajate între instanțe. Pentru a crea o nouă instanță, folosim new cuvânt cheie.

Cred că sintaxa clasei este mai clară și mai familiară pentru majoritatea dezvoltatorilor. Cu toate acestea, face un lucru similar, creează un prototip cu toate metodele și îl folosește pentru a defini obiecte noi. Prototipul poate fi accesat cu Cart.prototype.

Se pare că sistemul prototip este suficient de flexibil pentru a permite sintaxa clasei. Deci sistemul de clasă poate fi simulat folosind sistemul prototip.

Proprietăți private

Singurul lucru este că products proprietatea noului obiect este publică în mod implicit.

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

Îl putem face privat folosind hashul # prefix.

Proprietățile private sunt declarate cu #name sintaxă. # face parte din numele propriu-zis al proprietății și trebuie utilizat pentru declararea și accesarea proprietății. Iată un exemplu de declarare products ca proprietate privată:

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

Funcții din fabrică

O altă opțiune este de a crea obiecte ca colecții de închideri.

Închiderea este capacitatea unei funcții de a accesa variabile și parametri din cealaltă funcție chiar și după executarea funcției externe. Uită-te la cart obiect construit cu ceea ce se numește funcție din fabrică.

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 și getTotalPrice sunt două funcții interioare care accesează variabila products de la părintele lor. Au acces la products eveniment variabil după părinte Cart a executat. addProduct și getTotalPrice sunt două închideri care împart aceeași variabilă privată.

Cart este o funcție din fabrică.

Noul obiect cart creat cu funcția din fabrică are products variabil privat. Nu poate fi accesat din exterior.

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

Funcțiile din fabrică nu au nevoie de new cuvânt cheie, dar îl puteți folosi dacă doriți. Acesta va returna același obiect, indiferent dacă îl utilizați sau nu.

Recapitulare

De obicei, lucrăm cu două tipuri de obiecte, structuri de date care au date publice și fără comportament și obiecte orientate pe obiecte care au date private și comportament public.

Structurile de date pot fi construite cu ușurință folosind sintaxa literală a obiectului.

JavaScript oferă două moduri inovatoare de a crea obiecte orientate pe obiecte. Primul este utilizarea unui obiect prototip pentru a împărtăși comportamentul comun. Obiectele moștenesc de la alte obiecte. Clasele oferă o sintaxă frumoasă a zahărului pentru a crea astfel de obiecte.

Cealaltă opțiune este de a defini obiectele sunt colecții de închideri.

Pentru mai multe despre închideri și tehnici de programare a funcțiilor, consultați seria mea de cărți Programare funcțională cu JavaScript și React.

Programare funcțională în JavaScript cartea iese.