JavaScript引用数据类型和构造函数的秘密

前言

今天我们就来聊聊JavaScript中的引用数据类型和构造函数,引用数据类型和原始类型有什么区别呢?构造函数创造变量时,前面为什么要加new呢?

JavaScript中的数据类型分为两大类:原始数据类型(Primitive Data Types)引用数据类型(Reference Data Types)。

  • 原始数据类型
  1. 字符串(String) : 用于表示文本,用单引号(')或双引号(")括起来。

    js 复制代码
    let name = 'John';
  2. 数字(Number) : 用于表示数值,包括整数和浮点数。

    js 复制代码
    let age = 25;
  3. 布尔值(Boolean) : 用于表示真(true)或假(false)。

    js 复制代码
    let isStudent = true;
  4. 未定义(Undefined) : 表示一个变量已声明但未赋值。

    js 复制代码
    let variable;
  5. 空值(null) : 表示一个变量没有值或对象为空。

    js 复制代码
     let emptyValue = null;

引用数据类型

  1. 对象(Object) : 用于表示复杂数据结构,可以包含多个属性和方法。

    js 复制代码
    const person = {
      name: 'Alice',
      age: 30,
    };
  2. 数组(Array) : 用于存储多个值的有序列表。

    js 复制代码
    const numbers = [1, 2, 3, 4, 5];
  3. 日期(Date) : 用于表示日期和时间。

    ini 复制代码
    let today = new Date();

还有函数等等引用数据类型,我们下次再介绍。

引用数据类型跟原始数据类型有什么区别呢? 这里我们拿对象来解释一下,我们先来看一道题。

js 复制代码
let obj = {
    name: '菌菌'
}
let lw = obj
obj.name = '来颗奇趣蛋'
console.log(lw.name);

如果不太了解引用数据类型的小伙伴们,这里可能就会觉得输出菌菌了。让我们来看看输出结果:

我们可以看到,这里输出了来颗奇趣蛋。这是为什么呢,我们修改obj.name,为什么lw.name也被修改了呢?这其实就是引用数据类型和原始数据类型的区别了,我们可以用两句话来概括一下。

  • 原始类型的值是存在调用栈中的
  • 引用类型的值是存在堆当中,但是会将引用地址存在栈中

这里我们画张图来理解一下:(图画的不是很好,请见谅)

实际上,引用数据的值存放在堆区,也就是图中黄色框框所在的位置,堆区中存在着一段连续的地址,而这些地址又对应引用数据的值,如图中的1005,而栈区中变量存储的是堆区中的地址,就如图中obj = 1005,所以通过这些地址值,我们就可以访问堆区中的值,这里也可以叫指针

为什么改变obj.name会影响到lw.name呢?从图中观察到,obj和lw存储着相同的指向堆区的地址。当let obj = {...}时,obj会将变量的值的地址存储进去,这里为obj = 1005,而真正的变量值存储在堆区。当赋值语句lw = obj时,lw被赋值为obj相同的地址,lw = 1005.

而当执行到obj.name = '来颗奇趣蛋'时,先在栈区寻找变量obj,发现obj存储的是一段地址,然后会顺着obj存储的地址来到堆区,将堆区中name的值改为'来颗奇趣蛋'.所以当输出lw.name时,发现lw的值为1005,为地址,顺着地址找到了堆区中name的值,但此时name的值已经被修改为'来颗奇趣蛋',所以输出'来颗奇趣蛋',引用数据类型共享变量。

构造函数

我们想创建一个对象,有几种方法呢?

  1. 对象字面量(对象直接量)
js 复制代码
var obj = {} //对象字面量||对象直接量
  1. 构造函数
js 复制代码
 let obj = new Object()  //构造函数
  1. 自定义构造函数

我们来写一个构造车对象的构造函数

js 复制代码
function Car(name,color,size) {
    this.name = name
    this.color = color
    this.size = size
}

let car1 = new Car('BMW','red','long')   // 实例对象
let car2 = new Car('奔驰','green','short')

自定义构造函数跟构造函数方法差不多,只是自定义构造函数是我们人为的写出一个方法,而构造函数可以直接使用。我们将用构造函数声明出的对象,叫做实例对象。我们输出一下用自定义构造函数声明出的两个对象:

构造函数就像工厂,可以批量化生成对象

在上面的例子中,我们可以发现,使用构造函数创建对象时,必须要在前面加上一个new,那new的作用是什么呢?

构造函数中new的作用

我们首先可以试试,当使用构造函数时,不加new会怎么样:

js 复制代码
function Car(name,color,size) {
    this.name = name
    this.color = color
    this.size = size
}

let car = Car('BMW','red','short')

输出一下

我们可以发现,当我们输出car时,它的值为undefined,这里我们可以想一下,因为这个构造函数里面并没有返回值,那我们输出创建的对象时,应该是找不到这个值的。那为什么在构造函数创建对象时,在前面加一个new时,就可以创建一个有值的对象呢?

其实,当我们加入new关键字时,它会在构造函数内创建一个this对象,然后将属性传入this对象里面,最后return this对象,所以就有值了

js 复制代码
function Car(name,color,size) {
    this.name = name
    this.color = color
    this.size = size

    // let this = {
    //     name: name,
    //     color: color
    //     size: size
    // }
    // return this
}

let car =  new Car('BMW', 'red', 'short')
console.log(car);

而这里的this对象 其实就是由构造函数创造的实例对象 ,关于this小伙伴们可以先这样理解,后面的文章我们会仔细分析。所以,当我们在构造函数前加入new关键字时,在函数内部就会有一个返回值,所以我们创造出的实例对象是有值的。

总结

  • 原始类型的值是存在调用栈中的
  • 引用类型的值是存在堆当中,但是会将引用地址存在栈中
  • new会在函数内部生成一个this对象,最后返回this对象

今天的内容就到这啦,如果你觉得小编写的还不错的话,或者对你有所启发,请给小编一个辛苦的赞吧

相关推荐
gnip21 分钟前
链式调用和延迟执行
前端·javascript
SoaringHeart32 分钟前
Flutter组件封装:页面点击事件拦截
前端·flutter
杨天天.34 分钟前
小程序原生实现音频播放器,下一首上一首切换,拖动进度条等功能
前端·javascript·小程序·音视频
Dragon Wu44 分钟前
React state在setInterval里未获取最新值的问题
前端·javascript·react.js·前端框架
Jinuss1 小时前
Vue3源码reactivity响应式篇之watch实现
前端·vue3
YU大宗师1 小时前
React面试题
前端·javascript·react.js
木兮xg1 小时前
react基础篇
前端·react.js·前端框架
ssshooter1 小时前
你知道怎么用 pnpm 临时给某个库打补丁吗?
前端·面试·npm
IT利刃出鞘2 小时前
HTML--最简的二级菜单页面
前端·html
yume_sibai2 小时前
HTML HTML基础(4)
前端·html