第三章: 对象 - 类型
类型
对象是大多数 JS 程序依赖的基本构建块儿。它们是 JS 的六种主要类型(在语言规范中称为“语言类型”)中的一种:
string
number
boolean
null
undefined
object
注意 简单基本类型 (string
、number
、boolean
、null
、和 undefined
)自身 不是 object
。null
有时会被当成一个对象类型,但是这种误解源自于一个语言中的 Bug,它使得 typeof null
错误地(而且令人困惑地)返回字符串 "object"
。实际上,null
是它自己的基本类型。
一个常见的错误论断是“JavaScript中的一切都是对象”。这明显是不对的。
对比来看,存在几种特殊的对象子类型,我们可以称之为 复杂基本类型。
function
是对象的一种子类型(技术上讲,叫做“可调用对象”)。函数在 JS 中被称为“头等(first class)”类型,是因为它们基本上就是普通的对象(附带有可调用的行为语义),而且它们可以像其他普通的对象那样被处理。
数组也是一种形式的对象,带有特别的行为。数组在内容的组织上要稍稍比一般的对象更加结构化。
内建对象
有几种其他的对象子类型,通常称为内建对象。对于其中的一些来说,它们的名称看起来暗示着它们和它们对应的基本类型有着直接的联系,但事实上,它们的关系更复杂,我们一会儿就开始探索。
String
Number
Boolean
Object
Function
Array
Date
RegExp
Error
如果你依照和其他语言的相似性来看的话,比如 Java 语言的 String
类,这些内建类型有着实际类型的外观,甚至是类(class)的外观,
但是在 JS 中,它们实际上仅仅是内建的函数。这些内建函数的每一个都可以被用作构造器(也就是一个可以通过 new
操作符调用的函数 —— 参照第二章),其结果是一个新 构建 的相应子类型的对象。例如:
var strPrimitive = "I am a string";
typeof strPrimitive; // "string"
strPrimitive instanceof String; // false
var strObject = new String( "I am a string" );
typeof strObject; // "object"
strObject instanceof String; // true
// 考察 object 子类型
Object.prototype.toString.call( strObject ); // [object String]
我们会在本章稍后详细地看到 Object.prototype.toString...
到底是如何工作的,但简单地说,我们可以通过借用基本的默认 toString()
方法来考察内部子类型,而且你可以看到它揭示了 strObject
实际上是一个由 String
构造器创建的对象。
基本类型值 "I am a string"
不是一个对象,它是一个不可变的基本字面值。为了对它进行操作,比如检查它的长度,访问它的各个独立字符内容等等,都需要一个 String
对象。
幸运的是,在必要的时候语言会自动地将 "string"
基本类型强制转换为 String
对象类型,这意味着你几乎从不需要明确地创建对象。JS 社区的绝大部分人都 强烈推荐 尽可能地使用字面形式的值,而非使用构造的对象形式。
考虑下面的代码:
var strPrimitive = "I am a string";
console.log( strPrimitive.length ); // 13
console.log( strPrimitive.charAt( 3 ) ); // "m"
在这两个例子中,我们在字符串的基本类型上调用属性和方法,引擎会自动地将它强制转换为 String
对象,所以这些属性/方法的访问可以工作。
当使用如 42.359.toFixed(2)
这样的方法时,同样的强制转换也发生在数字基本字面量 42
和包装对象 new Nubmer(42)
之间。同样的还有 Boolean
对象和 "boolean"
基本类型。
null
和 undefined
没有对象包装的形式,仅有它们的基本类型值。相比之下,Date
的值 仅可以 由它们的构造对象形式创建,因为它们没有对应的字面形式。
无论使用字面还是构造形式,Object
、Array
、Function
、和 RegExp
(正则表达式)都是对象。在某些情况下,构造形式确实会比对应的字面形式提供更多的创建选项。因为对象可以被任意一种方式创建,更简单的字面形式几乎是所有人的首选。仅仅在你需要使用额外的选项时使用构建形式。
Error
对象很少在代码中明示地被创建,它们通常在抛出异常时自动地被创建。它们可以由 new Error(..)
构造形式创建,但通常是不必要的。