Az objektumok a kapszulázás fő egysége az objektum-orientált programozásban. Ebben a cikkben az objektumok JavaScript-ben történő létrehozásának számos módját ismertetem. Ők:
- Objektum szó szerint
- Object.create ()
- Osztályok
- Gyári funkciók
Object Literal
Először is különbséget kell tennünk az adatstruktúrák és az objektumorientált objektumok között. Az adatstruktúrák nyilvános adatokkal rendelkeznek, és nem viselkednek. Ez azt jelenti, hogy nincsenek módszereik.
Könnyen létrehozhatunk ilyen objektumokat az objektum szó szerinti szintaxisa segítségével. Ez így néz ki:
const product = { name: 'apple', category: 'fruits', price: 1.99 } console.log(product);
Az objektumok a JavaScript-ben kulcs-érték párok dinamikus gyűjteményei. A kulcs mindig egy karakterlánc, és egyedinek kell lennie a gyűjteményben. Az érték lehet primitív, objektum vagy akár függvény is.
A tulajdonsághoz a pont vagy a négyzet jelöléssel férhetünk hozzá.
console.log(product.name); //"apple" console.log(product["name"]); //"apple"
Itt van egy példa, ahol az érték egy másik objektum.
const product = { name: 'apple', category: 'fruits', price: 1.99, nutrients : { carbs: 0.95, fats: 0.3, protein: 0.2 } }
A carbs
tulajdonság értéke új objektum. Így érhetjük el az carbs
ingatlant.
console.log(product.nutrients.carbs); //0.95
Gyorsírású tulajdonnevek
Tekintsük azt az esetet, amikor tulajdonságaik értékeit változókban tároljuk.
const name = 'apple'; const category = 'fruits'; const price = 1.99; const product = { name: name, category: category, price: price }
A JavaScript támogatja az úgynevezett rövidített tulajdonságneveket. Ez lehetővé teszi számunkra, hogy objektumot hozzunk létre csak a változó nevével. Létrehoz egy azonos nevű tulajdonságot. A következő objektum literál megegyezik az előzővel.
const name = 'apple'; const category = 'fruits'; const price = 1.99; const product = { name, category, price }
Object.create
Ezután nézzük meg, hogyan lehet megvalósítani az objektumokat viselkedéssel, objektum-orientált objektumokkal.
A JavaScript rendelkezik az úgynevezett prototípus rendszerrel, amely lehetővé teszi az objektumok közötti viselkedés megosztását. A fő gondolat egy prototípus nevű objektum létrehozása közös viselkedéssel, majd felhasználás új objektumok létrehozásakor.
A prototípus rendszer lehetővé teszi olyan objektumok létrehozását, amelyek más objektumok viselkedését öröklik.
Hozzunk létre egy prototípus objektumot, amely lehetővé teszi számunkra, hogy termékeket adjunk hozzá, és a teljes árat bevásárlókosárból szerezzük be.
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); } }
Vegye figyelembe, hogy ezúttal a tulajdonság addProduct
értéke függvény. Az előző objektumot megírhatjuk egy rövidebb formával is, amelyet gyorsírásos módszer szintaxisnak nevezünk.
const cartPrototype = { addProduct(product){/*code*/}, getTotalPrice(){/*code*/} }
Az cartPrototype
a prototípus objektum, amely a közös viselkedést két módszerrel képviseli, addProduct
és getTotalPrice
. Használható más, ezt a viselkedést öröklő objektumok felépítésére.
const cart = Object.create(cartPrototype); cart.addProduct({name: 'orange', price: 1.25}); cart.addProduct({name: 'lemon', price: 1.75}); console.log(cart.getTotalPrice()); //3
Az cart
objektum cartPrototype
prototípusa. Örökli tőle a viselkedést. cart
rejtett tulajdonsággal rendelkezik, amely a prototípus objektumra mutat.
Ha egy objektumon használunk metódust, akkor ezt a módszert először magán az objektumon keresik, nem pedig a prototípusán.
ez
Ne feledje, hogy egy speciális kulcsszót használunk this
az objektum adatainak eléréséhez és módosításához.
Ne feledje, hogy a függvények a JavaScript független viselkedési egységei. Nem feltétlenül tartoznak egy tárgyhoz. Amikor vannak, rendelkeznünk kell egy referenciával, amely lehetővé teszi a függvény számára, hogy ugyanazon az objektumon más tagokhoz is hozzáférjen. this
a függvénykörnyezet. Hozzáférést biztosít más ingatlanokhoz.
Adat
Kíváncsi lehet, miért nem definiáltuk és inicializáltuk a products
tulajdonságot magán a prototípus objektumon.
Nem kellene ezt tennünk. Prototípusokat kell használni a viselkedés megosztásához, nem az adatokhoz. Az adatok megosztása azt eredményezi, hogy ugyanazok a termékek vannak több kosárobjektumon. Vegye figyelembe az alábbi kódot:
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
A közös viselkedést öröklődő objektumok cart1
és az cart2
objektumok cartPrototype
is ugyanazokat az adatokat osztják meg. Ezt nem akarjuk. Prototípusokat kell használni a viselkedés megosztásához, nem az adatokhoz.
Osztály
A prototípus rendszer nem elterjedt módszer az objektumok építésére. A fejlesztők jobban ismerik az objektumok osztályon kívüli építését.
Az osztály szintaxisa lehetővé teszi a közös viselkedésű objektumok létrehozásának ismertebb módját. Még mindig ugyanazt a prototípust hozza létre a jelenet mögött, de a szintaxis egyértelműbb, és elkerüljük a korábbi adatokkal kapcsolatos problémát is. Az osztály egy meghatározott helyet kínál az egyes objektumoktól elkülönülő adatok meghatározásához.
Itt van ugyanaz az objektum, amelyet az osztály cukorszintaxisával hoztak létre:
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.