前言
今天懂得了一个很常见却容易忽略的小问题,要是在面试的时候被问到具体原因,有点难完全回答正确的问题,那么就继续往下看吧。
思考一下:在下面的一段代码里,最终的输出结果是什么?
var str='string'
str.pro='hello'
console.log(str.pro+'world')
原因
JS的数据类型分成两大类,基本数据类型和引用数据类型。基本数据类型的值是保存在栈内存中的简单数据段,按值访问。而引用数据类型的值是保存在堆内存中的对象,按引用访问。
//基本数据类型
var a=10
var b=true
var c='abcd'
//引用数据类型
var d=[]
var e={}
var f=new String('abcde')
我们回到开头那段代码,很明显str是一个基本数据类型,然后str.pro添加了一个属性。但是我们似乎一般只会给对象添加key-value键值对吧?难道基本数据类型也可以吗?
我们接着看另一段代码
var ss='abcd'
console.log(ss.length) //4
变量ss并没有length属性,不是说好了只有对象才能用,或者[]去访问属性值吗?在这里我们就要引入基本包装类型的概念了。除了Object Array等引用数据类型外,其实还有三种特殊的引用类型String Number 和Boolean,方便我们操作与其对应的基本数据类型,而它们就是基本包装类型。ss作为一个基本数据类型是没有length属性的,但是它的基本包装类型String有length属性,其实在执行consolo.log(ss.length)这段代码的流程是:
- 创建String类型的一个实例
- 在实例上调用指定的方法
- 销毁这个实例 所有获取字符串变量ss的长度的代码,内部实现大概是:
var ss='abcd'
var len=ss.length
console.log(len) //4
var ss='abcd'
var _str=new String(str)
var len=_str.length
_str=null
console.log(len) //4
那么我们再次回到开头的例子,就很容易知道结果了。当执行str.pro='hello'时,实际上内部创建了一个基本包装类型的实例,然后给这个实例的pro属性赋值为'hello',实例创建后就马上被销毁,当下一次试图获取str.pro的值时,又会创建一个基本包装类型的实例,显然新创建的实例没有pro属性的,值为undefined,所以最后输出undefinedworld。
在针对字符串、数字和布尔值使用字面量时,只有在该值被视为对象的情况下才会创建实例的复杂对象。换句话说,在尝试使用与构造函数关联的方法或检索属性(如 var len='abcd'.length)之前,一直在使用基本数据类型。当这种情况发生时,JS会在背后为字面量值创建一个包装器对象,以便将该值视为一个对象。调用方法后,JS会抛弃包装器对象,该值返回字面量类型。这就是字符串、数字、布尔值被认为是原始数据类型的原因。