深入理解JavaScript物件

CodeForBetter發表於2023-02-21

前言

在 JavaScript 中,物件是一種非常常見的資料型別,幾乎每個程式設計師都會在日常工作中頻繁地使用物件。在本篇文章中,我們將深入瞭解 JavaScript 物件的一些基本概念和一些高階概念,這些概念對於我們正確理解物件在 JavaScript 中的行為非常重要。

物件的基本概念

在 JavaScript 中,物件是由鍵值對組成的集合。鍵是一個字串或者 Symbol 型別的值,值可以是任何型別的資料,包括其他物件。物件是一種動態資料結構,可以透過新增、刪除或修改屬性來改變物件的狀態。以下是 JavaScript 中定義物件的一些基本語法:

const myObj = {
  key1: 'value1',
  key2: 'value2',
  key3: 'value3',
};

在這個物件中,每一個鍵都有一個相應的值。可以使用點運運算元或方括號運運算元來訪問物件的屬性:

console.log(myObj.key1); // 輸出 'value1'
console.log(myObj['key2']); // 輸出 'value2'

可以透過以下方式向物件新增新的屬性:

myObj.newKey = 'newValue';
console.log(myObj.newKey); // 輸出 'newValue'

物件也可以作為函式的引數和返回值:

function createObj() {
  return { key1: 'value1', key2: 'value2', key3: 'value3' };
}

const obj = createObj();
console.log(obj.key1); // 輸出 'value1'

物件的高階概念-原型

在 JavaScript 中,物件之間可以有一種原型關係,即一個物件可以繼承另一個物件的屬性和方法。每一個物件都有一個原型物件,它是另一個物件的引用,可以透過 Object.getPrototypeOf(obj) 來訪問。

在 JavaScript 中,有兩種方式來建立一個新物件:

  • 使用物件字面量 {} 或者 new Object() 來建立一個空物件。
  • 使用建構函式來建立一個物件。

建構函式可以使用 new 關鍵字來呼叫,它會建立一個新的物件並把這個物件的原型設定為建構函式的 prototype 屬性。
在後一種方式中,可以透過建構函式的 prototype 屬性來為新物件新增方法和屬性。新建立的物件會繼承建構函式的原型,因此可以訪問這些方法和屬性。以下是一個簡單的例子:

function MyObj() {
  this.name = 'My Object';
}

MyObj.prototype.getName = function () {
  return this.name;
};

const obj = new MyObj();
console.log(obj.getName()); // 輸出 'My Object'

this

在 JavaScript 中,this 關鍵字指的是當前正在執行的函式的上下文物件。在物件中,this 指向該物件本身。例如:

const myObj = {
  name: 'My Object',
  getName: function () {
    return this.name;
  },
};

console.log(myObj.getName()); // 輸出 'My Object'

JS物件的建立和訪問

在JS中,物件是一種複合值,包含了無數個屬性(key-value pairs),它可以是字串、數字、布林值、陣列或者其他物件等等。物件是JS中最重要的資料結構之一。

物件的建立

物件字面量

最常見的建立物件的方法是使用物件字面量。物件字面量是一個由若干鍵值對組成的物件。這種方式建立的物件可以直接使用{}來表示,也可以使用一個變數來儲存這個物件。

const person = { name: 'Alice', age: 18, gender: 'female' };

物件字面量建立的物件可以直接在定義時新增屬性,也可以後期新增。

const person = {};
person.name = 'Alice';
person.age = 18;
person.gender = 'female';

Object建構函式

除了物件字面量之外,還可以使用Object建構函式來建立物件。可以使用new關鍵字和Object建構函式建立一個空物件,或者將一個現有物件作為引數傳遞給Object建構函式來建立一個與該物件相同的新物件。

const emptyObj = new Object();
const person = new Object({ name: 'Alice', age: 18, gender: 'female' });

工廠函式

