细说JS中的包装类
前言
阿里面试,想必大家都有所耳闻,问题往往只有短短几行代码,却暗含玄机,如果基础不打扎实,大概率会在一些意想不到的地方栽跟头。唯有一点一点夯实基础,才能 千磨万击还坚劲,任尔东西南北风
正文
今天就笔者昨天看到的一道阿里面试题来聊一聊JS中 包装类 的概念。话不多说,先上题目:
var str = 'abc'
str += 1
var test = typeof(str)
if (test.length == 6) {
test.sign = 'typeof的返回结果可能是String'
}
console.log(test.sign)
多思考多学习,进步才能更快。先独立思考几分钟,有想法后再来看答案哟~ 右滑下方进度条即可查看 --> 答案:undefined
本题其实就三个考点:
① 在四则运算中,string类型与number类型相加时,number类型会转换为string类型,v8引擎会对转换后的二者进行拼接处理。
因此str += 1的结果会是'abc1'
② typeof用于判断JS中的数据类型,对于string、number、boolean、undefined ,typeof返回的均为其自身数据类型string、number、boolean、undefined。
因此var test = typeof(str) <==> var test = 'string'
延伸一下typeof:
在简单数据类型 string、number、boolean、undefined、null中null是比较特殊的,它转为机器语言后是一串0,而typeof在区分简单数据类型和引用数据类型时,是看其转为机器语言后的前三位,引用数据类型前三位是000,typeof由此判断它需要返回object,这个判断的原则就导致了typeof会误以为null也是引用数据类型而返回object。
③ 在JS中简单数据类型是不能具有属性和方法的,属性和方法只有引用数据类型(对象)才能拥有。
那为什么我们又常说字符串string它自带length属性呢?
这就涉及到标题中提及的包装类的概念了,如果你看完①、②还没有想明白为什么答案是undefined,那你不妨先跟着笔者来了解一下JS中包装类的概念。
一、构造函数
也许有的朋友已经开始烦躁了,你不是说要带我了解包装类的概念么?怎么讲起构造函数了?
别急,包装类其实与构造函数的内部实现原理有关,我们首先得先聊明白构造函数内部是如何实现的。
1.构造函数是什么?
构造函数其实又分为系统自带的构造函数与人为定义的构造函数。
前者如:Number函数、String函数、Boolean函数等,由系统自带,可以直接调用
借助系统自带的构造函数创建对象示例:
let num = new Number(123)
console.log(num) // [Number: 123]
let str = new String('123')
console.log(str) // [String: 123]
let bl = new Boolean(true)
console.log(bl) // [Boolean: true]
后者需要人为定义和创建
借助人为定义的构造函数创建对象示例:
function Bag(brand, color) {
this.brand = brand
this.color = color
}
var myBag = new Bag('爱马仕', red)
console.log(myBag) // Bag {brand: '爱马仕', color: 'red'}
此处笔者定义的构造函数Bag()好像没有return任何值,但在借助构造函数Bag()创建的对象myBag中却出现了具体的对象。欸,这是为什么?这就涉及构造函数的内部实现原理了,我们继续往下看。
2.构造函数 内部实现原理
其实刚刚笔者创建的构造函数内部隐式的创建了一个this对象,然后才执行函数体中的代码,向this对象中传值,最后又隐式的返回this对象。
因此,借助构造函数Bag()创建的对象myBag其实就是此处隐式返回的this对象,所以console.log(myBag)结果会如上例所示。
什么隐式创建?什么隐式返回?this我好像听说过,但你这又是在弄啥嘞?能不能讲清楚了啊喂
其实,代码化构造函数的实现原理就会像下面这样:
function Bag(brand, color) {
// 隐式创建this对象
var this = {
brand: ''
color: ''
}
// 函数体执行
this.brand = brand
this.color = color
// 隐式返回this对象
return this
}
var myBag = new Bag('爱马仕', red)
console.log(myBag) // Bag {brand: '爱马仕', color: 'red'}
这样代码化一下是不是就好理解了?
二、包装类
1.什么是包装类?
在JS中,当我们去访问一个简单数据类型的属性和方法时,v8引擎会去借助系统内部的构造函数进行创建实例、查找属性或调用方法、操作完成后delete对应属性和方法的一个过程。这个过程其实就是我们所说的包装类。
2.包装类过程
让我们通过下面三个示例来讲明白包装类的过程:
示例1:
var Person(name, age, sex) {
name = '赵一',
age = 22,
sex = 'man'
}
console.log(a) // 报错 a is not defined
delete Person.name // 删除对象Person的name属性
console.log(Person.name) // undefined
console.log(Person.height) // undefined
console.log(Person.age) // 22
访问一个不存在的变量a,报错a is not defined,这个很常见,不难理解。
但访问对象身上不存在的属性,值为什么会是undefined呢?
这就得衍伸到对象属性的查找了,对象属性的查找是由点运算符(.)引起的,这个运算负责在引用对象内部查找属性,假如未找到属性,那么就会返回undefined。
示例2:
var num = 4
num.len = 3
console.log(num.len) // undefined
JS中万物皆对象,虽然说此处的变量num是简单数据类型中的number类型,按理说不该具有属性和方法。
但是在v8引擎执行这串代码的过程中其实发生了这样一个小插曲:num.len = 3让v8引擎迷惑了一下,你num不是number类型么,怎么还要我去找你的len属性赋值啊!于是v8引擎就会自作主张的去找系统内置的构造函数。
这时示例2中的代码在v8引擎的眼中是这样的:
var num = new Number(4) // 创建一个Number类型对象
new Number(4).len = 3 // 为这个对象实例的len属性赋值
delete Number(4).len // 销毁实例
console.log(num.len) // new Number(4).len => undefined
先是创建了一个number类型的对象,然后再在这个对象实例上去找它的len属性赋值为3;然后v8引擎开始发现不对劲了,它试图亡羊补牢一下,连忙销毁掉这个实例。在它销毁完成后才走到第三行console.log(num.len),此时也就相当于在这个对象中去找它的len属性,既然已经delete掉了,那肯定找不到啊,于是返回undefined。
示例3:
let str = 'abcd' // let str = new String('abcd')
str.length = 2 // new String('abcd').length = 2 => new String('ab')
// delete new String('ab').length
console.log(str.length) // new String('abcd').length => 4
回看面试题
回到开篇的这个问题:
③ 在JS中简单数据类型是不能具有属性和方法的,属性和方法只有引用数据类型(对象)才能拥有。
那为什么我们又常说字符串string它自带length属性呢?
现在你是不是就能回答上来了?
其实访问string的length属性就是在执行包装类的过程,具体的步骤拆解就像上面的示例3一样。
理解了包装类,让我们再回头看看这道阿里面试题:
var str = 'abc'
str += 1 // str = 'abc1'
var test = typeof(str) // test = 'string' 如果到这一步没跟上,回头再看看开篇
if (test.length == 6) {
test.sign = 'typeof的返回结果可能是String'
}
console.log(test.sign) // undefined
为什么最后结果是undefined呢?
①变量test是什么类型?string类型
②string类型是不是简单数据类型?当然是
③那访问它的sign方法是不是又得借助系统内部的构造函数了?当然
④类包装过程哪三步? 创建实例、查找属性和调用方法、操作完成后delete对应属性和方法。
本题的第三个关键点流程如下:
test.length <==> new String('string').length => 6 => 进入if语句
test.sign <==> new String('string').sign
new String('string').sign = 'typeof的返回结果可能是String'
delete new String('string').sign 注意:sign属性操作完就delete了
console.log(test.sign) <==> console.log(new String(string).sign) => undefined
这当然是找不到sign属性的,于是返回undefined
结语
以上就是由一道字节面试题到JS中包装类的介绍。
看完本文如果觉得有收获的话,不妨点个赞支持一下笔者。
觉得写的不错,但又还有些懵的朋友不妨收藏一下,下次想起再回看一下,印象会更深刻哟~
文章可能有一些错漏,欢迎评论指出和讨论。