基本数据类型和引用数据类型的区别

2,651 阅读6分钟

基本类型和引用数据类型的区别

1、堆和栈

1、栈(stack):栈会自动分配内存空间,会自动释放,存放基本类型,简单的数据段占据固定大小的空间。

2、堆(heap):动态分配的内存,大小不定也不会自动释放,存放引用类型,指那些可能由多个值构成的对象,保存在堆内存中,包含引用类型的变量,实际上保存的不是变量本身,而是指向该对象的指针。

2、基本数据类型和引用数据类型

1、基本数据类型

1、基本数据类型:数字、字符串、布尔、null、undefined、Symbol

2、基本数据类型特点:

1) 占用空间固定,保存在栈中(当一个方法执行时,每个方法都会建立自己的内存栈,在这个方法内定义的变量将会逐个放入这块栈内存里,随着方法的执行结束,这个方法的内存栈自然销毁。因此所有在方法中定义的变量都是放在栈内存中的,栈中存储的是基础变量以及一些对象的引用变量,基础变量的值是存储在栈中,而引用变量存储在栈中的是指向堆中的数组或对象的地址,所以修改引用类型会影响到其他指向这个地址的引用变量。)

2) 保存与复制的是值本身

3) 使用typeof检测数据的类型

4) 基本数据类型是值类型

2、引用数据类型

1、引用数据类型:Object(JS中除基本数据类型以外的都是对象,数据是对象,函数是对象,正则表达式是对象)、Function、Array、Data

2、引用数据类型特点:

1) 占用空间不固定,保存在堆中(当我们在程序中创建一个对象时,这个对象将被保存到运行时数据区中,以便反复利用(因为对象的创建成本通常较大),这个运行时数据区就是堆内存。堆内存中的对象不会随方法的结束而销毁,即便方法结束后,这个对象还可能被另一个引用变量所引用(方法的参数传递时常见),则这个对象依然不会被销毁,只有当一个对象没有任何引用变量引用他时,系统的垃圾回收机制才会在核实的时候回收他。)

2) 保存与复制的是指向对象的一个指针

3) 使用instanceof检测数据类型

4) 使用new()方法构造出的对象是引用型

3、基本数据类型存放在栈中

1、基本数据类型是指存放在栈中的简单数据段,数据大小确定,内存空间大小可分配,它们是直接按值存放的,所以可以直接按值访问。

  var a = 1;
  var b = a;
  b = 2;
  console.log(a);             //1
  console.log(b);             //2
  
  
  var c = true;
  var d = c;
  d = false;
  console.log(c);             //true
  console.log(d);             //false

第一个例子:初始栈内存中只有a(a=1),栈内存中添加b并把a的值赋值给b(a=1,b=1),栈内存中b的值改变为2(a=1,b=2)。b获取的值是a值的一份拷贝,虽然两个变量的值相等,但是两个变量保存两个不同的基本数据类型值。b只是保存了a复制的一个副本。所以当b的值改变时,a的值依然是1。

第二个例子:两个布尔变量c、d都是基本数据类型,同样保存两个不同的基本数据类型值,d保存c复制的一个副本。

2、基本数据类型不可以添加属性和方法

   var a = 'change';
   a.age = '18';
   a.method = function(){console.log(name)};
   
   console.log(a.age);                  //undefined
   console.log(a.method);               //undefined
4、引用数据类型存放在堆中

1、引用数据是存放在堆内存中的对象,变量其实是保存在栈内存中的一个指针(保存的是堆内存中的引用地址),这个指针指向堆内存

2、引用数据类型是栈内存中保存的实际上是对象在堆内存中的引用地址。通过这个地址可以快速查找到保存中堆内存的对象。

   var a = {
     name:'张三',
     age:'18',
   }
   
   var b = a;
   b.name = '小赵';
   
   console.log(a);           //{name:'小赵',age:'18'}

把a赋值给b,就相当于把a的内存地址指向b,即a和b指向同一内存地址,改变了b就相当于改变了a

3、引用数据类型可以添加属性和方法

  var person = {}
  person.name = '小张';
  person.say = function(){console.log('hello')};
  
  console.log(person.name);            //'小张'
  console.log(person.say);             //function(){console.log('hello')};

4、引用数据类型的赋值是对象引用

  var a = {}
  var b = a;
  a.name = "小赵";
  console.log(a.name);              //小赵
  console.log(b.name);              //小赵
  
  
  b.age = 18;
  console.log(a.age);               //18
  console.log(b.age);               //18

当从一个变量向另一个变量赋值引用类型的值时,同样也会将存储在变量中的对象的值复制一份放到为新变量分配的空间中。引用数据类型保存在变量中的是对象在堆内存中的地址。所以,与基本数据类型的简单赋值不同,这个值的副本实际上是一个指针,而这个指针指向存储在堆内存中的一个对象。那么赋值操作后,两个变量都保存了同一个对象地址,而这两个地址指向了同一个对象。因此,改变其中任何一个变量都会互相影响。

因此,引用类型的赋值其实是对象保存在栈区地址指针的赋值,所以两个变量指向同一个对象,任何的操作都会互相影响。

   var a = [1,2,3,4,5];
   var b = a;      //传址,对象中传给变量的数据是引用类型,会存放在栈中
   var c = a[0];   //传值,把对象中的属性/数组中的数组项赋值给变量,这是变量c是基本数据类型,
                   //存储在栈内存中;改变栈中的数据不会影响堆中的数据
   console.log(b);        //[1,2,3,4,5]
   console.log(c);        //1
   
   b[4] = 6;
   c = 7;
   console.log(a[4]);     //6
   console.log(a[0]);     //1

从这个例子可以得知,当改变b中的数据时,a中的数据也发生了变化,但是改变c的数据值时,a却没有发生改变。这就是传值和传址的区别。因为a是数组,属于引用类型,所以它赋给b的时候传的是栈中的地址(相当于新建了一个不同名的指针),而不是堆内存中的对象。而c仅仅是从a堆内存中获取的一个数据值,并保存在栈中。所以b修改的时候会根据地址回到a堆中修改,c直接在栈中修改,并且不能指向a堆内存中。