0%

JS 複製 By reference 與 By value 的不同

在實作時常會聽到 call by reference 還是 call by value,這兩個有什麼不同?最簡易的解釋方法是:
call by reference : 呼叫變數的記憶體位置
call by value : 呼叫變數的值

事實上除了這兩種,還有一種叫 call by Sharing,在傳遞參數時會用到,之後再來介紹。

JavaScript 的資料型別分成原始型別(primitive values)和物件型別(Object),詳細型別可看此篇:JavaScript 基礎 型別篇

Object(物件): Object, Array

Objet 和 Array 在 Javascript 屬於複合型(composite)或參考型(reference)的原始資料類型,
在呼叫、複製或傳參數的時候,是參考記憶體的位置而不是值,這點要特別注意。

複製資料的時候

複製資料的時候 是用 By valur 或是 By reference 呢?

By valur

1
2
3
4
5
6
var a = 'something';
var b = a; // 這時 b 應該是 something
b = 'somebody'; // 再次將其他值賦予給 b

console.log(a); // something
console.log(b); // somebody

a 和 b 如期的印出我們賦予的值。我們可成功無誤的修改 b 的值。

Primitive type(基本型別):Number, String, Boolean, Null, Undefined

By reference(參考值)

1
2
3
4
5
6
7
8
9
10
const person = {
name: 'Tracy',
city: 'Tainan'
};

const person2 = person;
person2.name = 'Philippe';

console.log(person); // name: "Philippe"
console.log(person2); // name: "Philippe"

上面的範例在修改 person2 這個 object 裡的 name 屬性時,也會同時改到原來參考的 person 的 name 屬性,是不是很恐怖?

1
2
3
4
5
6
7
8
9
10
11
12
13
// 陣列
let arr1 = [1, 2, 3];
let arr2 = arr1;
arr2[3] = 4;

console.log(arr1); // [1, 2, 3, 4]
console.log(arr2); // [1, 2, 3, 4]

// 防止傳參考,以解構式先淺拷貝
let arr3 = [...arr1]
arr3.push(42)
console.log(arr1); // [1, 2, 3, 4]
console.log(arr3); // [1, 2, 3, 4, 42]

因為 Array 也是參考型(reference)的原始資料類型所以也會像 Object 一樣,在修改時也會更改到原始參考值 by reference。

如何複製一個物件而不要更動到參考值?以下是方法:

1
2
3
let person = {
name: "Jacques";
}

方法一 使用 assign:先給一個空{} 來裝複製過來的物件

1
let person2 = Object.saaign({}, person);

方法二 使用 JSON 文字化

1
2
3
4
5
6
var person2 = JSON.parse(JSON.stringify(person));
// 這時兩筆資料已無關連
person2.name = 'Gisèle';

console.log(person); // {name:"Jacques"}
console.log(person2); // {name:"Gisèle"}

JSON.stringify(); 是文字化、JSON.parse(); 是解析文字,兩函式處理下來等於複製了一份 person 到 person2

使用 Object.assign()處理 object 的缺點

如果遇上多層結構的 Object,就無法使用 assign()來處理,因為它只能處理一層的結構而無法處理兩層,也就是說這種方法只能用在 Array 上。在第二層裡的原始資料仍然會被更動到。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
var person = {
name: 'Ayda',
data: {
id: 24,
city: 'Paris',
job: 'artist'
}
};

var person2 = Object.assign({}, person);
person2.data.id = 42;

console.log(person); // 42
console.log(person2); // 42

// 改成用第二種方法

var person3 = JSON.parse(JSON.stringify(person));
person3.data.id = 66;

console.log(person); // 42,原本 24 被Object.assign()改成 42
console.log(person3); // 66

function 也是一種特殊的 Object 資料型別。
參考:小龜前輩:參數傳遞方式