【面试篇】当面试官问你为什么原始数据身上可以访问属性不报错

引言

在上篇文章我们了解了JS中两种数据类型在创建时的底层逻辑,以及构造函数时引擎的执行过程,那么我们这次来聊一聊如果给原始数据类型赋予一个引用数据类型的特征时会发生什么,借助通俗易懂的代码,让我们了解什么是包装类概念

包装类

包装类概念

我们知道在JavaScript中,有5种基本数据类型,包括数字、字符串、布尔值、null和undefined,它们都是原始值。与此同时,JavaScript还支持对象,对象是键值对的无序集合,可以包含多种类型的数据,例如函数和数组。

然而,在JavaScript中,一些操作并不直接适用于原始类型的值,而是需要将它们转换为相应的对象。这就引入了"包装类"的概念。这些包装类是由JavaScript自动创建的临时对象,用于实现对原始值进行操作。一般来说,有三种包装类:Number、String和Boolean。当我们使用原始数据类型调用相关属性或方法时,JavaScript会自动将其转换为相应的包装对象,以便于对其进行操作。

三种包装类

  1. Number : new Number(num);
  2. String : new String(str);
  3. Boolean : new Boolean(bool)

正是由于JS提供了这三种方法,所以当我们对基础数据类型访问他们的属性时并不会报错,借助下面这些代码实例我们可以更好地理解。

代码解释包装类

js 复制代码
let obj = {}
console.log(obj.a);

这里我们定义了一个obj对象,但是里面我们没有添加属性,如果此时我们访问这个对象中一个不存在的属性会输出什么呢?答案是undefined 因为obj这个对象中并没有a这个属性,但是当我们对其进行访问时JS会自动创建一个临时属性a但是没有赋值,所以输出的是 undefined

js 复制代码
var num = 123
num.abc = 'hello'
console.log(num.abc);

我们再看这一个代码,这里创建了一个名为num的变量并将其赋值为数字123。然后尝试给num添加一个名为abc的属性,并将其值设置为字符串'hello',这段代码执行的结果是什么呢?

结果是 undefined 因为num也是原始数据类型,但是当我们给其身上添加一个属性并且赋值时其实JS会真给他创建一个对象然后赋值,为什么?原因就是JS这门语言定义了这三种包装类方法,所以JS其实隐式地执行了下面这些代码

js 复制代码
var num = new Number(num){
 abc: 'hello'
}
delete num.abc

在执行到第二行代码地时候,JS会认为num是个对象于是隐式地为他创建一个对象,但是当为这个属性赋值的时候,JS才反应过来原来这个num是一个原始数据类型,紧接着执行删除num.abc。

js 复制代码
var num = new Number(num){
 abc: 
}

最后在执行输出语句的时候JS识到要输出的是一个带有属性的值时,JS又会将num认定为是一个对象,并且将其创建一个临时对象,但是不赋值,于是最后输出的结果就是undefined

js 复制代码
var str = 'abcd'
console.log(str.length);

这里我们定义了一个字符串类型的变量str,我们可以直接查询到这个字符串的长度,可能你会感到疑惑,str不是一个原始数据类型吗,为什么他可以访问属性而且输出值呢,因为这是JS提供的一个获得字符串长度的方法,所以输出的结果是4

js 复制代码
var num = new Number(123)
num.abc = 'hello'
console.log(num.abc);
console.log(num * 2); //参与运算时会变成数字

这里创建了一个名为num的变量,并使用new Number()语法将其初始化为一个Number对象,该对象包装了数字123。然后给num添加一个名为abc的属性,并将其值设置为字符串'hello'。在这种情况下,num是一个对象,因此可以成功添加属性abc。接下来的console.log(num.abc)会输出字符串'hello'。但是当num参与乘法运算时,它会自动转换为原始类型的数字,因此console.log(num * 2)会输出数字246

我们接着再来看一个例子 从头分析一下JS执行过程

js 复制代码
var num = 4
num.len = 3
//var num = new Number(4)
// delete num.len
console.log(num.len);
var num = new Number(4).len  //隐式包装类

注释部分为JS隐式执行的过程

首先我们定义了一个变量为原始数据类型值为4,然后当我们给其添加一个len属性并且值为3时,JS首先会隐式地为这个num属性创建一个临时Number对象并且拥有len属性,但是当其为len属性赋值时,JS反应过来num是一个原始数据类型,然后立即执行删除num.len操作。然后执行输出语句,读取到要输出num.len又会为num创建一个临时对象并且有len属性但是没有赋值,于是最终输出的结果就是undefined

考点

