JS30 day14 Reference vs Copy

Posted by Anthony Chao on 2019-11-14

JS30 day14 - Reference vs Copy

作業內容

今天主要是在釐清 JS 裡面的基本型別 pass by value 跟 pass by reference 還有怎麼應對

學到什麼

  • JS

    1. JS 裡面的數字 / 布林值 / 字串是 pass by value,但陣列跟 Object 是 pass by reference
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    // 數字 字串 布林值
    let age = 100;
    let age2 = age;
    console.log(age, age2); // 100 100
    age = 200;
    console.log(age, age2); // 200 100

    // 陣列
    const players = ['Wes', 'Sarah', 'Ryan', 'Poppy'];
    const team = players
    console.log(players, team) // (4) ["Wes", "Sarah", "Ryan", "Poppy"] (4) ["Wes", "Sarah", "Ryan", "Poppy"]
    team[3] = 'Lux'
    console.log(players, team) //(4) ["Wes", "Sarah", "Ryan", "Lux"] (4) ["Wes", "Sarah", "Ryan", "Lux"]
    1. 陣列的 copy 方法
    1
    2
    3
    4
    const team2 = players.slice()
    const team3 = [].concat(players)
    const team4 = [...players] //ES6
    const team5 = Array.from(players)
    1. Object 是最麻煩的,如果只有一層你可以使用 assign
    1
    2
    3
    4
    5
    6
    const person = {
    name: 'Wes Bos',
    age: 80
    };
    const cap2 = Object.assign({}, person, { number: 99, age: 12 })
    console.log(person, cap2) //{name: "Wes Bos", age: 99} {name: "Wes Bos", age: 12, number: 99}

    問題是 assgin 是淺拷貝,兩層以上會出現一樣的問題 (WTF moment)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    const wes ={
    name: 'Wes',
    age: 100,
    social: {
    twitter: 'wesbos',
    facebook: 'developer'
    }
    }
    const dev = Object.assign({}, wes)
    dev.social.twitter = "qoo"
    console.log(wes, dev)

    結果如下:

    他這裡有介紹一種深拷貝的方法,但效能不怎麼好:

    1
    const dev2 = JSON.parse(JSON.stringify(wes));

    這篇文章寫得很詳細可以參考:
    Object深拷貝的方法

參考資料:
https://github.com/wesbos/JavaScript30

code 內容:

JS:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
// 對於字串 / 數字 / 布林值來說是 pass by value
let age = 100;
let age2 = age;
console.log(age, age2); // 100 100
age = 200;
console.log(age, age2); // 200 100

// 對於陣列來說是 pass by reference
const players = ['Wes', 'Sarah', 'Ryan', 'Poppy'];
const team = players
console.log(players, team) // (4) ["Wes", "Sarah", "Ryan", "Poppy"] (4) ["Wes", "Sarah", "Ryan", "Poppy"]
team[3] = 'Lux'
console.log(players, team) //(4) ["Wes", "Sarah", "Ryan", "Lux"] (4) ["Wes", "Sarah", "Ryan", "Lux"]

// 解決方法:複製一份
const team2 = players.slice()
const team3 = [].concat(players)

// ES6 方法:打散
const team4 = [...players]
const team5 = Array.from(players)
// The same thing goes for objects, let's say we have a person object

// 對於 Object 來說也是 pass by reference
const person = {
name: 'Wes Bos',
age: 80
};
const captain = person
console.log(person, captain) // {name: "Wes Bos", age: 80} {name: "Wes Bos", age: 80}
captain.age = 99
console.log(person, captain) // {name: "Wes Bos", age: 99} {name: "Wes Bos", age: 99}

// 所以也要做個複製出來
const cap2 = Object.assign({}, person, { number: 99, age: 12 })
console.log(person, cap2) //{name: "Wes Bos", age: 99} {name: "Wes Bos", age: 12, number: 99}
// 然而這個 assign 方法是 shallow 的,如果有多層結構,還是會改到原本 Object
const wes ={
name: 'Wes',
age: 100,
social: {
twitter: 'wesbos',
facebook: 'developer'
}
}
const dev = Object.assign({}, wes)
dev.social.twitter = "qoo"
console.log(wes, dev)
//解決方法之一:
const dev2 = JSON.parse(JSON.stringify(wes));




prevent_hack