JavaScript之按值傳遞

南波發表於2018-10-24

本文共 1200 字,讀完只需 5 分鐘

概述

引數的傳遞分為按值傳遞按引用傳遞,而 JavaScript 中引數的傳遞只有按值傳遞

ECMAScript 中所有函式的引數都是按值傳遞的。

所謂按值傳遞就是:

把函式外部的值複製給函式內部的引數,就和把值從一個變數複製到另一個變數一樣。 -- 《JavaScript 高階程式設計》

我們知道 JS 中,既有基本資料型別又有引用資料型別,那麼二者的按值傳遞有什麼樣的區別呢?

先給結論

在向引數傳遞基本資料型別時,被傳遞的值會被複制給一個區域性變數(arguments 類陣列物件中的一個元素)。在向引數傳遞引用資料型別時,會把這個值的記憶體地址賦給一個區域性變數。

一、資料型別

在 JS 中,資料型別分為基本型別和引用型別。

其中基本型別包括:number, string, boolean, undefined, null, Symbol(es 6 新增)。基本型別的值是儲存在棧記憶體當中的。

基本資料型別的值本身是不會改變的。

let num1 = 5;
let num2 = num1;
複製程式碼

JavaScript之按值傳遞

將儲存著原始值的變數 num1 賦值給 num2 後,會將原始值 num1 的副本賦值給新變數 num2, 此後這兩個變數是完全獨立的,他們只是擁有相同的值而已,是完全獨立的拷貝,互不干涉。

引用資料型別包括:Function, Array, Object 等等除了基本資料型別之外的資料。引用資料型別是儲存在堆記憶體當中的。

JS 不允許直接操作物件的記憶體空間,所以引用資料型別是通過儲存在變數處的值,也就是一個指標(point),指向儲存物件的記憶體地址,從而進行訪問的。

let obj1 = new Object();

var obj2 = obj1;
複製程式碼

JavaScript之按值傳遞

當把引用型別的變數 obj1 賦給另一個變數 obj2 後,obj2 接受的其實是引用型別資料的記憶體地址指標。所以,判斷兩個引用型別是否相等,其實比較的是記憶體地址是否相等。

二、按值傳遞

var num = 1;
function foo(param) {
    param = 2;
}
foo(num);
console.log(num); // num 值仍為1, 並沒有受 param = 2 賦值影響
複製程式碼

以上程式碼:

按值傳遞每次傳遞引數時,都會拷貝一份副本到函式內部,拷貝前後的兩個值互不影響。

二、“按引用傳遞”

var obj = {
    num: 1
};

function foo(o) {
    o.num = 2;
    console.log(obj.num);  // 2
}

foo(obj);
console.log(obj.num); // 2
複製程式碼

以上程式碼,foo 函式把 obj 物件作為實參,執行完畢後把 obj 物件的 num 屬性給改變了,說明引數 o 物件 和 外部變數 obj 物件是同一個物件。說好的按值傳遞呢,怎麼還是把原來的物件給改變了呢。

三、按共享傳遞

再看下面這段程式碼:

var obj = {
    num: 1
};

function foo(o) {
    o = 100;
}

foo(obj);
console.log(obj.num);  // 1
複製程式碼

如果是按引用傳遞的話,按理來說 obj 物件會被改變會 100 才對。

準確的說,JS中的基本型別按值傳遞,物件型別按共享傳遞的(call by sharing,也叫按物件傳遞、按物件共享傳遞)

在共享傳遞中對函式形參的賦值,不會影響實參本身的值。

所以,形參引用的物件是同一個,由於物件是可變的(mutable),修改形參中物件的屬性值,會影響到原本物件的屬性值。

按引用傳遞是傳遞物件的引用,而按共享傳遞是傳遞物件的拷貝的副本,所以副本本身無法直接修改。而拷貝副本也是一種拷貝,所以也被認為是按值傳遞。

基本型別本身是按值傳遞,具有不可變性(immutable),對基本型別的修改,實質上都是在棧記憶體中建立了新的值。

複習鞏固:

var obj = { num : 0 };
obj.num = 100;
var o = obj;
o.num = 1;
obj.num; // 1, 被修改
o = true;
obj.num; // 1,  o 是物件的一個拷貝,對 o 本身的修改,不會改變 obj 物件本身的值。
複製程式碼

總結

JavaScript 中引數的傳遞只有按值傳遞,而對於引用型別的傳遞,是一種共享傳遞,傳遞的是資料型別的拷貝副本,雖然引用的是同一個物件,但是無法通過改變形參來改變實參本身。

JS 中把這種拷貝也認為是按值傳遞。

歡迎關注我的個人公眾號“謝南波”,專注分享原創文章。

JavaScript之按值傳遞

掘金專欄 JavaScript 系列文章

  1. JavaScript之變數及作用域
  2. JavaScript之宣告提升
  3. JavaScript之執行上下文
  4. JavaScript之變數物件
  5. JavaScript原型與原型鏈
  6. JavaScript之作用域鏈
  7. JavaScript之閉包
  8. JavaScript之this
  9. JavaScript之arguments
  10. JavaScript之按值傳遞
  11. JavaScript之例題中徹底理解this
  12. JavaScript專題之模擬實現call和apply

相關文章