前面两章曾多次提到过JS的内置对象(natives),像是String 或是 Number 这类。现在我们要深入的了解它们了,嘿嘿嘿。

下面列出JS中常见的内置对象

  1. String()
  2. Boolean()
  3. Number()
  4. Object()
  5. Function()
  6. Array()
  7. Date()
  8. Error()
  9. RegExp()
  10. Symbol() // ES6

如果在浏览器中,还有各类的 HTMLDomElement ,ES6里也有许多新的内置对象。

你看,这些内置对象怎么像函数呢?其实你可以把它们理解成对象构造函数。

var s  = new String('I am a String');
typeof s; //=> 'object'

我们使用之前用过的typeof 检查一下,并没有什么区别,可以认为内置对象是对象的子类型,typeof只能显式值的类型,因此只能显示 'object' 'function' 和 'symbol';

[[Class]]属性

既然 typeof 无能为力,那如何确定其他的内置对象的类型呢。这些内置对象都有一个内置的隐藏属性[[Class]],需要用 Object.prototype.toString() 使用 call 绑定到该内置对象上来获取。

  Object.prototype.toString.call( [ ] ); //=> "[object Array]"

返回值中的 Array 就是[[Class]]的属性了,但是这个方法一样有些奇怪的地方

  Object.prototype.toString.call( undefined ); //=> "[object Undefined]"
  Object.prototype.toString.call( null ); //=> "[object Null ]"
  Object.prototype.toString.call( 1 ); //=> "[object Number]"
  Object.prototype.toString.call( 'build-in type' ); //=> "[object String]"

WTF...and more. JS 这个语言中检测方法怎么都不按照基本法啊?!内置对象并没有 null 和 undefined 啊,你们到底凑什么热闹啊,还有 1 和 'buid...' 明明是内置类型,怎么也成 object 了 ??那是因为内置类型会被装箱(Boxing Wrappers),那什么是装箱呢,装箱的东西是能吃还是能走私呢?

装箱(Boxing Wrappers)

前面我们看到在检查内置类型的时候,它们变成了对象,这种行为就叫装箱。那装箱有什么用呢?我猜你可能使用过这个功能但全然没注意过这个问题

'abc'.length; // 3
'lower'.toUpperCase(); // 'LOWER'

看到了吧,第二本书就介绍了JS的原型链机制,我们要知道JS中的所有内置方法都保存在某个构造函数的prototype对象中,但是像 'abc' 1 这种内置类型并不是对象,没有属性也没有方法,自然也没有[[proto]](__proto__),不能依靠原型链向上调用方法,因此在执行某些方法时,JS会把内置类型装箱成对象,让其获取调用原型链上的方法的能力。

看到这里你一定想到了一个好主意,就是如果你须要在一个 for 循环中 使用 'abc'.length,那JS不是每次都要进行装箱,你可以预先构造一个 new String('abc') 对象,这样是不是可以加速运行代码了?有趣的问题,你可千万不要这么做,因为这个问题早期的开发者早就想到了,因此他们已经做了优化,而如果你想来个预优化处理很可能适得其反。所以建议你不要使用构造函数创建一个内置类型对应的内置对象,而是让JS自己去装箱。

拆箱(Unboxing)

既然能装箱,必然也有拆箱的方法,那就是调用 valueOf( ) 这个函数,可以将一个内置对象的primitive值取出。

valueOf( new String('abc') ); // 'abc'

这是一种显式的拆箱方法,而隐式的拆箱方法在第四章 强制转型中介绍。

本章小结

  1. JS的内置对象有10种,都是由 JS 的构造函数用 new 操作符构造的对象(symbol不用new)
  2. 内置对象有个[[Class]]隐藏属性,需要用 Object.prototype.toString.call( native )获取
  3. 上一条的方法也可以对内置类型获取其 [[Class]] 属性,原因是 JS 会在需要时对内置类型进行装箱
  4. 装箱可以让内置类型获取原型链函数调用,但不要手动构造内置类型的对象,这样并不会提升性能
  5. 对内置对象应用 valueOf() 方法可以获取内置对象的 值,这个过程叫拆箱,另外的拆箱方法是强制转型,第四章介绍。