Klassen `Object` i JavaScript
Den här artikeln förklarar klassen Object i JavaScript.
Den här artikeln förklarar klassen Object i JavaScript, inklusive praktiska exempel.
YouTube Video
Klassen Object i JavaScript
Object är ett inbyggt objekt som fungerar som basen för alla JavaScript-objekt. Många av språkets kärnfunktioner, såsom egenskapshantering, arv (prototypkedja), uppräkning, kloning och frysning, tillhandahålls genom Object-beteendet.
Object.create
Det finns flera sätt att skapa objekt, och du bör använda dem på rätt sätt beroende på ditt syfte.
Objektliteral (det vanligaste sättet)
Följande kod visar det enklaste och mest lättlästa sättet att skapa ett objekt.
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 det här exemplet definieras egenskaper och metoder med hjälp av litteraler. Det är enkelt och ger vanligtvis överlägsen prestanda.
Konstruktorn new Object()
Konstruktorn Object används sällan, men det är bra att förstå dess beteende.
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()returnerar ett tomt objekt, men literalet{}är kortare och vanligare.
Att specificera prototypen med Object.create
Object.create används för att skapa ett objekt med en specificerad prototyp.
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är idealisk för arvbaserad objektdesign och gör att du kan kontrollera prototypkedjan exakt.
Egenskapsattribut och deskriptorer
Egenskaper har attribut som 'value', 'writable', 'enumerable' och 'configurable', vilka kan detaljstyras med Object.defineProperty.
Grundläggande exempel på att använda defineProperty
Härnäst följer ett exempel på hur man definierar icke-enumererbara och skrivskyddade egenskaper med 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
- Med
definePropertykan du exakt styra egenskapers beteende såsom enumerering, överskrivning och borttagning.
Åtkomstegenskaper (getter / setter)
Med accessorer kan du införa logik vid läsning och skrivning 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
getterochsetterkan du behandla egenskapsåtkomst som ett externt API och lägga till validering eller bieffekter.
Prototyp och arv (prototype / __proto__ / Object.getPrototypeOf)
Arv i JavaScript baseras på prototypkedjan, inte på klasser. Objekt kan referera till andra objekt som sina prototyper.
Object.getPrototypeOf och Object.setPrototypeOf
Följande exempel visar hur man inspekterar och sätter 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.getPrototypeOfhämtar prototypen för ett objekt.Object.setPrototypeOfändrar prototypen för ett existerande objekt, men bör användas med försiktighet då det kan påverka prestandan.
Viktiga inbyggda metoder
Vi kommer tydligt att förklara de mest använda och viktiga metoderna som valts från instansmetoderna som tillhandahålls av Object.prototype, samt de statiska metoder som ägs av Object.
hasOwnProperty, isPrototypeOf, toString, valueOf
hasOwnProperty, isPrototypeOf, toString och valueOf definierar objektens grundläggande beteende.
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är en viktig metod för att kontrollera om en egenskap finns direkt på objektet.isPrototypeOfkontrollerar om målobjektet har det som prototyp.
Object.keys, Object.values, Object.entries
Object.keys, Object.values och Object.entries returnerar listor med objektets egna uppräkningsbara egenskaper. De är användbara för iteration och transformation.
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));- Dessa används ofta för att iterera över och transformera objekt.
Object.assign
Object.assign används för ytlig kopiering och sammanslagning. Observera att prototyper och åtkomstegenskaper inte kopieras.
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)
- För nästlade objekt kopieras endast referenserna, så du behöver en annan implementering för djup kloning.
Object.freeze, Object.seal, Object.preventExtensions
Object.freeze, Object.seal och Object.preventExtensions styr objektens förändringsbarhet.
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är striktast; det förhindrar alla ändringar av objektets egenskaper.sealförhindrar att egenskaper läggs till eller tas bort, men tillåter ändringar av befintliga egenskapers värden.preventExtensionsförhindrar endast tillägg av nya egenskaper; befintliga egenskaper kan fortfarande ändras eller tas bort.
Objektuppräkning, ordning och for...in / for...of
for...in räknar upp uppräkningsbara egenskapsnamn, men inkluderar också egenskaper från prototypkedjan, så den används ofta ihop med hasOwnProperty. Att kombinera Object.keys() med for...of är säkrare och gör dina avsikter tydligare.
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}- Reglerna för egenskapsuppräkning definieras i ECMAScript-specifikationen, och ibland är uppräkningsordningen garanterad, ibland inte. Som en allmän regel ordnas nycklar som tolkas som siffror i stigande ordning, medan andra nycklar följer insättningsordningen.
Kloning och djupkopiering
Det finns två typer av kopiering av objekt: ytlig kopiering med Object.assign eller spridningssyntaxen, och djup kopiering med rekursiva metoder. Det är viktigt att använda dem på rätt sätt beroende på situationen.
Ytlig kopiering (spridningssyntax / 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
- Vid ytlig kopiering delar nästlade objekt referenser, så ändringar i originalobjektet kan påverka kopian.
Enkel djupkopiering (med förbehåll)
Att använda JSON-tricket är ett snabbt sätt att skapa en djupkopia, men det har nackdelar som att funktioner, Date, cirkulära referenser och undefined-värden försvinner. För äkta djupkloning behöver du använda ett särskilt 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
- JSON-baserade metoder är praktiska för att snabbt hantera enkel data, men i vanliga fall kan deras beteende orsaka problem.
Mixins och objektkomposition
Istället för multipelt arv används ofta mönstret att sätta ihop beteenden med 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 är flexibla, men man måste vara försiktig med egenskapsnamnskollisioner och testbarhet.
Vanliga fallgropar och bästa praxis
Här är några vanliga fallgropar och bästa praxis.
-
Föränderlighet Objekt är föränderliga som standard. I applikationer med tillståndshantering bör du överväga oföränderliga datastrukturer med
Object.freezeeller ett bibliotek för immutabilitet. -
Prototypförorening Att slå ihop extern data direkt till ett objekt med
Object.assigneller loopar kan orsaka oväntade bieffekter med speciella egenskaper som__proto__ellerconstructor, vilket skapar säkerhetsrisker. Filtrera användarinmatning innan du slår ihop den direkt. -
Fallgropar med
for...infor...inräknar även upp prototyps-egenskaper, så kontrollera medhasOwnProperty. Att användaObject.keysär tydligare. -
Missbruk av ytlig kopiering Överväg om du behöver djupkopiering för att förhindra att ändringar i nästlade objekt påverkar originalet.
Praktiskt exempel: Mönster för oföränderlig objektsuppdatering
Mönster som returnerar ett nytt objekt utan att direkt ändra tillståndet används ofta i React och liknande bibliotek.
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
- Den här koden är ett exempel på att skapa ett nytt tillståndsobjekt utan att direkt ändra det ursprungliga
state-objektet.toggleTodo-funktionen kopierartodos-arrayen och returnerar ett nytt objekt med endast det aktuella elementet ändrat, så det ursprungligastateförblir oförändrat. - Oföränderliga uppdateringar minskar bieffekter och gör tillståndshanteringen enklare.
Praktiskt exempel: Säker sammanslagning (Var försiktig med prototypförorening)
Vid sammanslagning av extern JSON, ignorera __proto__ för att undvika prototypförorening.
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
- Den här typen av skydd är också viktigt i bibliotek och ramverk.
Prestandaöverväganden
Vad gäller prestanda, tänk på följande punkter:.
- Undvik frekventa prototypändringar (
Object.setPrototypeOf) eller dynamiska tillägg/borttagningar av egenskaper, eftersom dessa förhindrar motoroptimeringar. - När du skapar många små objekt blir optimering mer effektiv om du använder objekt med en enhetlig struktur (samma uppsättning egenskaper).
- Djupkopiering är kostsamt. Minimera användningen eller överväg diff-baserade uppdateringar.
Sammanfattning
Object är centralt i JavaScript och möjliggör funktioner som objektskapande, egenskapsstyrning, arv, kopiering och hantering av föränderlighet. Det är viktigt att förstå API:er som Object.defineProperty, Object.assign och Object.freeze, och att designa noggrant för att undvika fallgropar som prototypförorening och ytlig kopiering.
Du kan följa med i artikeln ovan med hjälp av Visual Studio Code på vår YouTube-kanal. Vänligen kolla även in YouTube-kanalen.