參數傳值是指函數調用時,給函數傳遞配置或運行參數的行爲,包括經過call、apply 進行傳值。javascript
在實際開發中,咱們總結javascript
參數傳值分爲基本數據類型按值傳遞(String、Numbe、Boolean、Null、undefind),引用數據類型按引用傳遞(Object, 包括Array、Function、Data)。這篇文章將要糾正這一誤解: 實質上引用類型是按共享傳遞的。java
在探索傳值問題前,咱們先看一下 javascript 在聲明變量時是怎樣分配內存的app
基本類型是將原始值保存在棧中。引用類型是將數據保存在堆中,而後在棧中創建堆地址的引用。在javascript中是不容許直接訪問保存在堆內存中的對象的,因此在訪問一個對象時,首先獲得的是這個對象在堆內存中的地址,而後再按照這個地址去得到這個對象中的值,這就是傳說中的按引用訪問。而原始類型的值則是能夠直接訪問到的。不一樣的內存分配機制也帶來了不一樣的訪問機制。ecmascript
而基本類型和引用類型在變量複製的時候也存在區別:函數
因此咱們總結 javascript 中全部函數參數都是按值傳遞,都是把形參複製給實參,只是基本數據類型複製的是原始值
,而引用類型複製的是堆內存的地址
。性能
引用類型的這種求值策略就是 按共享傳遞(call by sharing).編碼
理論知識梳理完了,下面舉幾個例子分析一下具體的情形.lua
let a = 1 function foo(x) { console.log(x) } foo(a) console.log(a) // 2 // 1
變量 a
的值直接複製到了foo
函數的實參x
上,此時變量x
是變量 a 的一個副本。它們獨立的在各自的上下文棧中保存了值‘1’,且相互之間互不影響,咱們對 a、x的讀寫操做,操做的是他們各自的值。spa
示例圖中,在全局上下文和foo
的上下文中各自保存了值1
.設計
let a = { abc: 1 } function foo(x) { x.abc = 2 console.log(x.abc) } foo(a) console.log(a.abc) // 2 // 2
根據編碼經驗咱們會很錯誤的得出,上面的栗子是按引用傳遞。對象a
的引用被傳遞到函數foo內部, 函數內部變量x
指向全局變量a
,從而實現了引用的傳遞,因此變量x
和變量a
讀寫的是同一個對象。咱們用一張圖來揭示:
但實際上是按共享傳遞。按引用傳遞是咱們對 javascript 求值策略的誤解,若是是按引用傳遞那下面這個例子就懵比了:
let a = { abc: 1 } function foo(x) { console.log(x) // {abc: 1} x = 2 console.log(x) // 2 } foo(a) console.log(a.abc) // 1
foo 函數執行時第一個打印輸出 ‘{abc: 1}’, 第二個打印輸出 ‘2’, 調用 foo 函數後的console.log(a.abc)
一句打印輸出‘1’。
按引用傳遞的話。表達式console.log(a.abc)
是在 foo 函數執行後執行的,此時的a
應該已經被賦值爲 2, a.abc
是不存在的應該打印輸出 ‘undefind’。 可是卻打印輸出了 1,說明在 foo 函數的內部執行x = 2
是並無修改外層對象 a 的值。
爲何會出現a
、x
在指向同一個對象後,對x
賦值又沒有改變原對象的值呢?
由於這裏對象傳遞給實參是按共享傳遞(call by sharing)的,根據引用類型變量複製的特色(上面描述過):
foo 函數執行時, 形參 x 的值是傳進去的對象 a 的內存地址引用,即在變量對象建立階段x
保存的是一個對象的堆內存地址。此時 a、x 都指向同一對象。 接着在函數的執行階段,代碼的第二行將原始數據類型 2 賦值給 x,致使 x 再也不保存原先的堆內存地址轉而保存一個原始值,再次訪問 x 的時候是訪問對 x 最後一次賦值的原始值。
因此對 x 的賦值會改變上下文棧中標識符 x 保存的具體值
此時若是使用的是按引用傳遞 ,則變量 a 所指向的對象因該也被賦值爲 2。但其實對 x 賦值爲一個基本數據類型並無使原對象爲一個字面量值,這就說明引用類型並非按引用傳值,不是聽從按引用規則來處理值的寫入。
須要區分給對象屬性賦值與直接給對象賦值的區別:
let a = { abc: 1 } function foo(x) { x.abc = 99 console.log(x) // {abc: 99} x = 2 console.log(x) // 2 } foo(a) console.log(a) // {abc: 99}
在 foo 函數內部修改對象 x 的屬性,會致使 x
、a
指向的對象被修改,由於它們指向同一個堆地址。
參考:
《javascript高級程序設計》