封箱包装器

这些对象包装器服务于一个非常重要的目的。基本类型值没有属性或方法,所以为了访问 .length.toString() 你需要这个值的对象包装器。值得庆幸的是,JS 将会自动地 封箱(也就是包装)基本类型值来满足这样的访问。

  1. var a = "abc";
  2. a.length; // 3
  3. a.toUpperCase(); // "ABC"

那么,如果你想以通常的方式访问这些字符串值上的属性/方法,比如一个 for 循环的 i < a.length 条件,这么做看起来很有道理:一开始就得到一个这个值的对象形式,于是 JS 引擎就不需要隐含地为你创建一个。

但事实证明这是一个坏主意。浏览器们长久以来就对 .length 这样的常见情况进行性能优化,这意味着如果你试着直接使用对象形式(它们没有被优化过)进行“提前优化”,那么实际上你的程序将会 变慢

一般来说,基本上没有理由直接使用对象形式。让封箱在需要的地方隐含地发生会更好。换句话说,永远也不要做 new String("abc")new Number(42) 这样的事情 —— 应当总是偏向于使用基本类型字面量 "abc"42

对象包装器的坑

如果你 确实 选择要直接使用对象包装器,那么有几个坑你应该注意。

举个例子,考虑 Boolean 包装的值:

  1. var a = new Boolean( false );
  2. if (!a) {
  3. console.log( "Oops" ); // 永远不会运行
  4. }

这里的问题是,虽然你为值 false 创建了一个对象包装器,但是对象本身是“truthy”(见第四章),所以使用对象的效果是与使用底层的值 false 本身相反的,这与通常的期望十分不同。

如果你想手动封箱一个基本类型值,你可以使用 Object(..) 函数(没有 new 关键字):

  1. var a = "abc";
  2. var b = new String( a );
  3. var c = Object( a );
  4. typeof a; // "string"
  5. typeof b; // "object"
  6. typeof c; // "object"
  7. b instanceof String; // true
  8. c instanceof String; // true
  9. Object.prototype.toString.call( b ); // "[object String]"
  10. Object.prototype.toString.call( c ); // "[object String]"

再说一遍,通常不鼓励直接使用封箱的包装器对象(比如上面的 bc),但你可能会遇到一些它们有用的罕见情况。