前言
今天我们来学习一下JavaScript中的包装类,包装类是指在面向对象编程中,用来封装基本数据类型的类。在Java等编程语言中,基本数据类型(如int、double、boolean等)是不具备对象特性的,不能直接调用类的方法或参与面向对象的操作。为了能够在面向对象的环境中使用基本数据类型,需要将其封装成对象,这就是包装类的作用。
数据类型
原始类型
let num = 123 let str = 'hello' let flag = true
let un = undefined let nu = null
引用类型
let obj = {} //对象 let fn = function(){} //函数
let arr = [] //数组 let date = new Date()
原始类型与引用类型的区别
原始类型(primitive types)和引用类型(reference types)是在编程语言中常见的两种数据类型。
原始类型:原始类型是编程语言中的基本数据类型,通常包括整型(int、short、long、byte)、浮点型(float、double)、字符型(char)和布尔型(boolean)等。 原始类型的变量直接存储数据的值,而不是引用数据的地址。 原始类型的变量在栈内存中分配空间,数据存储在变量所在的位置,因此访问速度较快。 原始类型的变量存储的是实际的数据值,而不是对数据的引用。
引用类型 : 引用类型是指引用某个对象的变量,它们存储的是对象在内存中的地址。 引用类型包括类、接口、数组等,它们在堆内存中分配空间。 引用类型的变量在栈内存中存储的是对象的引用地址,而对象本身存储在堆内存中。 对象的属性和方法可以通过引用来访问,而不是直接访问对象本身。 总的来说,原始类型是存储基本数据值的变量,而引用类型是存储对对象的引用的变量。原始类型的变量直接存储数据值,而引用类型的变量存储的是对象的引用地址。
我们通过两端代码来解释:此时我们可以看到b的值并没有因为a的值改变而改变,还是等于原来a的值。
js
let a = 1
let b = a
a=2
console.log(b);
接下里看第二段代码:
js
let a = {
age:19
}
let b = a
a.age=20
console.log(b.age);
此时的a从原始类型变成了引用类型,在上一篇文展中我们讲过对于编译器来说在编译前会创建一个调用栈,全局以及函数的执行上下文都会暂时存储在调用栈中。
let定义的a会存放在词法环境中,但是a被赋于的是一个对象{},而栈中的内存有限,对象的大小不一,所以调用栈中不能存放a对象,因此会创建一个堆,堆和栈类似,都是一种数据结构,堆里面可以存放大小不一的对象等,每个对象都有一个特定的地址码。
此时这个地址码就会赋值给a,又因为b=a,所以这个地址码也会赋值给b,当a.age=20执行时,其修改的时堆中的值,但a与b的地址码并不会变,所以当b.age顺着这个不变的地址码找到age时,age变为了20
所以当a被赋予的类型为原始类型时,你修改a是不能修改b的,当a被赋予为引用类型时,当你修改a是可以修改b的。
对象
在JavaScript中,对象是一种复合值,它可以包含多个键值对。每个键值对中,键是一个字符串,值可以是任意类型的值,包括基本类型、对象或函数。对象是 JavaScript 中最重要的数据类型之一,它提供了一种灵活的方式来表示和组织数据。对象可以通过字面量语法创建,例如如下代码:
js
let person = {
name: "Alice",
age: 30,
city: "New York"
};
这里,person
对象有三个属性:name
、age
和 city
。属性名是字符串,属性值可以是任意类型的值。
对象的创建
js
var obj = {} //对象字面量方式
通过使用花括号 {}
,你可以直接创建一个空对象 obj
。你可以在对象字面量中添加属性和方法。
js
var obj2 = new object(); //构造函数方式
通过使用 new Object()
,你可以调用内置的构造函数 Object
来创建一个空对象 obj2
。这种方式也可以用于创建具有自定义属性和方法的对象。
以上这两种方式创建的对象没有区别!
自定义构造函数
js
function Car(){
this.name='abc'
this.height=1400
this.lang=3000
this.weight=1000
}
let car = new Car() //实例对象
console.log(car);
构建一个Car()其中包含四种属性,当我们用new调用Car()时,new会发挥作用:
- 创建新对象 :使用
new
关键字调用一个函数时,会创建一个新的空对象。 - 绑定this :在新创建的对象上调用该函数,并将该对象绑定到函数中的
this
上,使函数内部的this
指向新创建的对象。 - 返回新对象 :如果构造函数没有显式返回一个对象,则
new
关键字会自动返回新创建的对象实例;如果构造函数显式返回一个对象,则返回该对象,而不是新创建的对象实例。
结果演示:
另外,当你有两个实例对象时,你修改二者之一中属性的值,另外一个对象的对应属性是不会被修改的,也就是说两者相互独立。
js
let car =new Car()
let car2 =new Car()
car.name='ggbong' //此时car2的name并不会变成ggbong
car的name变成ggbong,car2的name还是abc。
对象属性的增删读改
看如下代码:
js
let obj = {
name:'小李',
age:18
}
读取对象
当你要读取对象name时,就可以通过console.log(obj.name)语句或者使用console.log(obj['name'])
修改值
将age:18修改为age:19,可以通过obj.age=19语句来修改
增加值
例如增加小李的性别,可以通过obj.sex='男'来增加
当你定义let n='sex'时,你想将输出sex:男,你得输入obj[n]='男',而不能写obj.n='男',因为[]中放的是变量,当你用点(.)时会被默认当作字符串n,得到的结果是n:'男'而不是sex:'男'。
删除属性
可以通过delete obj.sex 语句来删除属性
包装类
在 JavaScript 中,包装类(Wrapper Classes)是一种将基本数据类型(如字符串、数字、布尔值)封装成对象的机制。这允许基本数据类型具有对象的属性和方法。原始类型是不能具有属性和方法的,属性和方法只能对象独有的。
JavaScript 提供了三种主要的包装类:
String 类:用于封装字符串。它允许您对字符串执行各种操作,如访问字符串的长度、查找子字符串、替换文本等。
Number 类:用于封装数字。它允许您对数字执行各种操作,如四舍五入、转换为不同的进制、检查是否为 NaN 等。
Boolean 类:用于封装布尔值。它允许您对布尔值执行各种操作,如逻辑运算、类型转换等。
原始类型封装
看如下一段代码用于封装数字:
js
var num = 4
num.len = 3
console.log(num.len);
按照我们的惯性思维,此段代码是不能执行的,应该报错,因为num作为变量,不能在变量上加属性,除非是对象,但是此段代码并没有报错,而是输出了undefined。
这是为什么呢?
这就必须得提到我们今天的主角------------包装类。 尽管我们写入的代码为上所示,但是在执行过程中,在我们看不到的地方,机器会将代码运行为如下:
js
var num =new Number(4)
num.len = 3
delete num.len
console.log(num.len);
这也就意味着这两段代码实际上是相等的,只不过第一段是我们人类的思维,而第二段是电脑机器的思维。
在上文中我们提到了对象的创建,因为new调用了Number()函数,使得创建了一个新的对象并赋值给了num,所以我们可以说num现在在机器看来是一个对象,而num.len = 3,这样的给对象添加属性的操作就是再正常不过了,但机器会突然反应过来num实际上只是一个变量,所以会私自进行delete操作,将num.len=3删除,当进行到console.log(num.len)操作时,会找不到num.len的值,所以最终会输出undefined,以上这一系列操作统称为包装类
引用类型封装
看如下代码:
js
var arr =[1,2,3,4]
arr.length = 2
console.log(arr);
如结果所示,arr看似是变量,但是在运行过程中会被封装成数组,数组中有length属性,所以arr.length=2可以成功被加进去,所以当你输出arr时,就会少去两个长度,只剩下ab。
此时我们看下一段代码:
js
var str ='abcd'
str.length = 2
console.log(str); //此时abcd的长度并没有被修改,所以说明字符串不像数组一样有length属性
但在机器中实在运行的代码如下:
var str=new String('abcd')
str.length = 2
delete str.length //str.length会被删除,所以不影响输出abcd
console.log(str);
但是如果你代码这样写:
js
var str='abcd'
console.log(str.length);
你会得到4的输出结果,那么问题来了,既然字符串没有length属性,那又为什么可以输出长度4呢?
那是因为length不是字符串的属性,而是new String()构造函数体内自带的,是访问构造函数体内属性访问到了值,这其实不与我们说的原始类型不具有属性发生冲突。
从前文的基础到最后包装类的讲解,一步一步理清楚,我们将会对包装类有新的认知!
今天的讲解就到此结束了,感谢大家的支持!