前言
今天我们就来聊聊JavaScript中的引用数据类型和构造函数,引用数据类型和原始类型有什么区别呢?构造函数创造变量时,前面为什么要加new呢?
JavaScript中的数据类型分为两大类:原始数据类型(Primitive Data Types)和引用数据类型(Reference Data Types)。
- 原始数据类型
1.字符串(String) : 用于表示文本,用单引号(')或双引号(")括起来。
let name = 'John';
2.数字(Number) : 用于表示数值,包括整数和浮点数。
let age = 25;
3.布尔值(Boolean) : 用于表示真(true)或假(false)。
let isStudent = true;
4.未定义(Undefined) : 表示一个变量已声明但未赋值。
let variable;
5.空值(null) : 表示一个变量没有值或对象为空。
let emptyValue = null;
引用数据类型
1.对象(Object) : 用于表示复杂数据结构,可以包含多个属性和方法。
const person = { name: 'Alice', age: 30, };
2.数组(Array) : 用于存储多个值的有序列表。
const numbers = [1, 2, 3, 4, 5];
3.日期(Date) : 用于表示日期和时间。
let today = new Date();
还有函数等等引用数据类型,我们下次再介绍。
引用数据类型跟原始数据类型有什么区别呢? 这里我们拿对象来解释一下,我们先来看一道题。
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的值已经被修改为'来颗奇趣蛋',所以输出'来颗奇趣蛋',引用数据类型共享变量。
构造函数
我们想创建一个对象,有几种方法呢?
- 对象字面量(对象直接量)
var obj = {} //对象字面量||对象直接量
- 构造函数
let obj = new Object() //构造函数
- 自定义构造函数
我们来写一个构造车对象的构造函数
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会怎么样:
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对象,所以就有值了
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对象
今天的内容就到这啦,如果你觉得小编写的还不错的话,或者对你有所启发,请给小编一个辛苦的赞吧