1. 程式人生 > >JS中的函式引數傳遞到底是按值傳遞還是按引用傳遞

JS中的函式引數傳遞到底是按值傳遞還是按引用傳遞

首先我們知道JS中的資料型別大致可以分為簡單資料型別和複雜資料型別;
當我們宣告一個變數並給它賦值時,可以賦給其簡單值和複雜值(以下堆記憶體和棧記憶體的地址表示均隨意取的,只是為了區分,不代表真實的記憶體地址);
針對簡單資料型別:
例1

var simpleData1 = 18
var simpleData2 = simpleData1
simpleData2 =  19
console.log(simpleData1 )   //打印出18

以上例子的過程為:先給simpleData1 在棧記憶體中分配一個地址s101,裡面儲存數字18,然後將simpleData1賦給simpleData2 ,相當於緊接著在棧記憶體中又分配一個地址s102,裡面同樣儲存數字18,這時再將s102中的內容變成19,這當然不會影響s101中的內容。
在這裡插入圖片描述

針對複雜資料型別,以陣列為例
例2

var arr1 = ['zyp','zyp','zyp']
var arr2 = arr1
arr2[0] = 'zyp1'
console.log(arr1)  //打印出 ["zyp1", "zyp", "zyp"]

以上例子的過程為:先在堆中開闢一塊地址h1001來存陣列[‘zyp’,‘zyp’,‘zyp’],同時將該堆記憶體的地址h1001存放在棧記憶體s103中,然後再在棧記憶體中開闢一個地址s104同樣存放h1001,這時arr1和arr2指向同一塊堆記憶體,改變其中的值會同時反映在變數arr1和arr2上。
在這裡插入圖片描述
例3

var arr1 = ['zyp','zyp','zyp']
var arr2 = arr1
arr2 =  ['zyp1','zyp1','zyp1']
arr2[0] = 'zyp2'
console.log(arr2) //打印出 ["zyp2", "zyp1", "zyp1"]
console.log(arr1) //打印出 ["zyp", "zyp", "zyp"]

以上例子的過程為:跟上面一樣,先在堆中開闢一塊地址h1001來存陣列[‘zyp’,‘zyp’,‘zyp’],同時將該堆記憶體的地址h1001存放在棧記憶體s103中,然後再在棧記憶體中開闢一個地址s104同樣存放h1001,這時arr1和arr2指向同一塊堆記憶體。不同的是:這時我們又在堆記憶體中開闢出一塊新的地址h1002來存放新陣列[‘zyp1’,‘zyp1’,‘zyp1’],新地址h1002存放在s104中,這時arr2中儲存的地址已經由h1001變成了h1002, 這時再改變h1002對應的堆記憶體中的值,便和arr1沒有任何關係了。
在這裡插入圖片描述

明白了上述賦值過程再來理解函式傳參便是一個道理了

function changeObj(arr1) {
	arr1= ['a1', 'a1', 'a1']
}
var arr = ['a', 'a', 'a']
changeObj(arr)
console.log(arr)  // 打印出["a", "a", "a"]

這個例子中:假設陣列[‘a’, ‘a’, ‘a’]在堆記憶體中對應的地址為:h2001, 實參arr在棧記憶體中對應的地址為s201, 裡面儲存的是h2001。呼叫changeObj函式時,將arr複製給arr1,這裡對應的操作是自動將在棧記憶體中分配一塊地址s202來儲存h2001,因此arr和arr1對應的是同一塊堆記憶體,但這時又在堆記憶體中開闢新空間h2002來儲存陣列[‘a1’, ‘a1’, ‘a1’],h2002儲存在arr1對應的棧記憶體s202中,這裡的道理就和例3是一樣的了