JS:给原始类型数据加属性和方法,为什么不报错?

前言

近日看到一道阿里前端面试题,由于不了解JS包装类的特性,果然不出意外的做错了。题目如下:

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

上述代码的输出结果是什么?报错?还是typeOf的返回结果可能为String?最终结果都不是上面两种,最后输出结果为undefined,为什么会这样呢?为什么彻底理解此类问题,我们有必要深入了解一下包装类。

我们都知道JavaScript中的数据类型分为两种,第一种是原始数据类型,另一种是引用数据类型(也称为复杂数据类型)。

原始数据类型

ini 复制代码
let a = 'hello'
let b = 123
let c = true
let u =undefined
let n = null

原始数据类型主要分为六种,如上述代码所示,字符串类型、Number类型、Boolean类型、undefined类型、null类型。还有一种是ES6新增的Symbol类型(表示独一无二的值,Symbol最大的用途是用来定义对象的唯一属性名)

特点

这些原始数据类型都是不可变的,这意味着它们的值一旦被创建,就不能被修改,也意味着不能给原始数据类型加属性和方法,属性和方法是对象独有的。在JavaScript中,变量存储原始数据类型的值,而不是直接存储数据本身。原始数据类型的比较是基于它们的值,而不是引用,因此两个具有相同值的原始数据类型变量将被认为相等。

引用数据类型

在JavaScript中,除了原始数据类型(字符串、数字、布尔、未定义、空值和符号)之外,还有一种复杂的数据类型,被称为引用数据类型。引用数据类型是一种用于存储和处理更复杂数据结构的数据类型。它们不直接包含实际数据,而是存储对数据的引用。JavaScript中的引用数据类型包括对象(Object)、数组(Array)、函数(Function)、正则表达式(RegExp)、日期(Date)等。下面简单介绍一下部分引用数据类型。

对象

对象是JavaScript中最常见的引用数据类型。它是一个无序的数据集合,包含键值对。每个键值对的键是字符串,值可以是任意数据类型,包括原始数据类型和其他引用数据类型。对象用花括号 {} 定义。例如:

js 复制代码
    let person = {
        name : '小明',
        age: 18,
        hobby: basketball
    }

数组

数组是一种有序的集合,可以存储多个值,每个值可以是任意数据类型,包括原始数据类型和其他引用数据类型。数组用方括号 [] 定义。例如:

js 复制代码
    let num =[1,2,3,4]

函数

函数是JavaScript中的一种对象,也是引用数据类型的一种。函数可以被定义、传递、赋值和作为参数传递给其他函数。函数用 function 关键字定义,如下:

js 复制代码
function sayHello(){
    console.log("Hello")
}

正则表达式

正则表达式是一种用于匹配字符串模式的对象。它可以用于字符串的搜索、替换、分割等操作,提供了强大的文本处理功能。

js 复制代码
    let pattern = /[0-9]+/; // 匹配一个或多个数字

日期

日期对象用于处理日期和时间。它提供了各种方法来操作日期和时间,例如获取年、月、日、时、分、秒等信息,以及执行日期和时间的计算。

js 复制代码
 let nowDate = new Date();

引用数据类型在JavaScript中用于处理更复杂的数据结构和操作,它们提供了灵活性和功能性,使得JavaScript成为一种强大的编程语言。通过组合使用原始数据类型和引用数据类型,开发者可以处理各种类型的数据和问题。

介绍了一下JavaScript中的数据类型,接下来我们就可以讲解一下什么是包装类了。

在JavaScript中,包装类(Wrapper Objects)是一种特殊的对象,它们用来包装原始数据类型(如字符串、数字和布尔值),使其具备对象的方法和属性。这些包装类提供了对原始数据类型的操作,例如字符串的长度、数字的精度等。

