对象

JavaScript 中,对象属于非原始类型。同时,对象也是一种复合数据类型,它由若干个对象属性构成。对象属性可以是任意数据类型,如数字、函数或者对象等。当对象属性为函数时,我们通常称之为方法。当然,这只是惯用叫法不同,在本质上并无差别。

对象字面量

对象字面量也叫作对象初始化器,是最常用的创建对象的方法。

数据属性

对象字面量的 数据属性属性名属性值 组成,语法如下所示:

{
    PropertyName: PropertyValue,
}

在该语法中,PropertyName 表示属性名;PropertyValue 表示属性值。对象属性名可以为标识符、字符串字面量和数字字面量,对象属性值可以为任意值。

存取器属性

一个存取器属性由一个或两个存取器方法组成,存取器方法分为 get 方法和 set 方法两种。get 方法能够将属性访问绑定到一个函数调用上,该方法用于获取一个属性值。set 方法可以将对象属性赋值绑定到一个函数调用上,当尝试给该属性赋值时,set 方法就会被调用。存取器属性的语法如下所示:

{
    get PropertyName() {
        return PropertyValue;
    }
    set PropertyName(value) { }
}

存取器属性中的 get 方法和 set 方法不要求同时存在。我们可以只定义 get 方法而不定义 set 方法,反过来也是一样。如果一个属性只定义了 get 方法而没有定义对应的 set 方法,那么该属性就成了只读属性。

可计算属性名

可计算属性名是指在定义对象字面量属性时使用表达式作为属性名。可计算属性名适用于对象属性名需要动态计算的场景之中。属性名表达式求值后将得到一个字符串或 Symbol 值,该字符串或 Symbol 值将被用作对象属性名。它的语法如下所示:

{
    [PropertyExpression]: PropertyValue,
    get [PropertyExpression]() {
        return PropertyValue;
    },
    set [PropertyExpression](value) { }
}

原型对象

每个对象都有一个原型。对象的原型既可以是一个对象,即原型对象,也可以是 null 值。原型对象又有其自身的原型,因此对象的原型会形成一条原型链,原型链将终止于 null 值。原型对象本质上是一个普通对象,用于在不同对象之间共享属性和方法。对象与其原型之间具有隐含的引用关系,如图3-7所示。

image 2024 02 06 13 50 17 460
Figure 1. 图3-7 对象与原型间的引用关系

在图3-7中,animaldogcat 均表示对象。dogcat 对象的原型对象均为 animal 对象;而 animal 对象的原型为 null 值。dogcat 与其原型 animal 之间具有隐含的引用关系,该关系是通过对象的一个内部属性来维持的,即图3-7中的 “[[Prototype]]” 属性。所谓隐含的引用关系是指,对象的原型不是对象的公共属性,因此无法通过对象属性访问来直接获取对象的原型。该图也展示了对象的原型会形成一条原型链,例如从 doganimal 并终止于 null 值的虚线就表示了一条原型链。

原型能够用来在不同对象之间共享属性和方法,JavaScript 中的继承机制也是通过原型来实现的。原型的作用主要体现在查询对象某个属性或方法时会沿着原型链依次向后搜索,如图3-8所示。

image 2024 02 06 13 52 11 026
Figure 2. 图3-8 原型的作用

在访问 dog 对象上的 color 属性时,先尝试在 dog 对象上查找该属性。若找到 color 属性,则返回属性值;若没有找到 color 属性,则沿着原型链在原型对象 animal 中继续查找 color 属性。此例中,dog 对象没有 color 属性而 animal 对象包含了 color 属性,因此最终会返回 animal 对象上的 color 属性值。如果直到原型链的尽头(null 值)也没有找到相应属性,那么会返回 undefined 值而不是产生错误。

如果对象本身和其原型对象上同时存在要访问的属性,那么就会产生遮蔽效果。在这种情况下,当前对象自身定义的属性拥有最高的优先级,如图3-9所示。

image 2024 02 06 14 11 47 263
Figure 3. 图3-9 属性的遮蔽

此例中,在 dog 对象和 animal 对象上都定义了 color 属性。在 dog 对象上读取 color 属性时,dog 对象自身定义的 color 会被优先选用,而原型对象 animal 上的 color 属性会被忽略。

需要注意的是,原型对象在属性查询和属性设置时起到的作用是不对等的。在查询对象属性时会考虑对象的原型,但是在设置对象属性时不会考虑对象的原型,而是直接修改对象本身的属性值。