对象字面量

我们可以将JavaScript中的对象简单地理解为名值对组成的散列表(hash table,也叫哈希表)。在其他编程语言中被称作“关联数组”。其中的值可以是原始值也可以是对象。不管是什么类型,它们都是“属性”(property),属性值同样可以是函数,这时属性就被称为“方法”(method)。

JavaScript中自定义的对象(用户定义的本地对象)任何时候都是可变的。内置本地对象的属性也是可变的。你可以先创建一个空对象,然后在需要时给它添加功能。“对象字面量写法(object literal notation)”是按需创建对象的一种理想方式。

看一下这个例子:

  1. // 定义空对象
  2. var dog = {};
  3. // 添加一个属性
  4. dog.name = "Benji";
  5. // 添加一个方法
  6. dog.getName = function () {
  7. return dog.name;
  8. };

在这个例子中,我们首先定义了一个空对象,然后添加了一个属性和一个方法,在程序的生命周期内的任何时刻都可以:

  • 更改属性和方法的值,比如:

    dog.getName = function () { // 重新定义方法,返回一个硬编码的值 return “Fido”; };

  • 删除属性/方法

    delete dog.name;

  • 添加更多的属性和方法

    dog.say = function () { return “Woof!”; }; dog.fleas = true;

每次都创建空对象并不是必须的,对象字面量模式可以直接在创建对象时添加功能,就像下面这个例子:

  1. var dog = {
  2. name: "Benji",
  3. getName: function () {
  4. return this.name;
  5. }
  6. };

在本书中多次提到“空对象”(“blank object”和“empty object”),这只是一种简称,在JavaScript中根本不存在真正的空对象,理解这一点至关重要。即使最简单的{}对象也会包含从Object.prototype继承来的属性和方法。我们提到的“空(empty)对象”只是说这个对象没有自有属性(own properties),不考虑它是否有继承来的属性。

对象字面量语法

如果你从来没有接触过对象字面量的写法,可能会感觉怪怪的。但越到后来你就越喜欢它。本质上讲,对象字面量语法包括:

  • 将对象主体包含在一对花括号内({})。
  • 对象内的属性或方法之间使用逗号分隔。最后一个名值对后也可以有逗号,但在IE下会报错,所以尽量不要在最后一个属性或方法后加逗号。
  • 属性名和值之间使用冒号分隔。
  • 如果将对象赋值给一个变量,不要忘了在右括号}之后补上分号。

通过构造函数创建对象

JavaScript中没有类的概念,这给JavaScript带来了极大的灵活性,因为你不必提前知晓关于对象的任何信息,也不需要类的“蓝图”(译注:指类的结构)。但JavaScript同样具有构造函数,它的语法和Java或其他语言中基于类的对象创建非常类似。

你可以使用自定义的构造函数来创建对象实例,也可以使用内置构造函数来创建,比如Object()Date()String()等等。

下面这个例子展示了用两种等价的方法分别创建两个独立的实例对象:

  1. // 一种方法,使用字面量
  2. var car = {goes: "far"};
  3. // 另一种方法,使用内置构造函数
  4. // 注意:这是一种反模式
  5. var car = new Object();
  6. car.goes = "far";

从这个例子中可以看到,字面量写法的一个明显优势是,它的代码更少。“创建对象的最佳模式是使用字面量”还有一个原因,它可以强调对象就是一个简单的可变的散列表,而不必一定派生自某个类。

另外一个使用字面量而不是Object()构造函数创建实例对象的原因是,对象字面量不需要“作用域解析”(scope resolution)。因为存在你已经创建了一个同名的构造函数Object()的可能,当你调用Object()的时候,解析器需要顺着作用域链从当前作用域开始查找,直到找到全局Object()构造函数为止。

Object()构造函数的参数

译注:这小节的标题是Object Constructor Catch,恕译者水平有限,实在不知如何翻译,故自作主张修改了本节标题。

创建实例对象时能用对象字面量就不要使用new Object()构造函数,但有时你可能是在别人写的代码基础上工作,这时就需要了解构造函数的一个“特性”(也是不使用它的另一个原因),就是Object()构造函数可以接收参数,通过这个参数可以把对象实例的创建过程委托给另一个内置构造函数,并返回另外一个对象实例,而这往往不是你想要的。

下面的示例代码中展示了给new Object()传入不同的参数(数字、字符串和布尔值),最终得到的对象是由不同的构造函数生成的:

  1. // 注意:这是反模式
  2. // 空对象
  3. var o = new Object();
  4. console.log(o.constructor === Object); // true
  5. // 数值对象
  6. var o = new Object(1);
  7. console.log(o.constructor === Number); // true
  8. console.log(o.toFixed(2)); // "1.00"
  9. // 字符串对象
  10. var o = new Object("I am a string");
  11. console.log(o.constructor === String); // true
  12. // 普通对象没有substring()方法,但字符串对象有
  13. console.log(typeof o.substring); // "function"
  14. // 布尔值对象
  15. var o = new Object(true);
  16. console.log(o.constructor === Boolean); // true

Object()构造函数的这种特性会导致一些意想不到的结果,特别是当参数不确定的时候。最后再次提醒不要使用new Object(),尽可能的使用对象字面量来创建实例对象。