#hello,JS:05引用類型和深淺拷貝

一、基本類型VS引用類型

注: 這里的內(nèi)存,為虛擬內(nèi)存

1、引用類型:

  • 定義:保存在堆內(nèi)存中的對(duì)象,變量中保存的實(shí)際上只是一個(gè)指針,這個(gè)指針執(zhí)行內(nèi)存中的另一個(gè)位置,由該位置保存對(duì)象
  • 包括:對(duì)象、數(shù)組、函數(shù)、正則

假設(shè)變量中有一個(gè)函數(shù),函數(shù)內(nèi)東西特別多(或者有一個(gè)對(duì)象,對(duì)象里的數(shù)據(jù)特別大),這里可選堆的空白處存放函數(shù)、對(duì)象的數(shù)據(jù)(隨機(jī)選擇未使用的空白堆,隨意變大變?。旁诙阎械木鶠橐妙愋?/p>

2、基本類型(值類型):

  • 定義:指的是保存在棧內(nèi)存中的簡(jiǎn)單字段(成塊排列,棧,允許放進(jìn)去拿出來)
  • 包括:數(shù)值(number)、布爾值(boolean)、null、undefinedstring(在賦值傳遞中會(huì)以引用類型的方式來處理)

棧里面仍存有變量,只不過存放的不是數(shù)據(jù),而是大數(shù)據(jù)地址,比如這個(gè)地址為0x0011,棧內(nèi)存放的東西,均為可控、較小容量。從一個(gè)變量向另一個(gè)變量賦值基本類型時(shí),會(huì)在該變量上創(chuàng)建一個(gè)新值,然后再把該值復(fù)制到為新變量分配的位置上。

3、實(shí)例一:基本類型

  var a 
  var b 
  var obj 
  var obj2
 
 a =  1;
 b =  2; 
 var obj =  { 
     name:  'xiaoqin', 
     sex:  'male', 
      age:  30,
      friend:  { 
name:  'hello', age:  100 
      } 
 }  
var newObj =  {}
     b = a; 
     console.log(b)
  //返回1

如圖:


image

(1)基本類型的值被賦值給另一個(gè)變量,其實(shí)就是分配內(nèi)存空間
一開始,a的值為 1 ,當(dāng)使用a 來初始化b 時(shí),b 值此時(shí)為1。但b中的1與a中的是完全獨(dú)立的,該值只是a中的值的一個(gè)副本。說明在棧里變量再次變化,但這個(gè)兩個(gè)變量可以參加任何操作而相互不受影響。

總結(jié):
一個(gè)變量賦值給另一個(gè)變量時(shí),其實(shí)是分配了一塊新的內(nèi)存空間。按照以上操作,基本類型在賦值操作后,事實(shí)上就a分配了一塊新內(nèi)存空間給b,兩個(gè)變量是相互不受影響。

(2)基本類型的比較是值的比較 只有在它們的值相等的時(shí)候它們才相等。 當(dāng)比較的兩個(gè)值的類型不同的時(shí)候==運(yùn)算符會(huì)進(jìn)行類型轉(zhuǎn)換,但是當(dāng)兩個(gè)值的類型相同的時(shí)候,即使是==也相當(dāng)于是===。

var a = 1;
var b = true;
console.log(a == b);//true

(3)基本類型的變量其實(shí)就是存放在棧區(qū)。結(jié)合以上,棧區(qū)指內(nèi)存里的棧內(nèi)存,但是棧區(qū)里包括了變量名和變量值。

4、實(shí)例二:(續(xù)上面的例子)引用類型

(1)引用類型的值是可變的
可為引用類型添加屬性和方法,也可以刪除其屬性和方法。
看一下這個(gè)例子:一個(gè)為引用類型的變量賦值給另一個(gè)為引用類型的變量

  var obj2 = obj //控制臺(tái)測(cè)試一下二者的值
 obj
//  {name:  "ruoyu", sex:  "male", age:  30, friend:  {…}} 
obj2
 // {name:  "ruoyu", sex:  "male", age:  30, friend:  {…}}

值是一樣的。因為var obj2=obj,即通過obj的值(一個(gè)對(duì)象)賦值給obj2,那么obj2的值就是賦值后原本obj對(duì)應(yīng)屬性和值。作為一個(gè)引用類型,它被放在堆中。所以尋找obj2則在堆里找到,只是換了另一個(gè)名字為obj2

如圖:


image

總結(jié):
原本在棧中的對(duì)象分別指向了同一個(gè)堆,那么存放在堆中即為對(duì)象的內(nèi)存地址。引用類型的賦值其實(shí)是對(duì)象保存在棧區(qū)地址指針的賦值,因此兩個(gè)變量指向同一個(gè)對(duì)象,任何的操作都會(huì)相互影響。

(2)引用類型的比較是引用的比較
A、我們先看一下基本類型值的比較:

var obj3 =  '{name: 'hello'}';  
var obj4 =  '{name: 'hello'}';
 console.log( obj3 == obj4 ); 
 // true

總結(jié):
可以得出基本類型的比較:當(dāng)兩個(gè)比較值的類型相同(如字符串)的時(shí)候,相當(dāng)于是用 === ,所以輸出是true

B、再來看一下引用類型值的比較:

var obj3 =  {name:  'hello'}
var obj4 =  {name:  'hello'}
 obj3 === obj4   
 //返回false,說明二者并不相等

為什么是false?不相等呢?
放在棧中的變量 obj3、obj4,聲明前置均為undefined,當(dāng)兩者均被被聲明值的時(shí)候,是兩個(gè)對(duì)象,引用類型是引用訪問,相當(dāng)于在堆中分別開辟了兩個(gè)空間,堆中會(huì)有對(duì)應(yīng)的屬性+值,此時(shí)這兩個(gè)對(duì)象在堆中存的便是堆的地址。obj4obj3一樣都開辟了新的堆空間,但是存放的地址也不一樣。判斷obj3是否與obj4相等,看了分析之后,便知道堆存放的地址并不同,二者也就不相等

如圖:


image

(3)引用類型的值是同時(shí)保存在棧內(nèi)存和堆內(nèi)存中的對(duì)象

function  sum(){ 
              console.log('sum...')
     }  
var sum2 = sum; 
 sum2()  
//返回sum... 二者是相等的

我們可以就此分析,函數(shù)function sum(),分別有變量sum和函數(shù)對(duì)象代碼(為引用類型,已放在堆中)。之后sum賦值給sum2,即sum2事實(shí)上使用的是賦值后sum所指代堆的內(nèi)存地址,即后續(xù)sumsum2共用了堆里的代碼(變量的內(nèi)存地址就像指針一樣,通過JS自身引擎找到這個(gè)堆),一堆東西起了兩個(gè)不同的名字

如圖:


image

總結(jié): js不同于其他語言,其不允許直接訪問內(nèi)存中的位置,即不能直接操作對(duì)象的內(nèi)存空間,實(shí)際上,是操作對(duì)象的引用,所以引用類型的值是按引用訪問的。

準(zhǔn)確地說,引用類型的存儲(chǔ)需內(nèi)存的棧區(qū)(棧區(qū)是指內(nèi)存里的棧內(nèi)存)和堆區(qū)(堆區(qū)是指內(nèi)存里的堆內(nèi)存)共同完成,棧區(qū)內(nèi)存保存變量標(biāo)識(shí)符和指向堆內(nèi)存中該對(duì)象的指針,然后,棧區(qū)內(nèi)存地址也可以說是該對(duì)象在堆內(nèi)存的地址。

二、引用類型的實(shí)際應(yīng)用

1、函數(shù)的參數(shù)傳遞

第1個(gè)例子:

function  inc(n){ 
          n++; 
        }  
var a =  10; 
 inc(a)
 console.log(a)  
//返回10  

//等同于
 function  inc(){ 
       var n = arguments[0] 
       n++ 
  } 
 //在函數(shù)的一開始將var a = 10賦值進(jìn)var n = arguments[0],  //n=arguments[0]=10,此時(shí)與n++為11并沒有返回,所以與a并無關(guān)系  

var a =  10  
inc(a)
 console.log(a) 
 //返回10

?第2個(gè)例子:

     function  incObj(obj){
     //var obj = o     //0x0001 
       obj.n++ 
        } 
   var o =  {n:  10}    //o = 0x0001  對(duì)其做聲明,為一個(gè)對(duì)象  
   incObj(o)
   console.log(o)  

//等同于  
      function  incObj(){ 
                var obj =arguments[0] 
                 obj.n++  
          } 
 //incObj(o) 相當(dāng)于function incObj(){var obj =arguments[0];obj.n++},
  //可知道obj=arguments[0]=o,相當(dāng)于設(shè)obj為臨時(shí)變量,而o= 0x0001  var o =  {n:  10}  incObj(o) console.log(o)

如圖:


image

總結(jié):
引用類型的本質(zhì),變量所存的是這個(gè)對(duì)象的內(nèi)存地址指向堆,當(dāng)去做賦值時(shí)是把這個(gè)地址進(jìn)行一個(gè)賦值;當(dāng)去訪問的時(shí)候是通過這個(gè)地址去訪問這個(gè)對(duì)象

?第3個(gè)例子:

      function  squireArr( arr ){  
              //var arr = 0x0011  
                 for(var i =  0; i < arr.length; i++){ 
                         arr[i]  = arr[i]  * arr[i]; 
              }
       } 
                 var arr =  [2,1,3,6] 
                 squireArr(arr) 
                 console.log(arr) 
           //(4) [4, 1, 9, 36]

即把function squireArr(arr){}中的數(shù)組squireArr(arr)里的每一項(xiàng)變?yōu)樵瓉淼钠椒剑磪?shù)arr為數(shù)組里的值,用for循環(huán)進(jìn)行操作,外界調(diào)用時(shí),只需調(diào)用一次squireArr(arr),事實(shí)上數(shù)組squireArr(arr)操作就是對(duì)arr的操作

