为什么字符串类型可以调用构造函数String的方法,却又不是它的实例
从所周知,在js中定义一个字符串我们有两种办法:
var a = new String("a"); var a = "a";
第一种方法使用构造函数创建,作为String的实例,自然可以使用 String原型的方法,这个我们不讨论。
第二种方式,给变量a赋值一个原始类型string,它也可以使用String原型的方法,甚至也包含__proto__这个属性,那么看来原始类型string也是一个对象,也是String的实例咯?然而事实是这样吗?
我们通过简单的几行代码测试一下:
console.log(a.__proto__ === String.prototype) //true
首先是a的原型指向了String的原型,OK这个挺符合预期的。
console.log(a.constructor === String) //true
然后a的constructor属性指向了构造函数String,这个也是自然,毕竟constructor就是从String.prototype继承来的。
console.log(a instanceof String); //false
什么?为什么instanceof判断却说a不是String的实例或者说是原型链的一环,instanceof不就是沿着原型链去找吗,连a.__proto__ === String.prototype都成立了啊!
不卖关子了,其实我们所见并非所得:
在读取字符串的时候会创建一个对象,但是这个对象只是临时的,所以我们称它为临时对象,学术名字叫包装对象,说它临时,是因为我们在读取它的属性的时候,js会把这个string
字符串通过new String()
方式创建一个字符串对象,一旦引用结束,这个对象就被销毁了。
原来如此,怪不得会出现这么诡异的现象,为了验证上面这句话的正确性,我们可以尝试给a添加一个属性,如果a是一个实例对象,那么a的属性是可以修改的。
a.b = "b"; console.log(a.b) //undefined
证明了a不是一个对象。其他的原始类型如Number、boolean也并不是一个对象,它们都是通过包装对象来调用构造函数上的方法的。
最后引用《JavaScript权威指南》里面的一句话补充一下:
其实(包装对象)在实现上并不一定创建或销毁这个临时对象,然而整个过程看起来是这样的。