# Vue學習#6 物件傳參考的特性 ###### tags: `Vue 直播班 - 2022 春季班` `Vue` ## JS原始型別種類 Boolean:僅有 true, false 兩個值 Null:僅有 null 的值 Undefined:沒有被定義的變數 String:字串型別 Number:下述說明 Symbol(於 ECMAScript 6 新定義,以後再說明) ## JS原始型別傳值 Call by Value 用一段範例來解釋什麼是傳值 ```=JavaScript let meow = 'meowHello'; let cat; cat=meow; // cat = 'meowHello'; cat='gogo!'; console.log(meow,cat); // meowHello gogo! let dog; console.log(dog); // undefined ``` 透過這個範例可以知道幾件事情 1. 我們可以宣告一個變數並賦予值 => <font color='red'>let meow='meowHello';</font> 2. 我們可以再宣告一個變數並且直接透過變數給予一樣的值 =><font color='red'> cat=meow;</font> 3. 我們可以改變變數的值 => <font color='red'>cat='gogo';</font> 4. 如果宣告一個變數,如果沒賦予值就會是undefined => <font color='red'>let dog;</font> ### 變數賦予值是怎麼回事呢? 每宣告一個值都需要拿出一個記憶體的空間來儲存,而變數就是去指向這些記憶體位址,所以我們宣告變數是去取用這些值的記憶體位址,用範例來解釋就是記憶體有一塊空間存放一個叫做 'meowHello' 的值,並宣告一個meow的變數去指向這個記憶體位址。 要注意的是 cat 的值變成 'gogo!',是因為 cat 改變了記憶體的指向,不是改變了 'gogo!' 這個值,值是不能被變動的。像上面這樣的過程,我們可以稱做<font color="red" size=5>「傳值」</font>。 ### 什麼是傳值? ```=JavaScript let meow = 'meowHello'; let cat; cat=meow; // cat = 'meowHello'; ``` 以 let meow = 'meowHello' 來說,變數 meow 指向電腦中某記憶體的位置,並在這個記憶體儲存 'meowHello' 這個值,我們另外又宣告了一個 cat,雖然 meow 跟 cat 的值是一樣的,但其實 cat 另外在不同的記憶體位置,只是複製了 meow 的值,他們兩個仍然是各自存在於獨立的記憶體位置,因此後來我們又給 cat 一個新的值的時候 meow 仍然不會被變動。 最後再用一個範例再來明白一次 ```=JavaScript let a=50; let b=a; console.log(a === b); // true ``` 50 為原始型別,a 跟 b 互相比較的是彼此的值,因此這裡回傳 true。 ## JS物件型別種類 陣列 array : [] 函式 function : function abc(){} 物件 object : obj={} ## JS物件傳參考 Call by reference 物件型別是以「傳址 ( Pass by reference )」的方式傳遞。一個物件型別的變數,被存在某個有地址的「位置」,而這個「記憶體位置」則存在於這個變數中,因此「記憶體位置為參考」,並在變數間傳遞存取的行為,就稱為「傳參考呼叫」。 ```=JavaScript let arr=[1,2,3]; let newArr=arr; console.log(`arr : ${arr} , newArr : ${newArr}`); //"arr : 1,2,3 , newArr : 1,2,3" arr[1]=4; console.log(`arr : ${arr} , newArr : ${newArr}`); //"arr : 1,4,3 , newArr : 1,4,3" console.log(arr === newArr); //true ``` 建立了一個陣列 arr,這個陣列指向了一個新的記憶體位置,這時候我們再建立一個陣列 newArr,並且讓 newArr 等於 arr,此時 newArr 會指向 arr 的記憶體位置,因此之後不論我們怎麼修改,console.log 都會呈現 newArr === arr 這樣的結果,因為兩個記憶體指向是在同一個位置,物件型別是以「記憶體位置為參考」互相傳遞,而不是以值做傳遞的過程就稱為「傳址」。 ```=JavaScript let arr=[1,2,3]; let newArr=arr; console.log(arr === newArr); // true newArr=[1,2,3]; console.log(arr === newArr); // false ``` 要注意的是如果我們直接用等號賦予新的值,就等於建立一個新的記憶體位置,所以 arr 跟 newArr 會是false ## 物件型別如何好好的複製值並不會互相干涉呢? 物件型別修改或新增值會造成指向同個記憶體位置的變數值也跟著改變,很容易造成錯誤,所以有了兩種方法解決 ### 1.淺層拷貝 shallow copy ```=JavaScript const person={ name:'danny', } person2=person; person2.name='May' console.log(person.name , person2.name); // May May ``` 用範例來看 person2 複製了 person 的值,所以等於是 person、person2 共用記憶體空間,所以原本的 person.name 應該是 danny 也會被改成 May 共用同一個記憶體想當然只要有一方的值改變了另一方也會跟著改變,這個就叫做淺拷貝。 #### 那如何不被修改哩 ```=JavaScript const person={ name:'danny', } let person2=Object.assign({},person); let person3={...person}; console.log(person===person2 , person===person3); // false false person2.name='May' console.log(person.name , person2.name); // May May ``` 有兩種方法可以達成淺層拷貝 ##### 1. Object.assign ```=JavaScript let person2=Object.assign({},person); ``` ##### 2. ... ```=JavaScript let person3={...person}; ``` <font size=5 color='red'>!!!注意</font> **淺層拷貝只能複製第一層的物件並且不受影響,如果要是第二層甚至更多層的物件還是會讓原本的記憶體位置受到影響哦!** ### 2.深層拷貝 deep copy ```=JavaScript const person={ name:'danny' } const person2=JSON.parse(JSON.stringify(person)); console.log(person === person2); // false ``` 深層拷貝很簡單,就是另外創造一個一模一樣的物件,而且不共用記憶體空間,修改新的物件不會影響到原本的物件 在上面範例中我們先將 person 物件轉成字串,再將字串轉成物件,並由 person2 去指向,在轉成字串和轉回物件的過程中就已經分配一個不同的記憶體位置了!!! ### 圖片說明: ![](https://i.imgur.com/F3S6YG2.png) ## 參考資料 [JavaScript Day 19. by value ( 傳值 ) 與 by reference ( 傳址 )](https://ithelp.ithome.com.tw/articles/10273802) [JS-淺拷貝(Shallow Copy) VS 深拷貝(Deep Copy)](https://kanboo.github.io/2018/01/27/JS-ShallowCopy-DeepCopy/) [關於JS中的淺拷貝(shallow copy)以及深拷貝(deep copy)](https://medium.com/andy-blog/%E9%97%9C%E6%96%BCjs%E4%B8%AD%E7%9A%84%E6%B7%BA%E6%8B%B7%E8%B2%9D-shallow-copy-%E4%BB%A5%E5%8F%8A%E6%B7%B1%E6%8B%B7%E8%B2%9D-deep-copy-5f5bbe96c122)