?第4個(gè)例子:

        function  squireArr2( arr ){ 
                  var newArr =  []; 
                   for(var i =  0; i < arr.length; i++){ 
                              newArr[i]  = arr[i]  * arr[i]; 
                       } 
                        return newArr; 
                   }  
                  var arr2 =  squireArr2(arr) 
                  console.log(arr2)  //返回(4) [16, 1, 81, 1296] 

             arr 
            // (4)  [4,  1,  9,  36] 
arr2 -->  (4)  [16,  1,  81,  1296]

2、對(duì)象的深淺拷貝

針對(duì)這個(gè)例子:

     var obj;  
    var obj2;  
     var obj =  {
              name:  'ruoyu',
              sex:  'male',
              age:  30, 
              friend:  { 
                name:  'hello',
                age:  100 
                   } 
           }  
          var obj2 = obj;

想要?jiǎng)?chuàng)造一個(gè)新的b,那么就需要遍歷原始a的每一項(xiàng)屬性+值,用來獲取成為新個(gè)體的b所需的東西,并一一對(duì)b進(jìn)行改造,即從一無所有,改造成與a相似的新個(gè)體,此為克隆。

如果在遍歷的時(shí)候,b這個(gè)新個(gè)體只是遍歷a的前半部分或者局部,那么這稱之為淺拷貝,如:

      function  shallowCopy(oldObj)  { 
                   var newObj =  {};  
                    for(var i in oldObj)  { 
                        if(oldObj.hasOwnProperty(i))  {
                                newObj[i]  = oldObj[i];  
                         } 
                  }
                return newObj; 
           }