我们在介绍原始数据类型的时候曾讲到过,原始数据类型都是不可变的,这意味着它们的值一旦被创建,就不能被修改,也意味着不能给原始数据类型加属性和方法,属性和方法是对象独有的 上述加粗字体这些规则的存在显然是必要的,正是有了这些规则,使得我们不能直接给原始数据随便添加属性和方法,这使得原始数据往往都是很简单的。而在我们的日常代码练习中,你一定有用到过一些原始类型数据自带的方法,比如字符串类型的数据往往都会自带一个length方法,它能够直接返回一个字符串的长度,由于上述规则的限定,显然我们不能直接修改字符串的长度。这确保的数据的完整性,使原始数据在创建后不会被无意或者恶意更改,尤其是在多线程或者并发编程环境中尤为重要。等等!我们刚刚不是讲到原始数据不能附加属性和方法吗?那字符串类型的length方法是哪来的呢?这就是我们要特别介绍的包装类。

包装类

包装类的设计正是为了给原始数据类型增加方法和属性。

在JavaScript引擎内部,会对原始数据类型进行了一些封装和包装,使其能够调用一些内置方法和属性。当你尝试访问原始数据类型的方法或属性时,JavaScript引擎会自动将原始数据类型包装成对应的包装对象,然后调用相应的方法或属性,然后再将包装对象销毁。这个过程被称为"自动包装"。例如,当你访问字符串的 length 属性时,JavaScript引擎会在内部将字符串包装成 String 对象,然后访问其 length 属性。这一切都是自动完成的,你无需手动创建包装对象。虽然原始数据类型不能附加自定义属性和方法,但这种自动包装机制使得它们可以调用一些内置的属性和方法,使其更方便。不过需要注意,包装对象在访问完成后会被销毁,所以无法在原始数据类型上保留自定义属性或方法。如果需要自定义属性和方法,通常会使用对象类型来实现。

了解了这些,我们再回到最初看到那道面试题,尝试着去分析它,相信问题就迎刃而解了。

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

分析题目

  1. str是一个字符串类型,当它和1相加时我们能够知道,字符串和其他东西相加会将它'同化'最终也变为字符串,于是,我们得到得str变为'abc1'。
  2. typeof会读取到一个数据并返回它的类型,最终我们得到test被赋值为string.
  3. 由于string的长度为6,进入if判断语句。
  4. 但是test为字符串类型,为原始数据,它本身不能添加属性和方法,但由于包装类存在的缘故,test.sign = 'typeOf的返回结果可能为String' 会被执行为:new String(test).sign = 'typeOf的返回结果可能为String' 然后delete被销毁掉。
  5. 接着,我们退出if语句,控制台输出test.sign,由于我们之前创建的test.sign已经被销毁,但此时又读到了test.sign,JavaScript引擎又会生成 *new String(test).sign *
  6. 但由于 new String(test).sign并未被赋值,所以最终我们得到结果:undefined。

留言

如果喜欢博主的文章,还请麻烦给点个小赞♥(ˆ◡ˆԅ),如有说的不对的地方,欢迎及时指正哦。

相关推荐
开心工作室_kaic10 分钟前
ssm068海鲜自助餐厅系统+vue(论文+源码)_kaic
前端·javascript·vue.js
有梦想的刺儿29 分钟前
webWorker基本用法
前端·javascript·vue.js
cy玩具1 小时前
点击评论详情,跳到评论页面,携带对象参数写法:
前端
清灵xmf1 小时前
TypeScript 类型进阶指南
javascript·typescript·泛型·t·infer
小白学大数据1 小时前
JavaScript重定向对网络爬虫的影响及处理
开发语言·javascript·数据库·爬虫
qq_390161772 小时前
防抖函数--应用场景及示例
前端·javascript
334554322 小时前
element动态表头合并表格
开发语言·javascript·ecmascript
John.liu_Test2 小时前
js下载excel示例demo
前端·javascript·excel
Yaml42 小时前
智能化健身房管理:Spring Boot与Vue的创新解决方案
前端·spring boot·后端·mysql·vue·健身房管理
PleaSure乐事2 小时前
【React.js】AntDesignPro左侧菜单栏栏目名称不显示的解决方案
前端·javascript·react.js·前端框架·webstorm·antdesignpro