在JS中关于干货------------包装类有什么考点呢,让我们借助一个例子对比来理解

js 复制代码
var arr = [1, 2, 3, 4, 5]
arr.length = 2
console.log(arr);

输出的结果就是 (2) [1, 2]

我们再看下面一个例子

js 复制代码
var str = 'abcd'
str.length = 2
//new String(str).length = 2 
console.log(str.length); 

现在你们猜猜输出的结果会是什么?也会和上面的数组类似吗? 如果你读懂上面的代码实例后你就会发现如果我们给一个原始数据类型添加属性但是不赋值时可以访问得到undefined的,但是当我们为其属性添加值得时候却成功不了,因为JS会在这个时候立即执行删除操作,总结来说就是对原始数据类型可以访问其身上的属性,因为JS提供了包装类方法为其创建一个临时对象,但是不能赋值,JS当发现这个变量与其原本类型冲突时还会执行delete方法删除这个属性。 所以上述实例的最终结果也是 undefined

面试题

到了这里,想必大家已经通过这么多实例理解了JS包装类的概念和执行过程了,所以我将奉上一道阿里面试题,让我们一起思考一下这道题的执行过程时怎样的,那么话不多说,上题!

js 复制代码
var str = 'abc'
str += 1
var test = typeof(str)
if(test.length == 6){
    test.sign = 'typeOf的返回结果可能为String'
}
console.log(test.sign);

解释

首先,创建一个名为str的变量,并将其赋值为字符串'abc'。然后使用+=操作符将数字1附加到字符串str上,得到新的字符串'abc1'。接下来,创建一个名为test的变量,并使用typeof运算符获取字符串str的类型,并将结果赋值给test。由于typeof返回的是一个字符串,因此test被赋值为字符串'string'。然后,通过比较test.length是否等于6来判断test的长度是否为6。由于test的长度是6,执行条件为真,进入if语句块中。在if语句块中,尝试给test添加一个名为sign的属性,并将其值设置为字符串'typeOf的返回结果可能为String'。最后,通过console.log(test.sign)尝试输出test的属性sign的值,但是由于test其实是一个字符串类型,为其添加sign属性的时候,JS首先会为其创建一个String临时对象,并且有一个sign属性,然后执行为这个属性赋值操作时立即会删除这个属性,最后输出test.sign时,JS又会为test创建一个String临时对象,并且具有一个sign属性,不过没有赋值,所以最后的输出结果就是undefined

总结

md 复制代码
# 原始类型,引用类型
- 原始类型的值是存在调用栈的
- 引用类型的值是存在堆当中的,但是会将引用地址存在栈中

# 对象

- 对象的创建方法
1. var obj = {}  //对象字面量|对象直接量
2. let obj = new Object()  //构造函数    
3.  自定义构造函数
4. Object.create({})

# 构造函数

- new 执行的隐式过程
1. 创建一个this对象
2. 执行函数中的this.xxx = xxx
3. 隐式地返回this

# 包装类 v8包装类执行操作  

- 原始值是不能拥有属性和方法的,属性和方法是对象独有的。
- 当我们给原始类型赋予属性时,可以访问且值为undefined,但是当我们为其赋值时将会没有效果

感谢大家的阅读,点点赞吧♥

如果想了解更多有用的干货,点赞+收藏 编码不迷茫

开源Git仓库: gitee.com/cheng-bingw...

更多内容:【小白篇】JS中创建两种数据类型的底层逻辑

相关推荐
齐 飞6 分钟前
MongoDB笔记01-概念与安装
前端·数据库·笔记·后端·mongodb
神仙别闹23 分钟前
基于tensorflow和flask的本地图片库web图片搜索引擎
前端·flask·tensorflow
aPurpleBerry1 小时前
JS常用数组方法 reduce filter find forEach
javascript
sszmvb12341 小时前
测试开发 | 电商业务性能测试: Jmeter 参数化功能实现注册登录的数据驱动
jmeter·面试·职场和发展
测试杂货铺1 小时前
外包干了2年,快要废了。。
自动化测试·软件测试·python·功能测试·测试工具·面试·职场和发展
王佑辉1 小时前
【redis】redis缓存和数据库保证一致性的方案
redis·面试
真忒修斯之船1 小时前
大模型分布式训练并行技术(三)流水线并行
面试·llm·aigc
GIS程序媛—椰子1 小时前
【Vue 全家桶】7、Vue UI组件库(更新中)
前端·vue.js
DogEgg_0011 小时前
前端八股文(一)HTML 持续更新中。。。
前端·html
ZL不懂前端1 小时前
Content Security Policy (CSP)
前端·javascript·面试