# 物件與記憶體位置
## 物件傳參考特性
- 陣列、函式、物件、日期都屬於物件型別
- 物件型別屬於傳參考
```javascript
const person = {
name: "小明",
};
const person2 = person;
person2.name = "漂亮阿姨";
//漂亮阿姨
console.log(person.name);
```
```javascript
const person = {
name: "小明",
};
function fn(obj) {
obj.name = "漂亮阿姨";
}
fn(person);
//漂亮阿姨
console.log(person.name);
```
當 key 名稱存在於變數中時,可使用中括號取用:
```javascript
var variable = 'go';
obj[variable]();
```
### 進階觀念
- 物件中的屬性名稱本質上都是字串
- 可以加上?.可選串聯,防止存取不存在的屬性時拋錯
- 特殊字元(如 emoji)可作為 key,但需用字串形式包起來
- 可用變數作為 key,只要加上中括號 []
```javascript
const propertyName = "name";
const obj = {
[propertyName]: "小明",
姓名: "小華",
1: 1,
'❤️': "這是一個 emoji",
inner: {
姓名: "內層的物件"
}
};
console.log(obj);
console.log(obj.test?.name); // undefined,不拋錯
```
## 函式也是物件
函式屬於物件型別,因此可以使用物件的方式賦值屬性
```javascript
const fn = function () {};
const fn2 = fn;
fn2.age = 18;
console.dir(fn);
console.log(fn.age);
```
## parseInt相關補充
Number.parseInt 和全域的 parseInt 指向同一個記憶體位置
```javascript
//100
console.log(Number.parseInt("100元"));
//true
console.log(window.parseInt === Number.parseInt);
//true
console.log(parseInt("100元"));
```
將內建函數parseInt賦值給一個常數變數"轉數值"。轉數值這個變數就指向parseInt函數
```javascript
const 轉數值 = parseInt;
console.log(轉數值("100元"));
```
將 parseInt 指向 null,不影響 Number.parseInt
```javascript
parseInt = null;
console.log(Number.parseInt("100元"));
```
## 物件參考範例說明
函式內直接修改傳入物件的屬性,會影響原物件
```javascript
const family = {
name: "小明家",
Ming: {
name: "小明",
},
};
const Ming = family.Ming;
function fn(obj) {
obj.name = "漂亮阿姨";
}
fn(Ming);
//{name : '漂亮阿姨'}
console.log(Ming);
```
若函式內重新賦值一個新物件,則不影響原本的物件:
1. fn(Ming) 傳入的是 Ming 的參考(指向原本那個 { name: "小明" } 的記憶體位址)
2. 但在函數內你寫了 obj = { name: "杰倫" },這行讓 obj 改指向了一個新的物件
3. 此時 obj 跟原本的 Ming 就沒有任何關係了
4. 所以你後面改的是 obj 指向的新物件,對 Ming 沒有影響
```javascript
const family = {
name: "小明家",
Ming: {
name: "小明",
},
};
const Ming = family.Ming;
function fn(obj) {
obj = {
name: "杰倫",
};
obj.name = "漂亮阿姨";
}
fn(Ming);
console.log(Ming);
```
## 淺層拷貝
- 只複製第一層屬性
- 對於物件中的基本類型(primitive)值,會直接複製值
- 對於物件中的參考類型(object、array、function)值,只複製參考(reference)
**常見方法:**
- Object.assign({}, obj)
- 展開運算子:{ ...obj }、[ ...arr ]
```javascript
const family = {
name: "小明家",
Ming: { name: "小明" },
};
const newFamily = { ...family };
newFamily.name = "小華";
newFamily.Ming.name = "漂亮阿姨";
console.log(newFamily);
console.log(family);
```
## 深層拷貝
- 完整複製所有層級的資料,不共用記憶體位置
- 佔用較多記憶體,但避免資料意外互相影響
```javascript
const family = {
name: "小明家",
member: {
name: "小明",
},
};
const newFamily = JSON.parse(JSON.stringify(family));
newFamily.name = "小華家";
newFamily.member.name = "漂亮阿姨";
console.log(family);
console.log(newFamily);
```
## 函式參數重新賦值
**當函式內部 obj = {...},只會改變內部參數的參考,不影響原始物件:**
- 當你在函式內 obj = { name: 1 } 時,是在「重新賦值給參數變數 obj」,這個改變只發生在 fn 函式的內部作用域,不會影響外部的 person 變數所指的記憶體位址
- obj會另外創建另一個記憶體空間
```javascript
const person = {};
function fn(obj) {
obj = {
name: 1,
};
}
fn(person);
console.log(person);
```
## 補充:物件搭配陣列方法
物件可透過 Object.keys 和 Object.values 使用陣列方法:
```javascript
const family = {
Ming: { name: '小明' },
Jay: { name: '杰倫' },
Auntie: { name: '漂亮阿姨' }
};
console.log(Object.keys(family)); // ['Ming', 'Jay', 'Auntie']
console.log(Object.values(family)); // [ {...}, {...}, {...} ]
Object.values(family).forEach(item => {
console.log(item);
});
Object.keys(family).forEach(key => {
console.log(key);
console.log(family[key]);
});
```