引子

你有没有思考过一个问题,一个 number 类型的原始值,为什么可以调用方法。比如说:

  1. let num = 12;
  2. let str = num.toString();
  3. console.log(typeof str); // string

num 难道不是一个原始类型吗?没错,它是原始类型。那既然如此,为什么会有方法?

这就要提到一个东西,名为包装类。

包装类

概述

JavaScript为了方便我们操作原始值,为我们提供了一种特殊的类,名为包装类。

  • Number
  • String
  • Boolean
  • Symbol
  • BigInt

每当我们用到这些原始值的方法或者属性的时候,JavaScript 帮我们自动创建一个相应的原始包装类对象。

我们用包装类的思想分析一下下面的代码:

  1. let num = 12;
  2. let str = num.toString();
  3. console.log(typeof str); // string

在执行 num.toString() 的时候发生了什么:

  1. 创建一个 Number 类型的实例对象,并且这个对象的值为 12
  2. 对这个对象调用它的方法 toString() ,这个方法正好会返回一个字符类型的 12
  3. 执行完之后,销毁这个对象

转成代码,我们可以这么理解这三个步骤

  1. let newObj = new Number(12);
  2. // 返回 '12'
  3. newObj.toString();
  4. // 销毁对象
  5. newObj = null;

显式创建包装类

如果需要显式的创建包装对象,我们需要借用 Object()

  1. const strObj = new Object('str');
  2. const numObj = new Object(1);
  3. const booleanObj = new Object(false);
  4. const symbolObj = new Object(Symbol(1));

实际上,对于前面三种类型,我们还可以使用下面这种方式创建包装对象

  1. const strObj = new String('str');
  2. const numObj = new Number(1);
  3. const boolObj = new Boolean(false);

但是并不推荐使用这种方式创建,因为 ES6 并不支持这几种方法,只不过是由于兼容问题,还能够创建。

使用建议

并不推荐显式的创建包装类的实例(尤其是 Boolean 类型),因为可能会存在一些问题。

  1. let bool = new Object(false);
  2. console.log(bool && true); // true

分析:

  • 变量 bool 是一个对象,它的原始值是 false
  • 对象 bool 不是一个falsy值
  • 所以 隐式转型之后,bool 的 布尔值应该是true
  • 因此会判断第二个,而第二个值是true ,因此最后返回true

自动生成对象与显式生成对象的区别

所生成对象的生命周期不同。

  • 通过 new 关键字实例化的对象,只会在离开作用域的时候销毁
  • 通过包装类型自动生成的对象,在使用完之后直接就会销毁

注意措辞,这里说的是自动生成,也就是通过读模式访问的时候。也就是说,我们如果手动使用原始值包装类是不会被自动销毁的。

  1. let num = 12;
  2. let numStr = num.toString();
  3. // 执行完之后, num所生成的对象就又被销毁了, 访问不到
  4. let numObj = new Object(12);
  5. // 我们手动创建的一个 Number 对象
  6. // let numObj = new Number(12); 这样做也可以, 但是不推荐
  7. console.log(numObj instanceof Number); // true

注意为什么不是 ?

由于Symbol和BigInt的出现,对它们调用new都会报错,目前ES6规范也不建议用new来创建基本类型的包装类。

总结

由于原始包装类型的存在,JavaScript中的原始值可以被当成对象来使用。有 5 种原始值包装类型:

  • Boolean
  • Number
  • String
  • Symbol
  • BigInt

它们都具有以下特点:

  • 每种包装类型都映射到同名的原始类型
  • 以读模式访问原始值的时候,后台会实例化一个原始值的包装类对象,借助这个对象可以操作对应的数据
  • 语句执行完毕后,包装对象会被自动销毁