iT邦幫忙

0

JS30 Day 14 - JavaScript References VS Copying學習筆記

這個章節比較偏觀念,如淺複製及深複製的一些概念,我覺得是非常重要的重點。

start with strings, numbers and booleans

首先是字串、數值、布林值的賦值


    let a = "A";
    let b = a;
    a = "B";
    console.log(a, b); //  B,A

    let c = 0;
    let d = c;
    c++;
    console.log(c, d); //  1,0

    let e = true;
    let f = e;
    e = !f;

    console.log(e, f); // false,true

發現記憶體沒有A就創建一個A值並賦予記憶體位置,再將g,h,i指向,A的記憶體位置,再來找B,C都沒有,就創建B,C並賦予記憶體位置,再來h指向B記憶體位置,i指向C記憶體位置,最後將g+h=AB並賦予記憶體位置,g指向AB記憶體位置,接著g+i=ABC並賦予記憶體位置,g指向AB記憶體位置。


    let g = "A";
    let h = "A";
    let i = "A";
    h = "B", i = "C", g += h, g += i;
    console.log(g, h, i); //ABC , B ,C

https://ithelp.ithome.com.tw/upload/images/20200625/20126182AD6m6hVrCU.png

Let's say we have an array

接著是做關於陣列的複製。

    const players = ['Wes', 'Sarah', 'Ryan', 'Poppy'];
    let player2 = players;
    player2[0] = "jojo";
    console.log(players, player2);
    [0: "jojo",1: "Sarah",2: "Ryan",3: "Poppy"] [0: "jojo",1: "Sarah",2: "Ryan",3: "Poppy"]

發現這並不是我們所要得複製,他們都對應到同一個陣列,所以利用以下幾種方法,可達到互不影響的複製。

法1:slice()方法,利用slice可達到複製,因為slice會額外再產生一個新的陣列。

    let player2 = players.slice();
    player2[0] = "jojo";
    console.log(players, player2);
    (4) ["Wes", "Sarah", "Ryan", "Poppy"] (4) ["jojo", "Sarah", "Ryan", "Poppy"]

法2:concat()方法,被用來合併兩個或多個陣列。此方法不會改變現有的陣列,會回傳呼叫者陣列本身,也就是與players的值合併後的空陣列。

    let player2 = [].concat(players);
    player2[0] = "jojo";
    console.log(players, player2);

法3:解構賦值,將players中的值拆分出來,在合併至player2,回傳一個新的陣列。

    let player2 = [...players];
    player2[0] = "jojo";
    console.log(players, player2);

再來是配合呼叫函數所產生的陣列複製。

    function createObj(name) {
      return {
        //name:name 
        name
      };
    }

    let pr1 = createObj("johnny");
    let pr2 = createObj("lisa");
    let pr3 = createObj("iggy");

    let gr1 = [pr1, pr2, pr3];
    let gr2 = gr1.slice();
    // 由於gr2[0]值為pr1,所以他會去改變gr2[0].name,也就是pr1的值,那我再次獲取gr1,它裡面的pr1也會是改變後的值。
    gr2[0].name = "momo";
    console.log(gr1, gr2); // (3) ["momo", "lisa", "iggy"] 

    // 但如果改為這樣,可發現兩者已互不影響,因為記憶體發現我們的記憶體中並無momo這個值,所以他會創建一個momo的值,並給予他一個記憶體空間,而後我們gr2[0](也就是gr2的pr1)就會指向他,並不影響。
    gr2[0] = "momo";
    console.log(gr1, gr2); // (3) ["johnny", "lisa", "iggy"]  (3) ["momo", "lisa", "iggy"] 

https://ithelp.ithome.com.tw/upload/images/20200625/201261823sTZgeJG1D.png

with Objects

接下來是物件之間的複製與賦值。


    let person = {
      name: 'Wes Bos',
      age: 80
    };
    let p1 = person;
    // 查看記憶體無XXX後,創建一個XXX的值並賦予記憶體空間,接著person在指向他,而與p1互不影響。
    person = "XXX";
    console.log(person, p1); // XXX,{...}
    // 前面步驟與上面一樣,不同的是會改變person.name的值也就是0x1的name,因此p1也會受影響,因為p1指向的也是person所指向的0x1。
    
    person.name = "XXX";
    console.log(person, p1); // {XXX...},{XXX...}

https://ithelp.ithome.com.tw/upload/images/20200625/20126182dpX6oQO9e8.png

將物件x,y合併後,賦予z,會發現後者的重複的name可以覆蓋前者的。


    let x = {
      name: "bogi",
      age: 87
    };
    let y = {
      name: "dandy"
    };
    let z = Object.assign(x, y);

    console.log(z); // {name: "dandy", age: 87}

如果我們今天在物件新增一個函數,然後以kk複製他並一樣先字串化,然後再轉回obj化,會發現再以kk呼叫就會報錯,因為經stringify,function會被lose掉


    let k = {
      name: "eva",
      age: 22,
      eat: function () {
        console.log("amazing");
      }
    };

    let kk = JSON.parse(JSON.stringify(k));

    // k.eat()  // amazing
    // kk.eat() // Uncaught TypeError: kk.eat is not a function at <anonymous>:1:4

如果今天我們的物件不含函數,那麼利用JSON.parse(JSON.stringify(wes)),就可以達到deep copy,但如果有就無法。


    const wes = {
      name: 'Wes',
      age: 100,
      social: {
        twitter: '@wesbos',
        facebook: 'wesbos.developer'
      }
    };
    const dev = Object.assign({}, wes);

    // 第一題
    // 互不影響
    dev.social = null;
    console.log(wes); // 不變 {twitter...}; 

    // 第二題
    dev.social.facebook = null;
    console.log(wes.social); // {twitter:..., facebook:null}

    const dev3 = JSON.parse(JSON.stringify(wes));
    

第一題

https://ithelp.ithome.com.tw/upload/images/20200625/20126182YLg1nLeNlC.png

第二題

https://ithelp.ithome.com.tw/upload/images/20200625/20126182BJKWlWFn89.png

第三題:
首先將wes(0x6)的資料,經由stringify將0x6拿出來,並賦予一個新的記憶體空間0x8,再來經過parse,將0x5的資料拿出來(因為0x5為物件(或陣列或函數)資料就會重新賦予記憶體空間)0x9,而後重新創建一個記憶體空間0x10讓wes3指向他。

https://ithelp.ithome.com.tw/upload/images/20200706/201261821aoF8MI3eI.png


尚未有邦友留言

立即登入留言