Klassen `Object` i JavaScript

Klassen `Object` i JavaScript

Denne artikkelen forklarer klassen Object i JavaScript.

Denne artikkelen forklarer klassen Object i JavaScript, inkludert praktiske eksempler.

YouTube Video

Klassen Object i JavaScript

Object er et innebygd objekt som fungerer som grunnlaget for alle JavaScript-objekter. Mange av språkets kjernefunksjoner, som egenskapsstyring, arv (prototypekjede), enumerasjon, kloning og frysing, tilbys gjennom oppførselen til Object.

Object.create

Det finnes flere måter å opprette objekter på, og du bør bruke dem riktig avhengig av formålet ditt.

Objekt-litteral (mest vanlig)

Koden nedenfor viser den enkleste og mest lesbare måten å opprette et objekt på.

 1// Create an object using object literal
 2const user = {
 3  name: "Alice",
 4  age: 30,
 5  greet() {
 6    return `Hello, I'm ${this.name}`;
 7  }
 8};
 9
10console.log(user.greet()); // "Hello, I'm Alice"
  • I dette eksemplet defineres egenskaper og metoder ved hjelp av bokstavelige verdier. Den er enkel og gir vanligvis overlegen ytelse.

new Object()-konstruktøren

Object-konstruktøren brukes sjelden, men det er nyttig å forstå hvordan den oppfører seg.

1// Create an object using the Object constructor
2const objFromCtor = new Object();
3objFromCtor.x = 10;
4objFromCtor.y = 20;
5
6console.log(objFromCtor); // { x: 10, y: 20 }
  • new Object() returnerer et tomt objekt, men litteralet {} er kortere og mer vanlig.

Spesifisere prototype med Object.create

Object.create brukes til å lage et objekt med en spesifisert prototype.

1// Create an object with a specified prototype
2const proto = { hello() { return "hi"; } };
3const obj = Object.create(proto);
4obj.name = "Bob";
5
6console.log(obj.hello()); // "hi"
7console.log(Object.getPrototypeOf(obj) === proto); // true
  • Object.create er ideell for arvebasert objekt-design, fordi du kan kontrollere prototypekjeden nøyaktig.

Egenskapsattributter og deskriptorer

Egenskaper har attributter som 'value', 'writable', 'enumerable' og 'configurable', som kan kontrolleres i detalj med Object.defineProperty.

Grunnleggende eksempel på bruk av defineProperty

Neste er et eksempel på å definere ikke-itererbare og skrivebeskyttede egenskaper ved å bruke defineProperty.

 1// Define a non-enumerable read-only property
 2const person = { name: "Carol" };
 3
 4Object.defineProperty(person, "id", {
 5  value: 12345,
 6  writable: false,
 7  enumerable: false,
 8  configurable: false
 9});
10
11console.log(person.id); // 12345
12console.log(Object.keys(person)); // ["name"] — "id" is non-enumerable
13person.id = 999; // silently fails or throws in strict mode
14console.log(person.id); // still 12345
  • Ved å bruke defineProperty kan du presist kontrollere egenskapsadferd som enumerasjon, omskriving og sletting.

Aksessoregenskaper (getter / setter)

Med aksessorer kan du legge til logikk ved lesing og skriving av egenskaper.

 1// Use getter and setter to manage internal state
 2const data = {
 3  _value: 1,
 4  get value() {
 5    return this._value;
 6  },
 7  set value(v) {
 8    if (typeof v === "number" && v > 0) {
 9      this._value = v;
10    } else {
11      throw new Error("value must be a positive number");
12    }
13  }
14};
15
16console.log(data.value); // 1
17data.value = 5;
18console.log(data.value); // 5
19// data.value = -1; // would throw
  • Med getter og setter kan du behandle egenskapstilgang som et eksternt API og legge til validering eller bieffekter.

Prototype og arv (prototype / __proto__ / Object.getPrototypeOf)

Arv i JavaScript er basert på prototypekjeden, ikke på klasser. Objekter kan referere til andre objekter som sine prototyper.

Object.getPrototypeOf og Object.setPrototypeOf

Følgende eksempel viser hvordan man kan inspisere og sette prototyper.

1// Inspect and change prototype
2const base = { speak() { return "base"; } };
3const derived = Object.create(base);
4console.log(Object.getPrototypeOf(derived) === base); // true
5
6const other = { speak() { return "other"; } };
7Object.setPrototypeOf(derived, other);
8console.log(derived.speak()); // "other"
  • Object.getPrototypeOf henter prototypen til et objekt.
  • Object.setPrototypeOf endrer prototypen til et eksisterende objekt, men det bør brukes med forsiktighet, fordi det kan påvirke ytelsen.

Viktige innebygde metoder

Vi vil tydelig forklare de mest brukte og viktige metodene valgt fra instansmetodene gitt av Object.prototype, samt de statiske metodene eid av Object.

hasOwnProperty, isPrototypeOf, toString, valueOf

