JavaScript Stvori objekt - Kako definirati objekte u JS-u

Objekti su glavna jedinica enkapsulacije u objektno orijentiranom programiranju. U ovom ću članku opisati nekoliko načina za izgradnju objekata u JavaScript-u. Oni su:

  • Objekt doslovno
  • Object.create ()
  • Nastava
  • Tvorničke funkcije

Objekt doslovno

Prvo, moramo napraviti razliku između struktura podataka i objektno orijentiranih objekata. Strukture podataka imaju javne podatke i ne ponašaju se. To znači da nemaju metode.

Takve objekte možemo lako stvoriti koristeći doslovnu sintaksu objekta. Izgleda ovako:

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

Objekti u JavaScript-u dinamične su zbirke parova ključ / vrijednost. Ključ je uvijek niz i mora biti jedinstven u kolekciji. Vrijednost može primitiv, objekt ili čak funkciju.

Svojstvu možemo pristupiti pomoću točke ili kvadratnog zapisa.

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

Evo primjera gdje je vrijednost drugi objekt.

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

Vrijednost carbsnekretnine je novi objekt. Evo kako možemo pristupiti carbsnekretnini.

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

Stenografska imena svojstava

Razmotrimo slučaj kada imamo vrijednosti svojih svojstava pohranjenih u varijable.

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

JavaScript podržava ono što se naziva skraćenim imenima svojstava. Omogućuje nam stvaranje objekta koristeći samo ime varijable. Stvorit će svojstvo s istim imenom. Sljedeći je doslovni objekt ekvivalentan prethodnom.

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

Objekt.stvari

Dalje, pogledajmo kako implementirati objekte s ponašanjem, objektno orijentirane objekte.

JavaScript ima ono što se naziva prototipnim sustavom koji omogućuje dijeljenje ponašanja između objekata. Glavna ideja je stvoriti objekt koji se naziva prototip s uobičajenim ponašanjem, a zatim ga koristiti pri stvaranju novih objekata.

Prototipni sustav omogućuje nam stvaranje objekata koji nasljeđuju ponašanje od drugih objekata.

Stvorimo prototipski objekt koji nam omogućuje dodavanje proizvoda i dobivanje ukupne cijene iz košarice.

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

Primijetite da je ovaj put vrijednost svojstva addProductfunkcija. Prethodni objekt također možemo napisati pomoću kraćeg oblika koji se naziva sintaksa skraćene metode.

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

The cartPrototypeje prototipni objekt koji zadržava uobičajeno ponašanje predstavljeno dvjema metodama addProducti getTotalPrice. Može se koristiti za izgradnju drugih objekata koji nasljeđuju ovo ponašanje.

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

cartObjekt ima cartPrototypekao svoj prototip. Iz njega nasljeđuje ponašanje. cartima skriveno svojstvo koje upućuje na objekt prototipa.

Kada koristimo metodu na objektu, ta se metoda prvo pretražuje na samom objektu, a ne na njegovom prototipu.

ovaj

Imajte na umu da koristimo posebnu ključnu riječ pozvanu thisza pristup i izmjenu podataka na objektu.

Imajte na umu da su funkcije neovisne jedinice ponašanja u JavaScript-u. Oni nisu nužno dio predmeta. Kad jesu, moramo imati referencu koja omogućuje funkciji pristup ostalim članovima na istom objektu. thisje kontekst funkcije. Omogućuje pristup drugim svojstvima.

Podaci

Možda se pitate zašto nismo definirali i inicijalizirali productssvojstvo na samom prototipu objekta.

Ne bismo to trebali raditi. Za razmjenu ponašanja, a ne podataka, trebaju se koristiti prototipovi. Dijeljenje podataka rezultirat će istim proizvodima na nekoliko predmeta u košarici. Razmotrite donji kod:

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

Both the cart1 and cart2 objects inheriting the common behavior from the cartPrototype also share the same data. We don’t want that. Prototypes should be used to share behavior, not data.

Class

The prototype system is not a common way of building objects. Developers are more familiar with building objects out of classes.

The class syntax allows a more familiar way of creating objects sharing a common behavior. It still creates the same prototype behind the scene but the syntax is clearer and we also avoid the previous data-related issue. The class offers a specific place to define the data distinct for each object.

Here is the same object created using the class sugar syntax:

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

Notice that the class has a constructor method that initialized that data distinct for each new object. The data in the constructor is not shared between instances. In order to create a new instance, we use the new keyword.

I think the class syntax is more clear and familiar to most developers. Nevertheless, it does a similar thing, it creates a prototype with all the methods and uses it to define new objects. The prototype can be accessed with Cart.prototype.

It turns out that the prototype system is flexible enough to allow the class syntax. So the class system can be simulated using the prototype system.

Private Properties

The only thing is that the products property on the new object is public by default.

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

We can make it private using the hash # prefix.

Private properties are declared with #name syntax. # is a part of the property name itself and should be used for declaring and accessing the property. Here is an example of declaring products as a private property:

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

Factory Functions

Another option is to create objects as collections of closures.

Closure is the ability of a function to access variables and parameters from the other function even after the outer function has executed. Take a look at the cart object built with what is called a factory function.

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 and getTotalPrice are two inner functions accessing the variable products from their parent. They have access to the products variable event after the parent Cart has executed. addProduct and getTotalPrice are two closures sharing the same private variable.

Cart is a factory function.

The new object cart created with the factory function has the products variable private. It cannot be accessed from the outside.

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

Factory functions don’t need the new keyword but you can use it if you want. It will return the same object no matter if you use it or not.

Recap

Usually, we work with two types of objects, data structures that have public data and no behavior and object-oriented objects that have private data and public behavior.

Data structures can be easily built using the object literal syntax.

JavaScript offers two innovative ways of creating object-oriented objects. The first is using a prototype object to share the common behavior. Objects inherit from other objects. Classes offer a nice sugar syntax to create such objects.

The other option is to define objects are collections of closures.

For more on closures and function programming techniques check out my book series Functional Programming with JavaScript and React.

The Functional Programming in JavaScript book is coming out.