帶你深入理解傳遞引數

前端精髓發表於2019-03-01

ECMAScript中所有函式的引數都是按值傳遞的。也就是說,把函式外部的值複製給函式內部的引數,就和把值從一個變數複製到另一個變數一樣。基本型別值的傳遞如同基本型別變數的複製一樣,而引用型別值的傳遞,則如同引用型別變數的複製一樣。有不少開發人員在這一點上可能會感到困惑,因為訪問變數有按值和按引用兩種方式,而引數只能按值傳遞。

在向引數傳遞基本型別的值時,被傳遞的值會被複制給一個區域性變數(即命名引數,或者用ECMAScript的概念來說,就是arguments物件中的一個元素)。在向引數傳遞引用型別的值時,會把這個值在記憶體中的地址複製給一個區域性變數,因此這個區域性變數的變化會反映在函式的外部。請看下面這個例子:

function addTen(num) {
    num += 10;
    return num;
}
var count = 20;    
var result = addTen(count);
alert(count);    //20,沒有變化
alert(result);  //30
複製程式碼

這裡的函式addTen()有一個引數num,而引數實際上是函式的區域性變數。在呼叫這個函式時,變數count作為引數被傳遞給函式,這個變數的值是20。於是,數值20被複制給引數num以便在addTen()中使用。在函式內部,引數num的值被加上了10,但這一變化不會影響函式外部的count變數。引數num與變數count互不相識,它們僅僅是具有相同的值。假如num是按引用傳遞的話,那麼變數count的值也將變成30,從而反映函式內部的修改。當然,使用數值等基本型別值來說明按值傳遞引數比較簡單,但如果使用物件,那問題就不怎麼好理解了。再舉一個例子:

function setName(obj) {
obj.name = "Nicholas";    
 }
var person = new Object();
setName(person);
alert(person.name);    //"Nicholas"  
複製程式碼

以上程式碼中建立一個物件,並將其儲存在了變數person中。然後,這個變數被傳遞到setName()函式中之後就被複制給了obj。在這個函式內部,obj和person引用的是同一個物件。換句話說,即使這個變數是按值傳遞的,obj也會按引用來訪問同一個物件。於是,當在函式內部為obj新增name屬性後,函式外部的person也將有所反映;因為person指向的物件在堆記憶體中只有一個,而且是全域性物件。有很多開發人員錯誤地認為:在區域性作用域中修改的物件會在全域性作用域中反映出來,就說明引數是按引用傳遞的。

相關文章