今天從新看了一下《javascript高級程序設計》,其中講到了javascript中的值傳遞和值引用,因此就本身研讀了一下,可是剛開始沒有明白函數中的參數只有值傳遞,有的場景好像參數是以引用的方式傳遞的,可是實際上卻不是,那究竟是怎麼回事,或者是函數中的傳值是值傳遞仍是值引用呢,下面來對書上給出的例子作一個圖解,這樣可以更好的解釋這個問題。有頓悟的感受。javascript中貌似共有8種數據類型,包括了字符串類型,數值類型,布爾類型,undefined類型,null類型,對象,數組,函數;
1. 值傳遞,書上呀,各類的都說基本類型的都是值傳遞;javascript
基本類型:Number Boolean String Undefined Null 這5種就是javascript種的基本數據類型了;java
//基本類型演示: var a = 1; //數值型 var b = true; //布爾型 var c = undefined; //undefined類型 var d = null; //null類型 var e = 'helloworld'; //字符串類型 var a1 = a; a1 = 2; console.log(a1); // 2 console.log(a); // 1 var b1 = b;b1 = false; console.log(b1); //false console.log(b); //true var c1 = c; c1 = ' '; console.log(c1); //'' console.log(c); //undefined var d1 = d; d1 = ''; console.log(d1); //'' console.log(d); //null var e1 = e; e1 = 'i love you!'; console.log(e1); //'i love you' console.log(e); //'helloworld'
上面代碼的結果說明了,以上所列幾種都是值傳遞方式的,即原始值不會被改變,原變量與新變量之間在完成賦值,建立原始值的副本以後就沒有任何關聯了;數組
2. 值引用,書上呀,各類的都說引用類型的都是值引用;函數
引用類型:Object Array Fucntion spa
//1. 對象 var a = {}; var b = a; b.name = '我是b'; console.log(a.name); //'我是b' //2. 數組 var c = []; var d = c; d[0] = '2'; console.log(c); //['2'] //函數 var e = function(){ console.log('我是e函數'); } var f = e; f = function(){ console.log('我是f函數');} console.log(e); functioon(){ console.log('我是f函數');}
新變量中的行爲所產生的變化,會同時在原始變量上表現出來,說明原變量在賦值給新變量的時候是將其引用賦值給了新變量,這樣他們同時指向同一個引用,這樣兩個變量中的任何一個的動做所引發的變化都會同時在另外一個變量中反應出來。設計
3. 函數傳遞參數,書上呀,各類的都說是值傳遞;對象
先來簡單的看看這個例子:blog
function setName(obj){ obj.name = 'Nicholas'; } var person = new Object(); setName(person); console.log(person.name); //‘Nicholas'
行爲:ip
1. 在內存中建立一個對象,並把這個對象的引用賦值給person變量
2. 將變量person傳入函數setName()內存
3. 函數setName()爲obj增長一個name屬性,並賦值爲’Nicholas‘
4. 訪問person的name屬性,並將其打印在控制檯
在訪問person的name屬性時,獲得的結果是'Nicholas'。可是,這個場景讓人感受好像是值引用;看看下圖是程序運行時在內存中的一些變化:
解析:由於函數中的參數是以值傳遞方式傳遞的,obj是函數setName()裏面的一個局部變量,是在調用函數的時候才發生參數的傳遞的,調用函數時,person將其拷貝了一份並賦值給了obj,二者是獨立的,(由於 person是對象類型的數據,其本質是包含了建立它的對象的引用,在賦值給obj時,賦給obj的也是其原始對象的引用),可是因爲二者指向的是同一個引用,因此在爲obj添加name屬性,並賦值 爲「Nicholas」時,操做的不是obj自己的屬性,而是操做的其引用Object,而person引用的也是同一個Object,因此在訪問person的name屬性時,根據javascript 面向對象的原型鏈規則,首先會檢查person 自己是否有name屬性,由於person沒有name屬性,因此繼續往上搜索,在其引用的Object上面找到了name屬性,因此person.name返回的是其引用的name屬性,因此person.name的值是「Nicholas」。
再來看看下面的這個例子
function setName(obj){ obj.name = 'Nicholas'; obj = new Object(); obj.name = 'Grey'; } var person = new Object(); setName(person); console.log(person.name); //‘Nicholas'
行爲:
1. 在內存中建立一個對象,並把這個對象的引用賦值給person變量
2. 將變量person傳入函數setName()
3. 函數setName()爲obj增長一個name屬性,並賦值爲’Nicholas‘
4. 在內存中再新建一個對象,並把這個對象的引用賦值給person變量
5. 爲obj增長了一個name屬性,並賦值爲’Grey‘
4. 訪問person的name屬性,並將其打印在控制檯
解析:上個例子中解釋了爲何person.name爲’Nicholas‘,這個例子在其基礎上添加了兩行代碼,來看看這兩行代碼作了什麼:
在obj增長了name屬性後,又在內存中建立了一個新的Object,並且把它賦值給了obj,根據引用類型的賦值,是把這個新的Object的引用傳遞給了obj,這點是毫無疑問的。此時obj與原來的對象的引用被新的對象的引用所取代,因此obj實際上與person已經沒有任何關係了,此時將obj的name屬性值改成「Grey」,其實name屬性是不存在的,因此作的不是簡單的改變屬性的值,而是添加屬性,並添加屬性值。這個行爲與person的引用Object是沒有任何關係的,因此person的引用Object中的name也沒有發生變化,仍是‘Nicholas’。當setName()函數執行完,obj這個局部變量也就不復存在了,可是這對於person來講也是無所謂的。因此當setName()函數執行結束後,訪問person的name屬性時,如上仍是其引用Object的name屬性,因此仍是‘Nicholas’。
若是按照上例的猜測,函數中的參數是值引用方式,那麼咱們能夠先在腦海裏運行一下,會獲得什麼結果呢?
console.log(person.name) 會在控制檯上打印出 ’Grey‘,可是顯然結果不是這樣的,最終輸出的是’Nicholas‘,那是否是咱們的猜測錯了,函數的參數是值傳遞方式呢?看看下面的圖
因此:基本類型是值傳遞,引用類型是值引用,函數參數是值傳遞的說法是徹底正確的。
感受還有點兒細節須要在扣一下,後面在改改。