#hello,JS:05引用类型和深浅拷贝

一、基本类型VS引用类型

注: 这里的内存,为虚拟内存

1、引用类型:

  • 定义:保存在堆内存中的对象,变量中保存的实际上只是一个指针,这个指针执行内存中的另一个位置,由该位置保存对象
  • 包括:对象、数组、函数、正则

假设变量中有一个函数,函数内东西特别多(或者有一个对象,对象里的数据特别大),这里可选堆的空白处存放函数、对象的数据(随机选择未使用的空白堆,随意变大变小),放在堆中的均为引用类型

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

  • 定义:指的是保存在栈内存中的简单字段(成块排列,栈,允许放进去拿出来)
  • 包括:数值(number)、布尔值(boolean)、nullundefinedstring(在赋值传递中会以引用类型的方式来处理)

栈里面仍存有变量,只不过存放的不是数据,而是大数据地址,比如这个地址为0x0011,栈内存放的东西,均为可控、较小容量。从一个变量向另一个变量赋值基本类型时,会在该变量上创建一个新值,然后再把该值复制到为新变量分配的位置上。

3、实例一:基本类型

  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)基本类型的值被赋值给另一个变量,其实就是分配内存空间
一开始,a的值为 1 ,当使用a 来初始化b 时,b 值此时为1。但b中的1与a中的是完全独立的,该值只是a中的值的一个副本。说明在栈里变量再次变化,但这个两个变量可以参加任何操作而相互不受影响。

总结:
一个变量赋值给另一个变量时,其实是分配了一块新的内存空间。按照以上操作,基本类型在赋值操作后,事实上就a分配了一块新内存空间给b,两个变量是相互不受影响。

(2)基本类型的比较是值的比较 只有在它们的值相等的时候它们才相等。 当比较的两个值的类型不同的时候==运算符会进行类型转换,但是当两个值的类型相同的时候,即使是==也相当于是===

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

(3)基本类型的变量其实就是存放在栈区。结合以上,栈区指内存里的栈内存,但是栈区里包括了变量名和变量值。

4、实例二:(续上面的例子)引用类型

(1)引用类型的值是可变的
可为引用类型添加属性和方法,也可以删除其属性和方法。
看一下这个例子:一个为引用类型的变量赋值给另一个为引用类型的变量

  var obj2 = obj //控制台测试一下二者的值
 obj
//  {name:  "ruoyu", sex:  "male", age:  30, friend:  {…}} 
obj2
 // {name:  "ruoyu", sex:  "male", age:  30, friend:  {…}}

值是一样的。因为var obj2=obj,即通过obj的值(一个对象)赋值给obj2,那么obj2的值就是赋值后原本obj对应属性和值。作为一个引用类型,它被放在堆中。所以寻找obj2则在堆里找到,只是换了另一个名字为obj2

如图:


image

总结:
原本在栈中的对象分别指向了同一个堆,那么存放在堆中即为对象的内存地址。引用类型的赋值其实是对象保存在栈区地址指针的赋值,因此两个变量指向同一个对象,任何的操作都会相互影响。

(2)引用类型的比较是引用的比较
A、我们先看一下基本类型值的比较:

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

总结:
可以得出基本类型的比较:当两个比较值的类型相同(如字符串)的时候,相当于是用 === ,所以输出是true

B、再来看一下引用类型值的比较:

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

为什么是false?不相等呢?
放在栈中的变量 obj3obj4,声明前置均为undefined,当两者均被被声明值的时候,是两个对象,引用类型是引用访问,相当于在堆中分别开辟了两个空间,堆中会有对应的属性+值,此时这两个对象在堆中存的便是堆的地址。obj4obj3一样都开辟了新的堆空间,但是存放的地址也不一样。判断obj3是否与obj4相等,看了分析之后,便知道堆存放的地址并不同,二者也就不相等

如图:


image

(3)引用类型的值是同时保存在栈内存和堆内存中的对象

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