hasOwnProperty, isPrototypeOf, toString og valueOf definerer objektets grunnleggende oppførsel.

 1// Demonstrate prototype methods
 2const base = { greet() { return "hello"; } };
 3const child = Object.create(base);
 4const a = { x: 1 };
 5
 6console.log(a.hasOwnProperty("x")); // true
 7console.log(a.hasOwnProperty("toString")); // false — toString is inherited
 8
 9console.log(a.toString()); // "[object Object]" by default
10
11console.log(base.isPrototypeOf(child)); // true
12console.log(Object.prototype.isPrototypeOf(child)); // true
  • hasOwnProperty er en essensiell metode for å sjekke om en egenskap er direkte på objektet.
  • isPrototypeOf sjekker om målobjektet har seg selv som prototype.

Object.keys, Object.values, Object.entries

Object.keys, Object.values og Object.entries returnerer lister over objektets egne enumerebare egenskaper. Disse er nyttige for iterasjon og transformasjon.

 1// Keys, values and entries
 2const item = { id: 1, name: "Widget", price: 9.99 };
 3
 4// ["id", "name", "price"]
 5console.log(Object.keys(item));
 6
 7// [1, "Widget", 9.99]
 8console.log(Object.values(item));
 9
10// [["id",1], ["name","Widget"], ["price",9.99]]
11console.log(Object.entries(item));
  • Disse brukes ofte for å iterere og transformere objekter.

Object.assign

Object.assign brukes for grunn kopiering og sammenslåing. Vær oppmerksom på at prototyper og aksessoregenskaper ikke kopieres.

1// Shallow copy / merge using Object.assign
2const target = { a: 1 };
3const source = { b: 2 };
4const result = Object.assign(target, source);
5
6console.log(result); // { a: 1, b: 2 }
7console.log(target === result); // true (merged into target)
  • For nestede objekter kopieres kun referansene, så du trenger en annen implementasjon for dyp kloning.

Object.freeze, Object.seal, Object.preventExtensions

Object.freeze, Object.seal og Object.preventExtensions styrer hvor endrbare objekter er.

 1// Freeze vs seal vs preventExtensions
 2const obj = { a: 1 };
 3Object.freeze(obj);
 4obj.a = 2; // fails silently or throws in strict mode
 5delete obj.a; // fails
 6
 7const obj2 = { b: 2 };
 8Object.seal(obj2);
 9obj2.b = 3; // allowed
10// delete obj2.b; // fails
11
12const obj3 = { c: 3 };
13Object.preventExtensions(obj3);
14obj3.d = 4; // fails
  • freeze er den strengeste; den hindrer alle endringer i objektets egenskaper.
  • seal forhindrer å legge til eller slette egenskaper, men lar deg endre verdiene til eksisterende egenskaper.
  • preventExtensions hindrer kun at nye egenskaper legges til; eksisterende egenskaper kan fortsatt endres eller slettes.

Objekts enumering, rekkefølge, og for...in / for...of

for...in enumererer navnene på enumerebare egenskaper, men inkluderer også egenskaper i prototypekjeden, så det brukes ofte sammen med hasOwnProperty. Å kombinere Object.keys() med for...of er tryggere og gjør hensikten din tydeligere.

 1// Safe enumeration
 2const obj = Object.create({ inherited: true });
 3obj.own = 1;
 4
 5for (const key in obj) {
 6  if (obj.hasOwnProperty(key)) {
 7    console.log("own prop:", key);
 8  } else {
 9    console.log("inherited prop:", key);
10  }
11}
12
13for (const key of Object.keys(obj)) {
14  console.log("key via Object.keys:", key);
15}
  • Reglene for egenskapens enumerering er definert i ECMAScript-spesifikasjonen, og det finnes tilfeller hvor enumeringen har fast rekkefølge og andre der den ikke har det. Som en generell regel blir nøkler tolket som tall sortert i stigende rekkefølge, mens andre nøkler følger innsettingsrekkefølgen.

Kloning og dyp kopiering

Det finnes to typer kopiering av objekter: grunn kopiering ved bruk av Object.assign eller spredningssyntaksen, og dyp kopiering ved bruk av rekursive metoder. Det er viktig å bruke dem riktig avhengig av situasjonen.

Grunn kopiering (spread-syntaks / Object.assign)

1// Shallow copy with spread operator
2const original = { a: 1, nested: { x: 10 } };
3const shallow = { ...original };
4shallow.nested.x = 99;
5console.log(original.nested.x); // 99 — nested object is shared
  • Ved grunn kopiering deler nestede objekter referanser, så endringer i originalobjektet kan påvirke kopien.

Enkel dyp kopiering (med forbehold)

Å bruke JSON-trikset er en rask måte å lage en dyp kopi på, men det har ulemper, slik som tap av funksjoner, Date-objekter, sirkulære referanser og undefined-verdier. For ekte dyp kloning må du bruke et dedikert bibliotek.

