JS 資料型別和堆疊

查小小飛發表於2019-12-07

基本資料型別和複雜資料型別的本質區別

資料型別

JavaScript中的資料型別可以分為兩大類:

  • 原始值 (基本型別)
    • Boolean
    • Null
    • String
    • Number
    • Bigint
    • Symbol
    • Undefined
  • 物件 (複雜型別)
    • Object
    • Array
    • Function

小結:資料分為兩大類,物件和非物件

資料的存放

資料存放在記憶體裡面,記憶體分:棧區(stack),堆區(heap)。

棧區

棧區的特點是:資料按順序存放,記憶體大小由系統分配。

基本型別的存放示意圖:

JS 資料型別和堆疊
如果變數儲存的是原始值,那麼這個變數就是值型別,在 JS 裡也叫做基本型別

堆記憶體

堆記憶體的特點是:資料隨機存放,大小由程式設計師分配。

複雜型別的存放示意圖:

JS 資料型別和堆疊
如果變數儲存的是記憶體位置,那麼這個變數就是引用型別,在 JS 裡也叫複雜型別,也就是物件。

行為差異

賦值

基本型別賦值

var a = 1
var b = a
    b = 0
console.log(a)  // 1
複製程式碼

JS 資料型別和堆疊

在棧記憶體中的資料發生複製行為時,系統會自動為新的變數分配一個新值。var b = a執行之後,a與b雖然值都等於1,但是他們其實已經是相互獨立互不影響的值了,所以我們修改了b的值以後,a的值並不會發生變化。

複雜資料型別

var o1 = {name:"o1"}
var o2 = o1
o2.name = "o2"
console.log(o1.name)// o2 

複製程式碼

JS 資料型別和堆疊
當修改其中一個的值的時候,會影響到另一個。這是因為,這兩個變數指向同一塊記憶體,記憶體裡的資料發生了變化,所以會發生變化。

求值策略

所謂求值策略也就是函式的傳參方式,函式的引數可能是基本型別也可能是複雜型別,這兩種型別在傳參的時候有所區別。

按值傳遞

按值傳遞,引數的值是呼叫者傳遞的值的拷貝(copy of value),函式內部改變引數的值不會影響到外面的值,一般來說,是重新分配了新記憶體,該新記憶體塊的值是外部值的拷貝,並且它的值是用到函式內部的。

var a = 1
function changeA (a) {
    a = 2
}
changeA()
console.log(a)// 1
複製程式碼

這說明了,函式內部的a和函式外部的a是相互獨立的,傳參是將值拷貝一份。

那麼對於複雜型別的傳參呢。網上有各種各樣的教程和文章,有的說按引用傳遞,什麼共享傳遞,等等,其實很簡單的道理,明白了上面的記憶體的分配,就很清楚的知道是怎麼回事了,至於是叫按共享傳遞(Call by sharing)還是什麼其他的都不重要。

還是按值傳遞

對於基本型別已經搞清楚了,傳參的方式是按值傳遞,現在看複雜型別的傳參:

JS 資料型別和堆疊
這裡分明是已經被改變了,不是說按值傳遞的,只是將值拷貝一份嗎?再看一個例子:

JS 資料型別和堆疊
這裡沒有被改變。。是不是有點迷糊。總結一下就是:修改引數的屬性將會影響到外部,而重新賦值將不會影響到外部物件。 到底是怎麼回事呢?實際上,還是按值傳遞,只不過這個值,不是指物件資料本身,而是這個物件的記憶體地址,也就是說,函式的傳參是外部物件的地址的副本。這樣就可以很清楚的解釋了為什麼二者會出現不同的結果。

總結:在函式傳參的時候,不管是基本型別還是複雜型別,都是按值傳遞,對於基本型別是拷貝值也就是資料本身,而對於複雜型別,拷貝的是複雜型別在堆記憶體中的地址。

相關文章