我们可以就此分析,函数function sum(),分别有变量sum和函数对象代码(为引用类型,已放在堆中)。之后sum赋值给sum2,即sum2事实上使用的是赋值后sum所指代堆的内存地址,即后续sumsum2共用了堆里的代码(变量的内存地址就像指针一样,通过JS自身引擎找到这个堆),一堆东西起了两个不同的名字

如图:


image

总结: js不同于其他语言,其不允许直接访问内存中的位置,即不能直接操作对象的内存空间,实际上,是操作对象的引用,所以引用类型的值是按引用访问的。

准确地说,引用类型的存储需内存的栈区(栈区是指内存里的栈内存)和堆区(堆区是指内存里的堆内存)共同完成,栈区内存保存变量标识符和指向堆内存中该对象的指针,然后,栈区内存地址也可以说是该对象在堆内存的地址。

二、引用类型的实际应用

1、函数的参数传递

第1个例子:

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

//等同于
 function  inc(){ 
       var n = arguments[0] 
       n++ 
  } 
 //在函数的一开始将var a = 10赋值进var n = arguments[0],  //n=arguments[0]=10,此时与n++为11并没有返回,所以与a并无关系  

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

✨第2个例子:

     function  incObj(obj){
     //var obj = o     //0x0001 
       obj.n++ 
        } 
   var o =  {n:  10}    //o = 0x0001  对其做声明,为一个对象  
   incObj(o)
   console.log(o)  

//等同于  
      function  incObj(){ 
                var obj =arguments[0] 
                 obj.n++  
          } 
 //incObj(o) 相当于function incObj(){var obj =arguments[0];obj.n++},
  //可知道obj=arguments[0]=o,相当于设obj为临时变量,而o= 0x0001  var o =  {n:  10}  incObj(o) console.log(o)

如图:


image

总结:
引用类型的本质,变量所存的是这个对象的内存地址指向堆,当去做赋值时是把这个地址进行一个赋值;当去访问的时候是通过这个地址去访问这个对象

✨第3个例子:

      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){}中的数组squireArr(arr)里的每一项变为原来的平方,即参数arr为数组里的值,用for循环进行操作,外界调用时,只需调用一次squireArr(arr),事实上数组squireArr(arr)操作就是对arr的操作

✨第4个例子:

        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、对象的深浅拷贝

针对这个例子:

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

想要创造一个新的b,那么就需要遍历原始a的每一项属性+值,用来获取成为新个体的b所需的东西,并一一对b进行改造,即从一无所有,改造成与a相似的新个体,此为克隆

如果在遍历的时候,b这个新个体只是遍历a的前半部分或者局部,那么这称之为浅拷贝,如:

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

而如果b是遍历原始a的每一项属性和值,但是b又是一个独立个体,与a不相关,当修改b的时候,a仍然不会发生变化,而这叫做深拷贝,如:

       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——对象

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,277评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,689评论 3 393
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,624评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,356评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,402评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,292评论 1 301
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,135评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,992评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,429评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,636评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,785评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,492评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,092评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,723评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,858评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,891评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,713评论 2 354

推荐阅读更多精彩内容

  • 从三月份找实习到现在,面了一些公司,挂了不少,但最终还是拿到小米、百度、阿里、京东、新浪、CVTE、乐视家的研发岗...
    时芥蓝阅读 42,243评论 11 349
  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 11,100评论 1 32
  • 2017年9月1日 星期五 雨 朋友圈有晒小朋友的开学季,也有晒大学的开学季,让不想起爸爸送上学的情景。 没...
    皇城山茶业阿娟阅读 129评论 0 0
  • 【书籍名称】 《小说课》[壹] ——折磨读者的秘密。 作者:许荣哲 【阅读感受】 1,作者选取精彩的小说片段,用来...
    风之壹把刀阅读 302评论 0 1
  • 内容简介 初期の代表作「聖伝」のイラスト集です、特に最初の方にある描き下ろしのイラストの綺麗さと言ったら???。た...
    可爱的鹡鸰阅读 903评论 7 8