javascript中所有函數參數都是按值傳遞
在看《JavaScript高級程序設計》(第三版)的時候,傳遞參數這一節,里面提到
ECMAScript中所有函數的參數都是按值傳遞的
它自己的解釋是,
把函數外部的值復制給函數內部的參數,就和把值從一個變量復制到另一個變量一樣。
基本類型值的傳遞如同基本類型變量的復制一樣,
而引用類型值的傳遞,則如同引用類型變量的復制一樣。
我們先明白幾個概念,之后再討論。
數據類型
基本數據類型,有6種,Undefined、Null、Boolean、Number、String、Symbol
引用類型,Object、Array、Date、RegExg、Function等
數據類型介紹,可以參考JavaScript數據類型的非常6+1
內存空間
var a = 2
var b = 'abc'
var c = true
var d = {value : 1}
var e = [1, 2, 3]
定義的以上幾個變量,在內存中,所占用的空間如圖示意:

基礎數據類型值,在棧中存儲實際的值。引用數據類型值,在棧中存儲引用地址值,在堆中存儲實際的值。
賦值拷貝/復制
基礎數據類型,賦值拷貝,拷貝實際值
var a = 1
var b = a
b = 2
console.log(a) // 1
引用類型,賦值拷貝,拷貝引用地址,淺拷貝。可以參考javascript中的淺拷貝ShallowCopy與深拷貝DeepCopy
var a = {value : 1}
var b = a
b.value = 2
console.log(a.value) // 2
舉例分析
明白了以上幾個概念,我們再來說說,把函數外部的值復制給函數內部的參數,就和把值從一個變量復制到另一個變量一樣。也就是一個賦值復制過程。我們舉幾個例子分析下。
1. 傳遞基礎類型,函數中不修改參數類型
var a = 1
function func(o) {
o = 2
console.log(o)
}
func(a) // 2
console.log(a) // 1
這里的func函數,傳遞了一個參數o,而參數實際上是函數的局部變量。那么,我們就可以修改函數
var a = 1
function func() {
var o = a // 函數內部的參數變量,賦值函數外部的值
o = 2 // 修改內部變量的值
console.log(o)
}
func(a) // 2
console.log(a) // 1
可以得到相同的結果。他們在內存中的變化,如圖示意:

從以上圖中,我們能清楚的看出,變量a一直等于1,而變量o,由于賦值之后,復制了a的實際值,在內存中開辟了空間,存儲在棧中。再執行func函數,修改變量o的值,只會影響其自身。
2. 傳遞基礎類型,函數中修改類型
var a = 1
function func(o) {
o = {value : 1}
console.log(o)
}
func(a) // {value: 1}
console.log(a) // 1
同理,我們也可以修改這里的函數
var a = 1
function func() {
var o = a // 函數內部的參數變量,賦值函數外部的值
o = {value : 1} // 修改內部變量的值
console.log(o) // {value: 1}
}
func() // {value: 1}
console.log(a) // 1
內存中的變化示意圖:

從以上圖中,我們能清楚的看出,變量a一直等于1,而變量o,由于內部參數賦值之后,復制了a的實際值,在內存中開辟了空間,存儲在棧中。再執行func函數,修改變量o的值,只會影響其自身。
3. 傳遞引用類型,函數中不修改類型
var a = { value : 1 }
function func(o) {
o.value = 2
console.log(o)
}
func(a) // { value : 2}
console.log(a) // {value : 2}
同理,我們也可以修改這里的函數
var a = { value : 1 }
function func() {
var o = a // 函數內部的參數變量,賦值函數外部的值
o.value = 2 // 修改內部變量的值
console.log(o) // {value: 2}
}
func() // {value: 2}
console.log(a) // {value: 2}
內存中的變化示意圖:

從以上圖中,我們能清楚的看出,由于變量a是引用類型,通過函數內部參數的賦值復制,傳遞了引用地址值,那么變量a和o會指向同一個內存對象。再執行func函數,修改變量o在堆內存中的值,并沒有修改在棧中的引用地址的值。這樣,由于變量o和變量a使用的是同一個引用地址,也就是同一個堆內存中的值,那么變量a的值,也就會隨著變量o的變化而變化了。
4. 傳遞引用類型,函數中修改類型
var a = {value : 1 }
function func(o) {
o = 2
console.log(o)
}
func(a) // 2
console.log(a) // { value : 1}
接下來,我們也可以修改這里的函數
var a = { value : 1 }
function func() {
var o = a // 函數內部的參數變量,賦值函數外部的值
o = 2 // 修改內部變量的值
console.log(o) // 2
}
func() // 2
console.log(a) // {value: 1}
內存中的變化示意圖:

由于變量a是引用類型,通過函數內部參數的賦值復制,傳遞了引用地址值,那么變量a和o會指向同一個內存對象。再執行func函數時,改變了變量o的數據類型,變成了基礎數據類型,也就切斷了引用。這樣,變量a和o就沒有關系了。
總結
JavaScript中所有函數參數都是按值傳遞的。基本類型值,傳遞的是實際值,引用類型,傳遞的是引用地址值。
update 2020-11-17:
基本類型值作為參數傳遞,經過函數處理,不會修改函數外的基本類型的值。
引用類型值作為參數傳遞,經過函數處理,有可能會修改函數外的引用類型的值
參考
出處:http://www.rzrgm.cn/weiqinl
個人主頁http://weiqinl.com
github: weiqinl
簡書:weiqinl
您的留言討論是對博主最大的支持!
本文版權歸作者所有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接,否則保留追究法律責任的權利。

浙公網安備 33010602011771號