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.createer 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
definePropertykan 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
getterogsetterkan 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.getPrototypeOfhenter prototypen til et objekt.Object.setPrototypeOfendrer 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
hasOwnPropertyer en essensiell metode for å sjekke om en egenskap er direkte på objektet.isPrototypeOfsjekker 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
freezeer den strengeste; den hindrer alle endringer i objektets egenskaper.sealforhindrer å legge til eller slette egenskaper, men lar deg endre verdiene til eksisterende egenskaper.preventExtensionshindrer 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.freezeeller et uforanderlig bibliotek. -
Prototype-forurensing Hvis du slår sammen ekstern data direkte inn i et objekt med
Object.assigneller løkker, kan det gi uventede bieffekter med spesielle egenskaper som__proto__ellerconstructor, og skape sikkerhetsrisikoer. Filtrer brukerinput før du slår det sammen direkte. -
Fallgruver med
for...infor...inenumererer også egenskapene i prototypen, så sjekk medhasOwnProperty. Det er tydeligere å brukeObject.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 kopierertodos-arrayet og returnerer et nytt objekt der kun målelementet er modifisert, slik at det opprinneligestateforblir 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.