前言
【干货】 你真的了解JavaScript中的封装类嘛?
今天,我来带大家轻松学习JavaScript中的封装类(对象)
正文
在学习封装类之前,我们首先要知道,在JavaScript中有两种数据类型:原始数据类型和引用类型
原始数据类型和引用类型
javascript
在JavaScript中,数据类型大致可以分为原始数据类型和引用数据类型。
原始数据类型包括:Undefined,Null,Boolean,Number,String。这些类型直接存储在栈(stack)中的简单数据段,占据空间小,大小固定,属于被频繁使用的数据,所以存储在栈中。
引用数据类型则包括对象、数组、函数。它们存储在堆(heap)中的对象,占据空间大,大小不固定,如果存储在栈中,将会影响程序运行的性能。引用数据类型在栈中存储了指针,该指针指向堆中该实体的起始地址,当解释器寻找引用值时,会首先检索其在栈中的地址,取得地址后,从堆中获得实体。
首先,原始数据类型有以下这些:
js
原始数据类型
let a = 'hello'
let b = 123
let c = true
let u = undefined
let n = null
接下来我们来看看:引用数据类型
js
let obj = {
name:'小花'
}
let lw = obj
obj.name='小红'
console.log(lw.name)
也是我们经常说的对象,关于引用数据类型,我们要知道:
引用类型不能放在调用栈里面,防止内存泄漏,关于调用栈的知识大家可以学习:【干货】带你轻松理解JavaScript中的闭包、调用栈和作用域链!! - 掘金 (juejin.cn)
浏览器内核会开辟一个新的内存空间,叫做 堆 用来存放引用数据类型!然后给引用类型的变量赋一个地址,调用的时候就将地址赋给别的变量
调用的时候,顺着地址先判断它是不是对象,在取值。
好,我们来为大家上个图解:
我们回顾一下我们的代码:
js
let obj = {
name:'小花'
}
let lw = obj
obj.name='小红'
console.log(lw.name)
结合图解为大家分析:
- 在全局执行上下文当中,我们声明了一个
obj
对象,此时我们的内核会在调用栈之外 ,开辟一个堆空间,然后,将obj
的对象体存入堆空间当中,将存放这个对象体的地址 赋值给调用栈中的词法环境当中的obj
变量----传址不传值 - 接着,我们定义
lw
变量,并让它等于obj
,你说obj
没有值嘛?**地址也是值!**于是,我们的内核会将obj
的存储的地址赋值给lw
。 - 然后我们利用
obj.name = '小红'
修改了对象中的name
属性!,也就是说:将obj
指向的地址中的值修改成了"小红" - 我们再打印
lw.name
,lw
和obj
指向的是同一个地址,这个obj
指向的地址中的值发生了变化,lw
指向的地址中的值也会随着改变。
因此,我们的输出结果就是:
js
输出:小红
那我们如何给对象进行增删改查呢?
js
let obj = {
name:'丁总',
age:18,
}
let girl = '小红'
obj[girl] = '小红'
obj['girl'] = 'girl'
delete obj.girl
console.log(obj)
在这一段代码中,我们的输出结果是什么呢?我们来简单分析一下。
markdown
1. 我们定义了一个对象`obj`,定义一个变量`girl='小红'`
1. 为`obj`对象添加了两个属性 一个是属性`'小红'`,一个是属性`"girl"`,`小红`是哪里来的?在`obj[girl]`中的`girl`会被内核识别成一个变量,于是它会识别到这个变量的值,也就是`小红`
1. 后面我们又用`delete()`方法删除了`obj.girl`,这里指的是字符串`girl`而不是变量
所以,我们的输出结果是:
js
输出:{ name: '丁总', age: 18, '小红': '小红' }
对象的创建方法
js
对象的创建
1. var obj = {}//对象字面量||对象直接量 Object()//构造函数
2. var obj = new Object();
3. 自定义构造函数
4. Object.create({})
我来上一个案例更贴切的去学习这四种方法!
第一种var obj = {}
:
js
var obj = {}
obj.name = '小红'
console.log(obj)
js
输出:{ name: '小红' }
第二种var obj = new Object()
js
var obj = new Object();
obj.name = '小奈'
console.log(obj)
js
输出:{ name: '小奈' }
第三种自定义构造函数
js
var Newname = function(){
this.name = '小明'
}
var person = new Newname()
console.log(person)
js
Newname { name: '小明' }
第四中Object.create()
方法
js
let str = Object.create({})
str.name = '小行'
console.log(str)
js
输出:{ name: '小行' }
构造函数就像工厂,可以批量化生产
函数也是引用类型,可以说成是一种特殊的对象。
New
到底是什么执行原理?
在JavaScript中,new
关键字用于创建一个用户定义的对象类型的实例或具有构造函数的内建对象的实例。其运行原理大致可以分为以下步骤:
- 创建新对象 :首先,
new
关键字会创建一个新的空对象。这个对象是新创建的对象,该对象的类型是所指定的类型。 - 设置原型链 :新创建的对象的
[[Prototype]]
(即__proto__
)会被设置为构造函数的prototype
对象。这意味着新对象可以访问构造函数原型上的属性和方法。(这部分我们将在下次文章中学习--原型) - 构造函数执行 :之后,构造函数会使用这个新创建的对象作为其上下文被执行。换句话说,
this
关键字在构造函数内部指向这个新创建的对象。 - 返回新对象 :如果构造函数没有显式返回一个对象,那么会自动返回新创建的对象。如果构造函数返回了一个非原始类型(即对象或者函数),那么这个返回值会被作为整个
new
表达式的返回值。如果返回的是原始类型(例如:Number、String、Boolean、Null、Undefined),则忽略这个返回值,依然返回新创建的对象。
我们来看一看这样一段案例:
js
function Person(name,age,sex){
this.name = name
this.age = age
this.sex = sex
}
let p = new Person('海军',18,'boy')
console.log(p)
毫无疑问,这个代码的输出结果为:
js
Person { name: '海军', age: 18, sex: 'boy' }
那么,new
的执行原理到底是什么呢?
按照,我们目前的理解,new
的执行原理就相当于,在我们new
的那个函数体当中生成一个"this"对象,第二步执行函数体内的逻辑,最后返回this。也就相当于下面这个案例
js
function Person(name,age,sex){
var that = {}//new 新生成
that.name = name
that.age = age
that.sex = sex
return that//new 新生成
}
let p1 = Person('小五',18,'boy')
console.log(p1)
css
输出:{ name: '小五', age: 18, sex: 'boy' }
这就new
产生的一个效果!
接下来, 我们来介绍
包装类与原始数据的一些差别
包装类
js
var obj = {}
console.log(obj.a)
原始数据
js
let a = 'hello'
let b = 123
let c = true
let u = undefined
let n = null
- 声明方式:原始数据类型不可以使用new关键字,而包装类型需要使用new关键字来在堆中分配存储空间。
- 存储方式及位置:原始数据类型是直接将变量值存储在堆栈中,而包装类型是将对象放在堆中,然后通过引用来使用。
- 初始值:原始类型的初始值如int为0,boolean为false,而包装类型的初始值为null。
- 使用方式:原始类型可以直接赋值直接使用,而包装类型在集合如Collection、Map时会使用到。
- 传递方式:基本类型在传递参数时都是按值传递,而封装类型是按引用传递的。
- 继承性:包装类都是继承Number接口实现Comparable接口的。例如,Double extends Number implements Comparable。
同时原始数据不具备属性,而包装类具备属性。
也不可以给原始数据添加属性。
例如:
js
var num = 123
num.abc = 'hello'
console.log(num.abc)
js
输出:undefined
既然说,不能给原始数据添加属性,为什么这里输出的是undefined而不是报错呢?
重点重点!!!!
这是因为当我们内核执行到给原始数据添加属性等操作时,每次内核会堆我们原始数据new
一个对象,但是,我们内核发现,这个数据是原始数据,于是立马又会将它new
的对象delete
掉,每次调用num.abc
都会有这样一个过程。因此,在console.log(num.abc)
的时候也会执行这样一个过程,所以,最后输出的结果是:undefined
我们再来说一个案例:
js
var num = new Number(123)
num.abc = 'hello'
console.log(num.abc)
console.log(num*2)
js
输出:
hello
246
在这个案例当中,num
不参与运算时会被识别为对象,如果num
参加了四则运算会被识别为原始数据-数字
我们再来通过这个案例来学习一下隐式包装类
js
var num = 4
num.len = 3
var num = new Number(4)
num.len = 3 //来弥补num.len的不足
delete num.len
new Number(4).len //这个过程就叫隐式包装类
console.log(num.len)
js
输出:undefined
为什么这里输出的是undefined
?
在JavaScript中,当你尝试获取或设置一个原始数据类型的属性时,JavaScript实际上会创建一个包装对象来处理这个属性。这就是所谓的"隐式包装"。然而,当你再次尝试获取或设置这个属性时,如果之前没有设置过,那么就会返回undefined。
比如在上面的代码
js
var num = 4;
num.len = 3;
console.log(num.len);
会输出 undefined,因为num实际上被隐式地转换为了一个Number对象,而这个Number对象并没有一个名为len的属性
然后我们创建了一个新的Number对象:
js
ar num = new Number(4);
num.len = 3; //成功添加属性
delete num.len; //删除属性
console.log(num.len); // undefined,因为num.len已经被删除了
最后
js
new Number(4).len; // undefined,因为新创建的Number对象并没有len属性
我们输出num.len
由于num.len
已经被删除,所以返回的是undefined
关于"隐式包装类"的概念,它指的是当JavaScript尝试将原始数据类型当作对象来处理时,会在背后创建一个包装对象来模拟原始数据类型的行为。然而,这个包装对象只会在背后创建,你无法直接访问到它。这就是为什么你无法直接给原始数据类型添加属性的原因。
考点
js
var arr = [1,2,3,4,5]
arr.length = 2
console.log(arr)
这里输出的是什么?
js
输出:[ 1, 2 ]
这是因为在JavaScript中,arr.length = 2
并不会将数组截断为只有两个元素。相反,它会将数组的长度设置为2,这意味着数组将不再包含原来的所有元素。
当你运行 arr.length = 2
时,你实际上是在告诉JavaScript将数组的长度减少到2。这意味着只有索引0和1的元素会被保留,其它的元素将被丢弃。
所以,原本的数组 [1,2,3,4,5]
在执行 arr.length = 2
后变成了 [1,2]
。
我们再看这个案例:
js
var str = 'abcd'
str.length = 2
console.log(str.length)
输出:4
那为什么这里又是4呢?
这是因为在JavaScript中,字符串是一个特殊的对象类型。当你创建一个字符串变量时,例如 var str = 'abcd'
,实际上你创建的是一个字符串对象,而'abcd'只是这个对象的一个属性。这个属性的名字是0
,并且它的值就是字符串'abcd'。
然后,当你尝试修改字符串的长度属性(例如 str.length = 2
),你实际上是在修改这个特殊对象的长度属性,这个属性的名字是 length
。这个属性表示的是字符串中字符的数量。
然而,这个 length
属性的值并不会影响字符串对象中实际存储的字符数量。也就是说,无论你如何改变 length
属性的值,字符串对象中存储的实际字符数量(也就是属性 0
的值)都不会改变。
所以,当你运行 console.log(str.length)
时,输出的结果是你设定的 length
属性的值,也就是2。然而,这并不影响字符串对象中存储的实际字符数量,也就是属性 0
的值,它的值仍然是'abcd'。
所以,虽然你设定了 str.length = 2
,但是当你运行 console.log(str)
时,输出的结果仍然是'abcd',而不是你设定的两个字符的字符串。
new String('abcd).length
然后delete
刚生成立马会被删掉
改变不了原始值的值
好了!我们今日的学习就到此为止啦!
如果大家有任何指正和想法,欢迎大家在评论区留言!
希望大家给个小小的赞支持鼓励一下吧!🌹🌹🌹