Javascript基本包装类型
- 每当读取一个基本类型值的时候,后台就会创建一个对应的基本包装类型的对象,从而让我们能够调用一些方法来操作这些数据
- 为了便于操作基本类型值,ECMAscript还提供了3个引用类型:Boolean、Number、String
是基本类型也是引用类型,所以叫做基本包装类型:
let s = "sss";
let sub = s.substring(2);
s 是基本类型值却有方法?
实际上后台已经自动完成了一系列处理,当第二行访问 s 时,后台会完成下列处理:
- 创建一个 string 类型的实例
- 在实例上调用指定方法
- 销毁这个实例
实际上就相当于执行了下列过程:
let s = new String("sss");
let sub = s.substring(2);
s = null;
上面过程适用于 String Boolean Number
引用类型与基本包装类型的区别
基本数据类型是存放在栈区的,引用类型是同时保存在栈区和堆区中的
假如有以下几个基本类型的变量:
let name = "jozo";
let city = "guangzhou";
let age = 22;
那么它的存储结构如下图:
栈区包括了变量的标识符和变量的值。
引用类型的存储需要在内存的栈区和堆区共同完成,栈区保存变量标识符和指向堆内存的地址。
假如有以下几个对象:
let person1 = {name : "change1"};
let person2 = {name : "change2"};
let person3 = {name : "change3"};
则这三个对象在内存中保存的情况如下图:
- 引用类型的值是对象,保存在堆内存中
- 引用类型的变量实际上是一个指针,它保存在栈中,指向堆内存中的对象
- 复制引用类型变量实际是复制该指针,所以他们都指向同一个对象
引用类型与基本包装类型的主要区别就是对象的生存期
使用 new 操作符创建的引用类型的实例,在执行流离开当前作用域之前,会一直保存在堆内存中。而后台自动创建的基本包装类型的对象,则只存在一行代码的执行瞬间,然后立即被销毁。这意味着我们不能为基本类型的值添加属性和方法。
自动创建的基本包装类型的对象:
var str = "some text";
str.color = "red";
console.log(str.color); // undefined
var s = 'hello';
s[2] = 'x';
console.log(s); // 'hello'
在此,第二行表面上看是为 str 添加了 color 属性,但是仔细回想上面的后台执行的那 3 个步骤,会发现,第二行创建的 String 对象在添加了 color 属性后,被销毁了。执行到第三行时,第三行代码又创建了自己的 String 对象,然而这个对象没有 color 属性。
s[2] = 'x' 表面上是改变了 s 字符串。实际上,改变以后该对象被销毁了。最后输出的 s = 'hello' 是第三行代码新创建的 String 对象。
为了与上面的例子形成对比,我们显式地创建基本包装类型的对象,代码如下:
var str = new String("some text");
str.color = "red";
console.log(str.color); // red
通过引用类型创建基本包装类型的对象
- 可以显式地调用 Boolean 、Number 和 String 来创建基本包装类型的对象,但最好不要这么做。对基本包装类型的实例调用 typeof 会返回 object,而且所有基本包装类型的对象都会被转换为布尔值 true。
- Object 构造函数也会像工厂方法一样,根据传入值的类型返回相应基本包装类型的实例。
let s = new String('');
console.log(!s); // false
let obj = new Object("some text");
console.log(obj instanceof String); // true
要注意的是,使用 new 调用基本包装类型的构造函数,与直接调用同名的转型函数是不一样的。
var value = "25";
var number = Number(value);
console.log(typeof number); // "number"
var obj = new Number(value);
console.log(typeof obj); // "object"