工廠函式是一個返回物件的函式。它們透過抽象化物件的建立和初始化來降低重複程式碼的量。當需要建立多個類似的物件時,可以使用工廠函式來代替每個物件都寫一遍相同的程式碼。

function createPerson(name, age, gender) {
  return { name, age, gender };
}

const person1 = createPerson('Alice', 18, 'female');
const person2 = createPerson('Bob', 20, 'male');

建構函式

建構函式是一種特殊的函式,用於建立並初始化一個由該型別定義的物件。當我們使用new關鍵字建立物件時,實際上是在呼叫建構函式。

function Person(name, age, gender) {
  this.name = name;
  this.age = age;
  this.gender = gender;
}

const person1 = new Person('Alice', 18, 'female');
const person2 = new Person('Bob', 20, 'male');

class

ES6引入了class語法,讓JS的物件導向程式設計更加直觀和易於理解。使用class建立物件的語法如下:

class Person {
  constructor(name, age, gender) {
    this.name = name;
    this.age = age;
    this.gender = gender;
  }
}

const person1 = new Person('Alice', 18, 'female');
const person2 = new Person('Bob', 20, 'male');

訪問物件的屬性

訪問 JS 物件的屬性和方法通常有兩種方式:點語法和方括號語法。
使用點語法,可以透過物件名稱後面跟隨一個句點,然後是屬性名,來訪問物件的屬性和方法,例如:

const obj = {
  name: 'Alice',
  age: 25,
  sayHello() {
    console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);
  }
};

console.log(obj.name); // 輸出 'Alice'
console.log(obj.age); // 輸出 25
obj.sayHello(); // 輸出 'Hello, my name is Alice and I am 25 years old.'

使用方括號語法,可以透過物件名稱後面跟隨一個方括號,裡面是屬性名或者變數名,來訪問物件的屬性和方法,例如:

const obj = {
  name: 'Alice',
  age: 25,
  sayHello() {
    console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);
  }
};

console.log(obj['name']); // 輸出 'Alice'
console.log(obj['age']); // 輸出 25
obj['sayHello'](); // 輸出 'Hello, my name is Alice and I am 25 years old.'

使用方括號語法時,可以在方括號中使用表示式,例如:

const obj = {
  name: 'Alice',
  age: 25,
  sayHello() {
    console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);
  }
};

const propName = 'name';
console.log(obj[propName]); // 輸出 'Alice'

const method = 'sayHello';
obj[method](); // 輸出 'Hello, my name is Alice and I am 25 years old.'

需要注意的是,在方括號語法中,屬性名或者變數名必須用引號括起來,否則會被解釋為變數名。另外,在使用方括號語法訪問物件的屬性和方法時,可以使用變數名,這個變數名可以在執行時決定。

總的來說,訪問 JS 物件的屬性和方法是非常簡單的,但是需要注意使用點語法和方括號語法的區別,以及在使用方括號語法時需要注意屬性名或者變數名的引號問題。

物件的遍歷

遍歷物件是很常見的操作。物件的遍歷可以使用 for-in 迴圈,也可以使用 Object.keys() 方法遍歷物件的鍵名。

const obj = { name: 'Tom', age: 18, gender: 'male' };

// 使用 for-in 迴圈遍歷物件
for (let key in obj) {
  console.log(key, obj[key]);
}

// 使用 Object.keys() 方法遍歷物件的鍵名
Object.keys(obj).forEach(key => {
  console.log(key, obj[key]);
});

物件常用的三個方法

Object.keys(), Object.values(), 和 Object.entries() 都是用於操作物件的方法。

  • Object.keys(obj):返回一個由給定物件的所有可列舉自身屬性的屬性名組成的陣列。
  • Object.values(obj):返回一個給定物件的所有可列舉自身屬性的屬性值組成的陣列。
  • Object.entries(obj):返回一個給定物件自身可列舉屬性的鍵值對陣列。

下面我們舉個例子來說明這三個方法的使用:

