javascript
JavaScript 是传值调用还是传引用调用?
1. 例子
先來看兩個個來自于 《JavaScript 高級程序設計》P70-P71 的兩個例子。
1.1. 基本類型參數傳遞
function addTen(num) {num += 10;return num; }var count = 20; var result = addTen(count); alert(count); // 20, 沒有變化 alert(result); // 30書上解釋說,JavaScript 參數傳遞都是按值傳參。
所以傳遞給 addTen 函數的值是 20 這個值,所以函數執行結束原始變量 count 并不會改變。
1.2. 引用類型參數傳遞
function setName(obj) {obj.name = 'Nicholas';obj = new Object();obj.name = 'Greg'; }var person = new Object(); setName(person); alert(person.name); // Nicholas為什么結果是 Nicholas 呢?
疑問:如果是傳值,那應該是把 person 變量的值(也就是一個指向堆內存中對象的指針)傳遞到函數中,obj.name = 'Greg'; 改變了堆內存中對象的屬性,為什么 person.name 還是 Nicholas ?
2. 傳值還是傳引用?
讓我們再將上面兩個例子綜合為下面的例子:
function changeStuff(a, b, c) {a = a * 10;b.item = "changed";c = {item: "changed"}; }var num = 10; var obj1 = {item: "unchanged"}; var obj2 = {item: "unchanged"};changeStuff(num, obj1, obj2);console.log(num); console.log(obj1.item); console.log(obj2.item);最終的輸出結果是:
10 changed unchanged所以 JS 到底是傳值調用還是傳引用調用呢?要弄清楚這個問題,首先我們要明白到底什么是傳值調用(Call-ny-value)和傳引用調用(Call-by-reference)。
2.1. 傳值調用(Pass by value)
在傳值調用中,傳遞給函數參數是函數被調用時所傳實參的拷貝。在傳值調用中實際參數被求值,其值被綁定到函數中對應的變量上(通常是把值復制到新內存區域)。
即 changeStuff 的參數 a b c 是 num1 obj1 obj2 的拷貝。所以無論 a b c 怎么變化,num1 obj1 obj2 都保持不變。
問題就在于 obj1 變了。
2.2. 傳引用調用(Pass by reference)
在傳引用調用調用中,傳遞給函數的是它的實際參數的隱式引用而不是實參的拷貝。通常函數能夠修改這些參數(比如賦值),而且改變對于調用者是可見的。
也就是說 changeStuff 函數內的 a b c 都分別與 num obj1 obj2 指向同一塊內存,但不是其拷貝。函數內對 a b c 所做的任何修改,都將反映到 num obj1 obj2 上 。
問題就在于 num 和 obj2 沒變。
從上面的代碼可以看出,JavaScript 中函數參數的傳遞方式既不是傳值,也不是傳引用。主要問題出在 JS 的引用類型上面。
JS 引用類型變量的值是一個指針,指向堆內存中的實際對象。
2.3. 傳共享調用(Call by sharing)
還有一種求值策略叫做傳共享調用(Call-by-sharing/Call by object/Call by object-sharing)。
傳共享調用和傳引用調用的不同之處是,該求值策略傳遞給函數的參數是對象的引用的拷貝,即對象變量指針的拷貝。
也就是說, a b c 三個變量的值是 num obj1 obj2 的指針的拷貝。 a b c 的值分別與 num obj1 obj2 的值指向同一個對象。函數內部可以對 a b c 進行修改可重新賦值。
function changeStuff(a, b, c) {a = a * 10; // 對 a 賦值,修改 a 的指向,新的值是 a * 10b.item = "changed"; // 因為 b 與 obj1 指向同一個對象,所以這里會修改原始對象 obj1.item 的內容c = {item: "changed"}; // 對 c 重新賦值,修改 c 的指向,其指向的對象內容是 {item: "changed"} }3 代碼分析
接下來讓我們再來分析一下代碼。
3.1 變量初始化
var num = 10; var obj1 = {item: "unchanged"}; var obj2 = {item: "unchanged"};
3.2 調用函數
changeStuff(num, obj1, obj2);
可以看到,變量 a 的值就是 num 值的拷貝,變量 b c 分別是 obj1 obj2 的指針的拷貝。
函數的參數其實就是函數作用域內部的變量,函數執行完之后就會銷毀。
3.3 執行函數體
a = a * 10; b.item = "changed"; c = {item: "changed"};
如圖所示,變量 a 的值的改變,并不會影響變量 num。
而 b 因為和 obj1 是指向同一個對象,所以使用 b.item = "changed"; 修改對象的值,會造成 obj1 的值也隨之改變。
由于是對 c 重新賦值了,所以修改 c 的對象的值,并不會影響到 obj2。
4. 結論
從上面的例子可以看出,對于 JS 來說:
基本類型是傳值調用
引用類型傳共享調用
傳值調用本質上傳遞的是變量的值的拷貝。
傳共享調用本質上是傳遞對象的指針的拷貝,其指針也是變量的值。所以傳共享調用也可以說是傳值調用。
所以《JavaScript 高級程序設計》說 JavaScript 參數傳遞都是按值傳參 也是有道理的。
本文同步于我的博客 https://github.com/nodejh/nodejh.github.io/issues/32
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的JavaScript 是传值调用还是传引用调用?的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 虎牙直播怎么投屏(虎牙手游直播怎么投屏电
- 下一篇: gradle idea java ssm