# JavaScript References VS Copying #### 從Javascript30-14 學習Javascript中陣列與物件的引用(refrence)及複製(Copying)。 #### ![](https://i.imgur.com/gG6DVBY.jpg) --- ### :lollipop:Step1 #### 從String、Number、Boolean類型的值開始。 ``` let coffee = 100; //先宣告Number型的變量age let mocha = age; //將此變量賦值給另一個變量age2 console.log(coffee,mocha); // 100 100 coffee = 200; console.log(coffee,mocha); // 200 100 ``` 由上例可見對coffee修改並不會對mocha造成影響 ### :lollipop:Step2 #### 陣列的複製 ``` const players = ['Wes', 'Sarah', 'Ryan', 'Poppy']; const team = players; console.log(players, team); team[3] = 'Lux'; //延續上一個步驟的思路,將值賦予給team[3] console.log(players, team); // ["Wes", "Sarah", "Ryan", "Lux"] ["Wes", "Sarah", "Ryan", "Lux"] ``` 發現players陣列也被修改了。因為team是這個陣列的引用,並不是複製。team 和 players 這兩個變量指向的是同一個數组。該如何解決問題?接者我們開始進行真正的複製 #### Array.prototype.slice() slice 得到的结果是一个對原數组的淺拷貝,原數组不會被修改。 ``` const team2 = players.slice(); team2[3] = 'Lux2' console.log(players,team2); //["Wes","Sarah","Ryan","Lux"] ["Wes","Sarah","Ryan","Lux2"] ``` #### Array.prototype.concat() 使用concat()可以合併陣列: 1. 不會改變原有的數組,而是產生一個新的陣列 2. 可以使用空陣列合併與原本陣列合併 ``` const team3 = [].concat(players); team3[3] = 'Lux3'; console.log(players,team3); // ["Wes","Sarah","Ryan","Lux"] ["Wes","Sarah","Ryan","Lux4"] ``` #### ES6 擴展語法 ``` const team4=[...players]; team4[3] = 'Lux4'; console.log(players,team4); ``` #### Array.from() 使用Array創建新的陣列 ``` const team5 = Array.from(players); team5[3] = 'Lux5'; console.log(players,team5); ``` ### :lollipop:Step3 #### 物件的複製 ``` const person = { name: 'Wes Bos', age: 80 }; ``` 接著我們思考一下可以如何取得上列物件的複製 ``` const captain = person; captain.number = 99; console.log(person, captain); //{name: "Wes Bos", age: 80, number: 99} //{name: "Wes Bos", age: 80, number: 99} ``` 發現person物件的值也被改變了,該如何複製呢? * [`Object.assign()`](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Global_Objects/Object/assign) 使用`Object.assign()`來複製一個或多個物件自身所有可數的屬性到另一個目標物件。 但`Object.assign()`是屬於淺層複製,假設物件裡面還有一個物件的時候還是會修改 到物件的值(如(2)) (1) ``` const cap2 = Object.assign({}, person, { number: 99, age: 12 }); console.log(cap2); // {name: "Wes Bos", age: 12, number: 99} ``` (2) ``` var school = { name: 'Geroge', obj: { name: 'Jovita' } }; var home = Object.assign({}, school); home.obj.name = 'Anna'; console.log(school.obj.name); // Anna console.log(home.obj.name); // Anna ``` 這樣的話該如何做到深層拷貝呢? ##### JSON轉換 利用JSON先將物件轉換成字串,再將字串格式轉換成物件來實現深層複製。 ``` const wes = { name: 'Wes', age: 100, social: { twitter: '@wesbos', facebook: 'wesbos.developer' } }; const dev = Object.assign({}, wes); const dev2 = JSON.parse(JSON.stringify(wes)); ``` #### 但是使用上述方法有一個問題,若你的物件中含有`undefined` 、 `NaN` 或是 `function` 就無法生成。 ``` var b = { name: 'Geroge', obj: { name: 'Jovita' }, fn: function() { // 消失 console.log('123'); }, un: undefined, // 消失 nan: NaN, 直接變成 null }; var e = JSON.parse(JSON.stringify(b)); console.log(e); ``` #### 若要解決此問題可以使用框架,如Jquery的`$.extend`就可以做到完全的深層拷貝。 ``` var b = { name: 'George', obj: { name: 'Jovita' }, fn: function() { console.log('hello'); }, hamburger: undefined, chips: NaN, }; var e = $.extend(true, {}, b); console.log(e); //{name: 'George', obj: {…}, chips: NaN, fn: ƒ} ``` > 參考資料: > 1. https://github.com/soyaine/JavaScript30/tree/master/14%20-%20JavaScript%20References%20VS%20Copying > 2. https://hsiangfeng.github.io/javascript/20200905/1375484447/