JS的方法引數傳遞(按值傳遞)

Mike617發表於2019-02-12

出處:JavaScript高階程式設計(第三版)第四章——變數、作用域和記憶體問題

一句話總結

JS的方法引數是按值傳遞

前提

ES變數分為兩種資料型別的值:基本型別和引用型別

基本型別儲存

基本型別的值在申請記憶體時是固定大小,所以儲存在棧記憶體,故在複製基本型別變數時也是在棧記憶體中新開闢一份記憶體空間進行儲存

引用型別儲存

而引用型別變數的值大小不固定,且可任意改動,引用型別變數的本質是指向某一塊記憶體區域的指標變數,故引用型別變數儲存在記憶體自由分配的堆記憶體中,在對引用變數作直接複製(重新賦值)時也只是將新的變數指向相同的一片記憶體區域(即兩個指標指向同一片堆記憶體空間)

引用型別探析

引用型別的值是存放在記憶體中的物件,但由於JS語言不允許直接訪問記憶體中的位置(即不能直接操作物件的記憶體空間),故操作物件時實際是在操作物件的引用,而不是直接操作實際的物件本身(即引用型別的值是按引用訪問的,這個引用可以理解為物件的控制程式碼)

特別地

很多語言對於字串是以物件形式進行表示,故為引用型別變數,但ES不是

詳解

ES中所有函式(方法)的引數都是按值傳遞的,即呼叫一個方法時,是將呼叫該方法時傳入該方法的引數的複製給函式內部的引數(將實參的值複製給形參)

具體分類

JS在訪問變數時有按值和按引用兩種方式,但引數只會按值傳遞

向引數傳遞基本型別的值

被傳遞的值會被複制給一個區域性變數(這個區域性變數就是形參,在ES中就是arguments物件的一個元素)

例項
function addTen(num) {
  num += 10;
  return num;
}
var count = 20;
var result = addTen(count);
// 看有沒有影響到原變數
alert(count);
alert(result)
複製程式碼

執行結果

20
30
複製程式碼

向引數傳遞引用型別的值

JS會把被傳遞的值的地址複製給一個區域性變數,因為複製的是地址,所以在函式執行時,函式形參在函式內部改變時會影響到函式外部的該引用變數的值,因為兩個地址指向同一片記憶體區域,但在函式執行結束,函式內部的區域性變數被銷燬,影響即會消失

例項
function setName(obj) {
  obj.name = 'tom';
}
var person = new Object();
setName(person);
// 當把person傳遞給setName時,obj和person都指向相同的記憶體,所以對obj所指向的記憶體區域修改會影響到person
alert(person.name);
複製程式碼

執行結果

tom
複製程式碼
分析

因為person指向的物件在堆記憶體中只存在一個,並且是全域性物件

求證引數是按值傳遞而不是按引用傳遞

function setName(obj) {
  obj.name = 'tom';
  obj = new Object();
  obj.name = 'jerry';
}
var person = new Object();
setName(person);
// 當把person傳遞給setName時,obj和person都指向相同的記憶體,所以對obj所指向的記憶體區域修改會影響到person
alert(person.name);
複製程式碼

執行結果

tom
複製程式碼
結論

函式內部重新生成的物件obj,並對其新賦值jerry並沒有改變函式外部person對應的屬性值

如果向引用型別引數賦值是按引用賦值,那麼person的name應該變為jerry,因為假設形參obj拿到的是person的引用,而不是person引用的值,那麼當函式內部生成新物件,並對obj進行重新指向時,形參obj的指向改變,外部的person的指向也應該改變,但是結果證明alert(person.name)顯示的依舊是tom,所以即使函式引數是引用型別,也是按值傳遞

而實際上在函式內部重寫obj時,這個變數引用的是一個區域性物件變數,該區域性物件會在函式執行完畢之時銷燬

原文自 個人github部落格,歡迎star

相關文章