写在前面
欢迎来到我的专栏,《包装类:赋予原始类型新的可能性》!在这个专栏中,我们将通过数据类型、对象、构造函数、包装类四个层面进行讲解,希望你能坚持看下去!
在前面的章节中,我们详细讨论了JavaScript中的各种数据类型,特别是对象作为引用数据类型的重要性。在探讨对象创建的不同方式时,我们着重介绍了构造函数创建对象的过程,深入解析了这一创建模式。最后,让我们来探讨JavaScript中的包装类————赋予原始类型新的可能性。
包装类
包装类是指JavaScript中的一组特殊对象,它们可以将原始数据类型(如数字、字符串和布尔值)转换为对象。
问题导入
我们将用几个题目来构建你对包装类的认知。
题目一:
var obj ={}
console.log(obj.a);
这里创建了一个空对象 obj,然后尝试访问对象的属性 a。输出结果什么呢?
undefined
在JavaScript中,如果你访问一个对象的不存在的属性,会返回 undefined。这意味着该属性并没有被赋值,或者该属性根本不存在于对象中。
题目二:
var num = 123;
num.abc = 'hello'
console.log(num.abc);
我创建了一个原始数据类型的变量 num 并赋值为 123。然后,我尝试在 num 上创建一个属性 abc 并赋值为 'hello',输出结果又会是什么呢?
undefined
然而,在JavaScript中,原始数据类型(如数字、字符串、布尔值等)不具备对象的特性,它们是不可变的,无法添加属性或方法。
题目三:
var num = new Number(123);
num.abc = 'hello'
console.log(num.abc);
在这段代码中,我创建了一个 Number 函数创建了一个 num,然后尝试在 num 上添加一个属性 abc 并赋值为 'hello'。输出结果又会是什么呢?
hello
啊?不是说原始数据类型无法添加属性或方法吗?
这里我们使用了new Number(123)这样的语法时,实际上创建的是一个Number实例对象,而不是原始数据类型,所以num 是一个对象,你可以给它添加属性。会打印 'hello',是因为在 num 对象上添加了属性 abc 并赋值为 'hello'。
这个过程叫做包装,它会将原始数据类型(比如数字)封装成相应的包装对象,使得原始数据类型具备对象的方法和属性。
那你有没有想过这个问题:为什么访问一个原始数据类型的属性时,打印结果是undefined却不是报错呢?
这是因为,当我尝试给原始数据类型添加属性时,JavaScript 会将该数据类型临时转换成一个相应的包装对象(如数字类型的包装对象是通过 Number 构造函数创建),然后给这个临时对象添加属性。但是,这个操作不会影响到原始的数据类型。给你举个例子:
var num = 4
num.len = 3
console.log(num.len);
我们创建了一个原始数据类型的变量 num 并赋值为 4。然后,并尝试在 num 上添加一个属性 len 并赋值为 3。
在这里,JavaScript执行num.len时,引擎会将它当做一个对象属性的访问,将数字4转换为Number对象,并临时添加了len属性。但是,等到引擎缓过神来之后,它发现num是一个数字类型,会立马使用delete操作符移除这个属性。因此,num.len 在这行代码后面的操作中将是undefined。
就相当于进行了以下操作:
var num = new Number(4);
num.len = 3; // 在Number对象上添加len属性
delete num.len; // 移除len属性
console.log(num.len); // 这里引擎又会上头,但是属性已经移除,打印undefined
这就是隐式包装——当你尝试在原始数据类型上调用属性或方法时,JavaScript会自动将原始数据类型转换为对应的包装对象,这个过程被称为隐式包装。
考点
请看考点
var arr = [1,2,3,4,5]
arr.length = 2
console.log(arr)
在这段代码中,创建了一个数组 arr 包含了五个元素 [1, 2, 3, 4, 5]。然后,你将数组的 length 属性设置为 2,这样就会截断数组,只保留前两个元素。
但下面这种情况呢:
var str = 'abcd'
console.log(str.length)
str.length = 2
console.log(str)
在这段代码中,你创建了一个字符串 str,其值为 'abcd'。然后,你使用console.log(str.length) 打印了字符串的长度,这里就会进行隐式包装,接着,你尝试将字符串的 length 属性设置为 2,这里又会进行一次隐式包装,我们知道这样是行不通的。
两个打印结果分别是什么呢?
4
abcd
我们分别来模拟一下两次操作隐式包装:
第一次:console.log(new String('abcd').length)
第二次:new String('abcd').length = 2 ; delete num.len
我们尝试读取原始类型属性的时候,引擎会将原始类型包装成对象,可以轻而易举读取到属性,但是在修改的时候,引擎有充足的时间可以反应,当它发现它是原始数据类型的时候,就会立马删除刚才的修改。
面试题
最后我们用一道面试题来总结今日所学。
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,使得 str 的值变为 'abc1'。接着,使用 typeof 运算符获取 str 的数据类型,结果是字符串 "string"。接下来,你将 typeof 的结果赋给变量 test。然后,你尝试读取 test 的 length 属性,这里会进行第一次隐式包装,得到 test.length 的值是 6,于是进入if语句,又尝试将test不存在的sign属性修改成'typeOf的返回结果可能为String',在这里就会进行第二次隐式包装,最后console.log(test.sign)进行第三次隐式包装。
分别进行模拟分析:
第一次:new String('string').length == 6
第二次:new String('string').sign = 'typeOf的返回结果可能为String';delete test.sign
第三次:console.log(new String(test).sign)
在第三次进行读取的时候,引擎发现该属性已经被删除了,所以打印undefined。
你做对了吗?
结语
包装类为原始类型赋予了新的可能性,使得它们能够像对象一样拥有属性和方法。然而,在实际开发中,我们应该谨慎使用包装类,避免不必要的性能开销。
至此,包装类的全部内容就讲解完毕了,如果这个专栏对你有帮助,不要吝啬你的小爱心啦,当然文章内容还需要完善,如果你有建议跟想法,欢迎评论区留言喔!
END
我的Gitee: CodeSpace (gitee.com)
技术小白记录学习过程,有错误或不解的地方还请评论区留言,如果这篇文章对你有所帮助请 “点赞 收藏+关注” ,感谢支持!!