而如果b是遍歷原始a的每一項(xiàng)屬性和值,但是b又是一個(gè)獨(dú)立個(gè)體,與a不相關(guān),當(dāng)修改b的時(shí)候,a仍然不會(huì)發(fā)生變化,而這叫做深拷貝,如:

       function  deepCopy(oldObj)  { 
                  var newObj =  {}; 
                   for(var key in oldObj)  { 
                           if(typeof oldObj[key]  ===  'object')  {
                              newObj[key]  =  deepCopy(oldObj[key]); 
                          }else{ 
                             newObj[key]  = oldObj[key]; 
                                    } 
                       } 
                        return newObj;  
                  }

json——string——對(duì)象

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 從三月份找實(shí)習(xí)到現(xiàn)在,面了一些公司,掛了不少,但最終還是拿到小米、百度、阿里、京東、新浪、CVTE、樂視家的研發(fā)崗...
    時(shí)芥藍(lán)閱讀 42,793評(píng)論 11 349
  • Swift1> Swift和OC的區(qū)別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴(yán)謹(jǐn) 對(duì)...
    cosWriter閱讀 11,641評(píng)論 1 32
  • 2017年9月1日 星期五 雨 朋友圈有曬小朋友的開學(xué)季,也有曬大學(xué)的開學(xué)季,讓不想起爸爸送上學(xué)的情景。 沒...
    透過樹葉的一抹陽光閱讀 163評(píng)論 0 0
  • 【書籍名稱】 《小說課》[壹] ——折磨讀者的秘密。 作者:許榮哲 【閱讀感受】 1,作者選取精彩的小說片段,用來...
    風(fēng)之壹把刀閱讀 373評(píng)論 0 1
  • 內(nèi)容簡(jiǎn)介 初期の代表作「聖伝」のイラスト集です、特に最初の方にある描き下ろしのイラストの綺麗さと言ったら???。た...
    可愛的鹡鸰閱讀 992評(píng)論 7 8

友情鏈接更多精彩內(nèi)容