基本資料型別和複雜資料型別的本質區別
資料型別
JavaScript中的資料型別可以分為兩大類:
- 原始值 (基本型別)
- Boolean
- Null
- String
- Number
- Bigint
- Symbol
- Undefined
- 物件 (複雜型別)
- Object
- Array
- Function
小結:資料分為兩大類,物件和非物件
資料的存放
資料存放在記憶體裡面,記憶體分:棧區(stack),堆區(heap)。
棧區
棧區的特點是:資料按順序存放,記憶體大小由系統分配。
基本型別的存放示意圖:
如果變數儲存的是原始值,那麼這個變數就是值型別,在 JS 裡也叫做基本型別堆記憶體
堆記憶體的特點是:資料隨機存放,大小由程式設計師分配。
複雜型別的存放示意圖:
如果變數儲存的是記憶體位置,那麼這個變數就是引用型別,在 JS 裡也叫複雜型別,也就是物件。行為差異
賦值
基本型別賦值
var a = 1
var b = a
b = 0
console.log(a) // 1
複製程式碼
在棧記憶體中的資料發生複製行為時,系統會自動為新的變數分配一個新值。var b = a執行之後,a與b雖然值都等於1,但是他們其實已經是相互獨立互不影響的值了,所以我們修改了b的值以後,a的值並不會發生變化。
複雜資料型別
var o1 = {name:"o1"}
var o2 = o1
o2.name = "o2"
console.log(o1.name)// o2
複製程式碼
當修改其中一個的值的時候,會影響到另一個。這是因為,這兩個變數指向同一塊記憶體,記憶體裡的資料發生了變化,所以會發生變化。
求值策略
所謂求值策略也就是函式的傳參方式,函式的引數可能是基本型別也可能是複雜型別,這兩種型別在傳參的時候有所區別。
按值傳遞
按值傳遞,引數的值是呼叫者傳遞的值的拷貝(copy of value),函式內部改變引數的值不會影響到外面的值,一般來說,是重新分配了新記憶體,該新記憶體塊的值是外部值的拷貝,並且它的值是用到函式內部的。
var a = 1
function changeA (a) {
a = 2
}
changeA()
console.log(a)// 1
複製程式碼
這說明了,函式內部的a和函式外部的a是相互獨立的,傳參是將值拷貝一份。
那麼對於複雜型別的傳參呢。網上有各種各樣的教程和文章,有的說按引用傳遞,什麼共享傳遞,等等,其實很簡單的道理,明白了上面的記憶體的分配,就很清楚的知道是怎麼回事了,至於是叫按共享傳遞(Call by sharing)還是什麼其他的都不重要。
還是按值傳遞
對於基本型別已經搞清楚了,傳參的方式是按值傳遞,現在看複雜型別的傳參:
這裡分明是已經被改變了,不是說按值傳遞的,只是將值拷貝一份嗎?再看一個例子: 這裡沒有被改變。。是不是有點迷糊。總結一下就是:修改引數的屬性將會影響到外部,而重新賦值將不會影響到外部物件。 到底是怎麼回事呢?實際上,還是按值傳遞,只不過這個值,不是指物件資料本身,而是這個物件的記憶體地址,也就是說,函式的傳參是外部物件的地址的副本。這樣就可以很清楚的解釋了為什麼二者會出現不同的結果。總結:在函式傳參的時候,不管是基本型別還是複雜型別,都是按值傳遞,對於基本型別是拷貝值也就是資料本身,而對於複雜型別,拷貝的是複雜型別在堆記憶體中的地址。