const person = {
  firstName: 'John',
  lastName: 'Doe',
  age: 30,
  email: 'john.doe@example.com'
};

// Object.keys()
const keys = Object.keys(person);
console.log(keys); // ['firstName', 'lastName', 'age', 'email']

// Object.values()
const values = Object.values(person);
console.log(values); // ['John', 'Doe', 30, 'john.doe@example.com']

// Object.entries()
const entries = Object.entries(person);
console.log(entries); // [['firstName', 'John'], ['lastName', 'Doe'], ['age', 30], ['email', 'john.doe@example.com']]

透過這三個方法,我們可以方便地操作物件,快速獲取物件的屬性、屬性值以及屬性和屬性值的鍵值對。
小技巧可以透過Object.entries()方便的將物件轉為Map型別。

物件的複製

在 JavaScript 中,物件的賦值是淺複製,即只會複製物件的引用。如果需要實現物件的深複製,則需要使用一些特殊的方法。

淺複製

淺複製只是將物件的引用複製給了另一個物件,因此在修改原物件時,複製物件也會發生變化。

const obj1 = { name: 'Tom', age: 18 };
const obj2 = obj1;

obj1.age = 20;

console.log(obj1); // { name: 'Tom', age: 20 }
console.log(obj2); // { name: 'Tom', age: 20 }

Object.assign()

該方法可以將多個物件合併為一個物件,實現淺複製。

const obj1 = { name: 'Tom', age: 18 };
const obj2 = { gender: 'male' };
const obj3 = Object.assign({}, obj1, obj2);

console.log(obj3); // { name: 'Tom', age: 18, gender: 'male' }

深複製

使用深複製建立的物件,對其進行操作不會影響原有物件。實現物件的深複製需要使用一些特殊的方法。常見的深複製方法有 JSON.parse(JSON.stringify()) 和遞迴複製兩種方法。

JSON.parse(JSON.stringify())

JSON.parse(JSON.stringify()) 方法可以實現深複製,但是隻能處理物件中的原始型別,不能處理函式、正規表示式等型別。

const obj1 = { name: 'Tom', age: 18, hobbies: ['reading', 'music'] };
const obj2 = JSON.parse(JSON.stringify(obj1));

obj1.hobbies.push('travel');

console.log(obj1); // { name: 'Tom', age: 18, hobbies: [ 'reading', 'music', 'travel' ] }
console.log(obj2); // { name: 'Tom', age: 18, hobbies: [ 'reading', 'music' ] }

遞迴複製

遞迴複製可以處理任何型別的資料,包括函式、正規表示式等。在複製物件時,需要遞迴遍歷物件的屬性,並將屬性值進行複製。

function deepClone(obj) {
  // 判斷是否為物件或陣列
  if (typeof obj !== 'object' || obj === null) {
    return obj;
  }

  // 判斷是陣列還是物件
  const newObj = Array.isArray(obj) ? [] : {};

  // 遍歷物件或陣列
  for (let key in obj) {
    if (Object.prototype.hasOwnProperty.call(obj, key)) {
      newObj[key] = deepCopy(obj[key]);
    }
  }

還可用第三方庫Lodash實現深淺複製。

總結

在 JavaScript 中,物件是由鍵值對組成的集合,每個鍵值對就是物件的一個屬性,屬性名是鍵,屬性值可以是任何資料型別,包括基本型別、物件型別和函式型別等。物件可以透過字面量、建構函式以及 Object.create() 方法建立,也可以透過 Object.defineProperty() 方法定義屬性的 getter 和 setter。

JavaScript 的物件具有動態性,可以隨時新增或刪除屬性,也可以改變屬性的值。透過使用“點”或“中括號”語法可以訪問和修改物件的屬性值。此外,可以使用 Object.keys()、Object.values()、Object.entries() 等方法來遍歷物件的屬性,也可以使用 for...in 和 for...of 迴圈遍歷物件屬性。

相關文章