1// Deep clone using JSON methods — limited use-cases only
2const source = { a: 1, d: new Date(), nested: { x: 2 } };
3const cloned = JSON.parse(JSON.stringify(source));
4console.log(cloned); // ok for plain data, but Date becomes string, functions lost
  • Metoder basert på JSON er praktiske for rask håndtering av enkel data, men i vanlige tilfeller kan oppførselen deres svikte.

Mixins og objektsammensetning

I stedet for multippel arv brukes ofte mønsteret med å sette sammen oppførsel ved hjelp av mixins.

 1// Simple mixin function
 2const canEat = {
 3  eat() { return `${this.name} eats`; }
 4};
 5const canWalk = {
 6  walk() { return `${this.name} walks`; }
 7};
 8
 9function createPerson(name) {
10  const person = { name };
11  return Object.assign(person, canEat, canWalk);
12}
13
14const p = createPerson("Dana");
15console.log(p.eat()); // "Dana eats"
16console.log(p.walk()); // "Dana walks"
  • Mixins er fleksible, men du må være forsiktig med navnekollisjoner på egenskaper og testbarhet.

Vanlige fallgruver og beste praksis

Her er noen vanlige fallgruver og beste praksis.

  • Mutabilitet Objekter er muterbare som standard. I applikasjoner med tilstandshåndtering bør du vurdere uforanderlige datastrukturer ved å bruke Object.freeze eller et uforanderlig bibliotek.

  • Prototype-forurensing Hvis du slår sammen ekstern data direkte inn i et objekt med Object.assign eller løkker, kan det gi uventede bieffekter med spesielle egenskaper som __proto__ eller constructor, og skape sikkerhetsrisikoer. Filtrer brukerinput før du slår det sammen direkte.

  • Fallgruver med for...in for...in enumererer også egenskapene i prototypen, så sjekk med hasOwnProperty. Det er tydeligere å bruke Object.keys.

  • Feil bruk av grunn kopiering Vurder om du trenger dyp kopiering for å unngå at endringer i nestede objekter påvirker originalobjektet.

Praktisk eksempel: Immutable oppdateringsmønster for objekt

Mønstre som returnerer et nytt objekt uten å endre tilstanden direkte, brukes ofte i React og lignende biblioteker.

 1// Immutable update example
 2const state = { todos: [{ id: 1, text: "Buy milk", done: false }] };
 3
 4// Toggle todo done immutably
 5function toggleTodo(state, todoId) {
 6  return {
 7    ...state,
 8    todos: state.todos.map(t => t.id === todoId ? { ...t, done: !t.done } : t)
 9  };
10}
11
12const newState = toggleTodo(state, 1);
13console.log(state.todos[0].done); // false
14console.log(newState.todos[0].done); // true
  • Denne koden er et eksempel på å opprette et nytt tilstandsobjekt uten å endre det opprinnelige state-objektet direkte. toggleTodo-funksjonen kopierer todos-arrayet og returnerer et nytt objekt der kun målelementet er modifisert, slik at det opprinnelige state forblir uendret.
  • Immutable oppdateringer reduserer bieffekter og gjør det enklere å håndtere tilstand.

Praktisk eksempel: Sikker sammenslåing (vær forsiktig med prototype-forurensing)

Når du slår sammen ekstern JSON, overse __proto__ for å hindre prototype-forurensing.

 1// Safe merge ignoring __proto__ keys
 2function safeMerge(target, source) {
 3  for (const key of Object.keys(source)) {
 4    if (key === "__proto__" || key === "constructor") continue;
 5    target[key] = source[key];
 6  }
 7  return target;
 8}
 9
10const target = {};
11const source = JSON.parse('{"a":1,"__proto__":{"polluted":true}}');
12safeMerge(target, source);
13console.log(target.polluted); // undefined — safe
14console.log({}.polluted); // undefined — prototype not polluted
  • Denne typen beskyttelse er også viktig i biblioteker og rammeverk.

Ytelseshensyn

Når det gjelder ytelse, vurder følgende punkter:.

  • Unngå hyppige prototypeendringer (Object.setPrototypeOf) eller dynamisk tillegg/fjerning av egenskaper, da dette kan hindre optimalisering i motoren.
  • Når du genererer mange små objekter, blir optimalisering mer effektiv hvis du bruker objekter med en ensartet struktur (samme sett med egenskaper).
  • Dyp kopiering er ressurskrevende. Minimer bruken, eller vurder å bruke diff-baserte oppdateringer.

Sammendrag

Object er sentralt i JavaScript og gir en rekke funksjoner som objektopprettelse, egenskapskontroll, arv, kopiering og håndtering av mutabilitet. Det er viktig å forstå API-er som Object.defineProperty, Object.assign og Object.freeze, og å designe nøye for å unngå fallgruver som prototypeforurensning og overfladisk kopiering.

Du kan følge med på artikkelen ovenfor ved å bruke Visual Studio Code på vår YouTube-kanal. Vennligst sjekk ut YouTube-kanalen.

YouTube Video