JavaScript - 基本类型与引用类型值

本文讲述了在JavaScript中,基本类型与引用类型值的概念及区别


引入概念:基本类型和引用类型

  1. 可以感受到,JS的变量及其松散,那么,正是 JS变量松散 的本质,决定了:
    JS变量名只是 一个在特定的时间用于保存特定值的一个名字 而已,也就是说,变量的值及其数据类型可以在脚本的生命周期内改变 ,尽管这个功能看起来有趣、强大,但是JS变量实际上是比较复杂,
  2. ECMAScirpt 变量有两种不同的数据类型:基本类型引用类型,另外还有其他的叫法,比如:原始类型和对象类型拥有方法的类型和不能拥有方法的类型 等等
  3. 将一个值赋值给变量时,解析器 必须确定这个值是基本类型值还是引用类型值
    基本类型 指的是简单的数据段,而 引用类型 指的是可能由多个值构成的对象
    基本类型:undefinednullstringnumberbooleansymbo(ES6)
    引用类型:ObjectArrayRegExpDateFunction

两种类型的区别

  1. 存储:
    • 基本类型的值是存放在 栈区 的,即内存中的栈内存
      • 假如有以下变量:
        var name = 'jozo';
        var city = 'guangzhou';
        var age = 22;
      • 那么他们的存储结构如下:(栈区包括了变量的标识符和值)
        在这里插入图片描述
    • 引用类型的值是同时保存在 栈内存和堆内存
      • 假如有以下对象:
        var person1 = {name:'jozo'};
        var person2 = {name:'xiaom'};
        var person3 = {name:'xiaoq'};
      • 那么他们的存储结构如下:
        在这里插入图片描述
  2. 访问:
    • 基本类型的值是 按值访问 的,因为可以操作保存在变量中的实际的值。
    • 引用类型的值是 按引用访问 的,因为引用类型的值是保存在内存中的对象,而与其他语言不同的是,JavaScript 不允许直接访问内存中的位置,即不可以直接操作对象的内存空间,那么,在操作对象时,实际上是在操作对象的引用而不是实际的对象。
  3. 动态的属性:
    • 对于引用类型的值,很明显,我们可以为其 添加、改变、删除 属性和方法:
      var person = new Object();
      person.name = "Ozzie";
      console.log(person.name);    //"Ozzie"
      • 上述过程中,我们创建了一个对象并为其添加了一个属性,如果对象不被销毁或者这个属性不被删除,那么这个属性将一直存在
    • 但是,我们不可以给基本类型的值添加方法和属性
      var name = "Ozzie";
      name.age = 19;
      consoe.log(name.age);    //undefined
      • 尽管这样操作不会报错,但是仍然会被默默地挂掉
  4. 比较:
    • 基本类型的比较是 值的比较
      • 例如:
        var a = 1;
        var b = true;
        console.log(a == b);    //true
    • 引用类型的比较是 引用的比较
      • 例如:
        var person1 = {};
        var person2 = {};
        console.log(person1 == person2);    //false
      • 上文提到过,引用类型时按引用访问的,换句话说就是比较两个对象的堆内存中的地址是否相同,很明显,并不是同一个内存位置:
        在这里插入图片描述
  5. 复制变量值:
    • 将一个基本类型的值复制给另一个变量,那么,会在新变量上创建一个新值,然后再把该值复制到为新变量分配的位置上:
      • 例如:
        var a = 10;
        var b = a;
        a++;
        console.log(a);    // 11
        console.log(b);    // 10
      • a与b是完全独立的,该值只是a中的值的一个副本。
      • 基本类型在赋值操作后,两个变量是相互不受影响的:
        在这里插入图片描述
    • 那么,复制引用类型的值时,同样也会将一份存储在对象中的值复制到新变量的空间中,不同的是,这个值的副本实际上是一个 指针,指向的是存储在堆中的对象。也就是说,复制结束后,这两个变量将引用同一个对象。
      • 例如:
        var a = {}; // a保存了一个空对象的实例
        var b = a;  // a和b都指向了这个空对象
        a.name = 'jozo';
        console.log(a.name); // 'jozo'
        console.log(b.name); // 'jozo'
      • 改变其中一个变量就会影响到另一个变量
        在这里插入图片描述
  6. 传递参数:
    • 请记住,尽管在访问变量时有着按值访问和按引用访问这两种方式,但 ECMAScript 中所有的函数的参数都是按值传递的,即参数只能按值传递,也就是说,把函数外部的值复制给函数内部的参数,就类似于变量之间的值复制一样
    • 基本类型的值的传递如同基本类型的变量的复制,被传递的值会被赋值给一个局部变量(即命名参数,用 ECMAScript 中的概念说,就是 arguments 对象中的一个元素),此处不再赘述…
    • 但是向参数传递引用类型的值时,复制给局部变量的是 内存中的地址,因此这个局部变量的变化会被反映在函数的外部。
      • 例如:
        function setName(obj){
              obj.name = "Ozzie";
        }
        var person = new Object();
        setName(person);
        console.log(person.name);    //"Ozzie"
      • 我们可以看到,在函数内部,objperson 引用的是同一个对象,换句话说,即使这个变量是按值传递的,obj 也会按引用来访问同一个对象,因为person指向的对象在堆内存中只有一个,而且是全局对象。
      • 很多人会 错误地认为:参数是按引用传递的,因为在局部作用域中修改的参数会在全局作用域中反映出来,OK,那么我们再看一个例子:
        function setName(obj){
              obj.name = "Ozzie";
              obj = new Object();
              obj.name = "Nicholas"
        }
        var person = new Object();
        setName(person);
        console.log(person.name);    //Ozzie
      • 如果是按引用传递参数的,那么显然 person对象就会在函数内部自动修改name 属性为 Nicholas,但结果仍然是 Ozzie,这说明,即使在函数内部修改了参数的值,但原始的引用仍然保持不变,实际上,在函数内部重写obj时,这个变量的引用就是一个局部对象了,而这个局部对象在函数执行完毕后立即被销毁。
本作品采用《CC 协议》,转载必须注明作者和本文链接
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!