1. 程式人生 > >JS引數傳遞(值傳遞和引用傳遞)

JS引數傳遞(值傳遞和引用傳遞)

前端紅寶書第一遍看過去之後,相當多的東西都忘記了,第二遍看的時候,也開始注意到一些細節的東西以及理解不到位的地方。

書P66 一個加粗框中寫到:ECMAScript中所有引數傳遞都是值,不可能通過引用傳遞引數

當時看過沒有怎麼仔細想過,一知半解吧,今天理解的更加深一些。當然也是昨天做了個題目就因為這個掉坑裡一時沒爬出來!

訪問變數有按值和按引用兩種方式,而引數只能按值傳遞。

一、基本資料型別: Number, String等都是按值傳遞這個好理解,當時我也就理解這個吧。

var a = 10
function add(num){
	num+=10
}
add(a)
console.log(a)

如上面的程式碼,執行add(a)的時候,會複製一份值給函式的arguments這偽陣列,當然同時也是num這個命名引數。arguments[0] = num,函式內部對num進行了加操作,num = 20了。但是外部最後輸出a時,仍然是10,說明a與函式內部的num互不干擾。其實此時可以把命名引數看成是區域性引數,函式執行結束,它也隨之銷燬。而它是按值傳遞的,複製了記憶體中的一份值給num。所以內部對num的操作不影響全域性變數a的值

二、基本型別的值好理解,但是JS中物件型別的就不怎麼好理解了,反正令我覺得有點繞;

先看例子:

var student = new Object()
function addNum (obj) {
	obj.num = '23'
}

addNum(student)

console.log(student.num)

如上,首先定義了一個student物件,然後通過函式給該物件添加了num學號的屬性值:23。之後列印輸出的時候,很奇怪,居然給student物件新增上了屬性,並且影響到了外部物件,這傳值明顯就是引用傳遞啊,函式內部obj和student都指向堆中同一個物件。就是引用傳遞嘛。但是又和紅寶書上寫的矛盾:不可能通過引用傳遞引數

??這就莫名奇妙了。

查閱了很多大神寫的說明,但是感覺都不能夠說明,真是非常難理解。我覺得是不是這樣理解:以上述函式為例,我把student物件例項作為引數傳遞給obj時,是將student所指物件的地址複製了一份給obj,這樣obj空間所存的地址和student就都指向了共同的儲存空間。而如果是引用傳遞的話,obj是沒有空間的,它將會和student公用一個空間,這個空間就儲存著一個指向student例項物件的地址。

下圖為說明:傳值 

下圖為引用傳遞的情況:

為了驗證,我們來看下面的程式碼:

var student= new Object()

function addName(obj){
	obj.name = 'roddan'
	obj = new Object()
	obj.name = 'liujiang'
}

addName(student)

console.log(student)

注意: 對於物件等引用型別的資料,“=”操作會覆蓋掉原來的地址值。

分析: 如上程式碼,我們先將student=》 obj物件,然後為它增加了name屬性為:"roddan"。之後我們將obj指向了一個新的物件,然後為它同樣增加了"name"屬性值:"liujiang"。但是我們最後輸出的仍然是“roddan”,這裡就不貼圖了,程式碼簡單,各位自己驗證就好。那麼問題來了,如果是引用傳遞的話,如上面圖所示,在將obj重新指向新的物件時,會等同於在執行student空間所儲存的地址"0x41",如果將它覆蓋掉的話,外部最後列印student時,應該列印的物件是重新定向的name:“liujiang”的這個物件。但事實是打印出來的是name: "roddan"的這個物件。也就是說,obj複製了一份地址儲存在自己的空間,當僅對它本身操作時,它指向的是student共同指向的物件。但是當我操作 obj = new Object()時,它自己空間儲存的地址將會被新的物件的儲存地址所覆蓋,因為是傳值不是引用,所以它不會影響到student空間所儲存的地址,故最後雖然對obj的name屬性重新賦值,但也絲毫不影響之前賦值結果,最終輸出的仍為"roddan"。

這就讓我想起了昨晚一位網友做題時碰到的問題: 

題目如下: 附上網址: 點選開啟連結

/**
 * @param {number[]} nums
 * @param {number} k
 * @return {void} Do not return anything, modify nums in-place instead.
 */
var rotate = function(nums, k) {
    
};

有位同學的程式碼是這樣的:

var rotate = function(nums, k) {
   var len = nums.length;
   var newArr = nums.splice(0, len-k)
   var temp = nums.concat(newArr)
   nums = temp
};

var a = [1,2,3,4,5,6,7]

rotate(a, 3)
console.log(a)

而最終網站提示:


當時他找不到原因,應該是這樣啊,a陣列最後輸出怎麼就只有[5,6,7]了呢,其實這裡就是函式傳參只傳值而不是引用理解的不透徹。nums中複製了一份指向a陣列的地址,你只能對其本身進行操作,也就等同於在運算元組a。但是當你來了句:

nums = “某某” 的時候,這問題就來了。“=”會覆蓋掉nums中儲存的指向a陣列的地址。並將其新地址指向temp.concat(newArr)這個新陣列,那麼此刻,nums已經完全是個區域性變量了,而且地址的改變使你無論對它進行什麼操作都不會影響到a陣列上面來。故最後輸出的就只是之前對a陣列擷取剩下的部分[5,6,7]。

正確的做法就是不要改變nums中儲存的指向a陣列的地址,這樣對nums的操作就會影響到a陣列上面來。達到題目的要求。