JavaScript Opret objekt - Sådan defineres objekter i JS

Objekter er den vigtigste enhed for indkapsling i objektorienteret programmering. I denne artikel vil jeg beskrive flere måder at oprette objekter i JavaScript på. De er:

  • Objekt bogstaveligt
  • Object.create ()
  • Klasser
  • Fabriksfunktioner

Objekt bogstaveligt

For det første skal vi skelne mellem datastrukturer og objektorienterede objekter. Datastrukturer har offentlige data og ingen adfærd. Det betyder, at de ikke har nogen metoder.

Vi kan nemt oprette sådanne objekter ved hjælp af objektets bogstavelige syntaks. Det ser sådan ud:

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

Objekter i JavaScript er dynamiske samlinger af nøgleværdipar. Nøglen er altid en streng og skal være unik i samlingen. Værdien kan være en primitiv, et objekt eller endda en funktion.

Vi kan få adgang til en ejendom ved hjælp af prikken eller kvadratnotationen.

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

Her er et eksempel, hvor værdien er et andet objekt.

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

Ejendommens værdi carbser et nyt objekt. Sådan får vi adgang til carbsejendommen.

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

Shorthand Property Navne

Overvej tilfældet, hvor vi har værdierne for vores egenskaber gemt i variabler.

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

JavaScript understøtter det, der kaldes stennavne. Det giver os mulighed for at oprette et objekt ved kun at bruge navnet på variablen. Det opretter en ejendom med samme navn. Det næste bogstavelige objekt svarer til det forrige.

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

Objekt. Opret

Lad os derefter se på, hvordan man implementerer objekter med adfærd, objektorienterede objekter.

JavaScript har det, der kaldes prototypesystemet, der tillader deling af adfærd mellem objekter. Hovedideen er at oprette et objekt kaldet prototypen med en fælles opførsel og derefter bruge det til oprettelse af nye objekter.

Prototypesystemet giver os mulighed for at skabe objekter, der arver adfærd fra andre objekter.

Lad os oprette et prototypeobjekt, der giver os mulighed for at tilføje produkter og få den samlede pris fra en indkøbskurv.

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

Bemærk, at ejendommens værdi denne gang addProducter en funktion. Vi kan også skrive det forrige objekt ved hjælp af en kortere form kaldet stenografi metode syntaks.

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

Det cartPrototypeer prototypeobjektet, der holder den fælles opførsel repræsenteret af to metoder, addProductog getTotalPrice. Det kan bruges til at opbygge andre objekter, der arver denne adfærd.

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

Det cartobjekt har cartPrototypesom sin prototype. Det arver adfærden fra det. carthar en skjult egenskab, der peger på prototype-objektet.

Når vi bruger en metode på et objekt, søges denne metode først på selve objektet snarere end på dets prototype.

dette

Bemærk, at vi bruger et specielt nøgleord kaldet for thisat få adgang til og ændre dataene på objektet.

Husk, at funktioner er uafhængige adfærdsenheder i JavaScript. De er ikke nødvendigvis en del af et objekt. Når de er, skal vi have en reference, der giver funktionen adgang til andre medlemmer på det samme objekt. thiser funktionskonteksten. Det giver adgang til andre ejendomme.

Data

Du undrer dig måske over, hvorfor vi ikke har defineret og initialiseret productsejendommen på selve prototypen.

Vi burde ikke gøre det. Prototyper skal bruges til at dele adfærd, ikke data. Deling af data fører til at have de samme produkter på flere vognobjekter. Overvej koden nedenfor:

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

Både cart1og cart2objekter, der arver den fælles opførsel fra cartPrototype, deler også de samme data. Det ønsker vi ikke. Prototyper skal bruges til at dele adfærd, ikke data.

Klasse

Prototypesystemet er ikke en almindelig måde at bygge objekter på. Udviklere er mere fortrolige med at bygge genstande uden for klasser.

Klassesyntaksen tillader en mere velkendt måde at skabe objekter, der deler en fælles opførsel. Det skaber stadig den samme prototype bag scenen, men syntaksen er klarere, og vi undgår også det tidligere datarelaterede problem. Klassen tilbyder et specifikt sted at definere de data, der er forskellige for hvert objekt.

Her er det samme objekt oprettet ved hjælp af klassen sukkersyntaks:

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.

Den anden mulighed er at definere objekter er samlinger af lukninger.

For mere om lukninger og funktionsprogrammeringsteknikker, se min bogserie Funktionel programmering med JavaScript og React.

Den Funktionel programmering i JavaScript bog er på vej ud.