原始值的包装对象

JavaScript中有五种原始类型:数字、字符串、布尔值、nullundefined。除了nullundefined之外,其他三种都有对应的“包装对象”(primitive wrapper object)。可以通过内置构造函数Number()String()Boolean()来生成包装对象。

为了说明数字原始值和数字对象之间的区别,看一下下面这个例子:

  1. // 一个数字原始值
  2. var n = 100;
  3. console.log(typeof n); // "number"
  4. // 一个Number对象
  5. var nobj = new Number(100);
  6. console.log(typeof nobj); // "object"

包装对象带有一些有用的属性和方法。比如,数字对象就带有toFixed()toExponential()之类的方法,字符串对象带有substring()chatAt()toLowerCase()等方法以及length属性。这些方法非常方便,和原始值相比,这是包装对象的优势,但其实原始值也可以调用这些方法,因为原始值会首先转换为一个临时对象,如果转换成功,则调用包装对象的方法。

  1. // 像使用对象一样使用一个字符串原始值
  2. var s = "hello";
  3. console.log(s.toUpperCase()); // "HELLO"
  4. // 值本身也可以像对象一样
  5. "monkey".slice(3, 6); // "key"
  6. // 数字也是一样
  7. (22 / 7).toPrecision(3); // "3.14"

因为原始值可以根据需要转换成对象,这样的话,也不必为了用包装对象的方法而将原始值手动“包装”成对象。比如,不必使用new String(“hi”),直接使用”hi”即可。

  1. // 避免这些:
  2. var s = new String("my string");
  3. var n = new Number(101);
  4. var b = new Boolean(true);
  5. // 更好更简洁的办法:
  6. var s = "my string";
  7. var n = 101;
  8. var b = true;

不得不使用包装对象的一个场景是,有时我们需要对值进行扩充并保持值的状态。原始值毕竟不是对象,不能直接对其进行扩充。

  1. // 字符串原始值
  2. var greet = "Hello there";
  3. // 为使用split方法,原始值被转换为对象
  4. greet.split(' ')[0]; // "Hello"
  5. // 给原始值添加属性并不会报错
  6. greet.smile = true;
  7. // 但实际上却没有作用
  8. typeof greet.smile; // "undefined"

在这段示例代码中,greet只是临时被转换成了对象,以保证访问其属性、方法时不会出错。而如果是另一种情况,greet通过new String()被定义为一个对象,那么扩充smile属性的过程就会像我们预期的那样。对字符串、数字或布尔值进行扩充的情况很少见,因此建议只在确实有必要的情况下使用包装对象。

当省略new时,包装对象的构造函数将传给它的参数转换为原始值:

  1. typeof Number(1); // "number"
  2. typeof Number("1"); // "number"
  3. typeof Number(new Number()); // "number"
  4. typeof String(1); // "string"
  5. typeof Boolean(1); // "boolean"