对象和原型
对象 Objects
对象是一个用 {} 括起来的 property name-value 对的集合。property 指属性,分数据属性(data property,将键与值相关联)和访问属性(accessor property,将键与两个访问器函数 get 和 set 相关联,以获取或者设置值)。value 指的是 property 的值,可以是各种对象。
ECMAScript 认可两类对象:原生(Native)对象和宿主(Host)对象,原生对象属于语言,而宿主对象由环境提供,比如文档对象、DOM 等。原生对象具有松散和动态的命名属性(对于某些实现的内置对象子类别而言,动态性是受限的)。对象的命名属性用于保存值,该值可以是指向另一个对象的引用,也可以是一些基本的数据类型,比如:String、Number、Boolean、Null 或 Undefined。
构造器 Constructor
JavaScript 开始没有class,对象通过构造器创建。预定义的两大构造器:
-
Function()
一切 function 都是由 Function() 构造出来的。构造出来的 function,都会有 prototype 属性,该属性是用 new Object() 构建出来的,初始公开属性只有一个 constructor。
-
Object()
行为取决输入参数类型。
对象初始化三种方式:
// new 实例化
const obj1 = new Object()
// create 创建
const obj2 = Object.create()
// 字面量
const obj3 = {}
对象赋值
对象的属性可以通过为该属性赋值来创建,或重新赋值。
const obj = {}
obj.x = 1
obj['x'] = 2
对象值读取
通过属性访问器(property accessor)读取。
const val = obj.x
原型 prototypes
每个对象都有一个内置属性,prototype,叫做此对象的原型,因为原型本身也是对象,所以原型也可能有它自己原型,这样就构成了所谓的原型链(prototype chain)。原型链终止于链中原型为 null 的对象。
ECMAScript 为 Object 类型定义了一个内部 [[prototype]] 属性。这个属性不能通过脚本直接访问,但在属性访问器解析过程中,需要用到这个内部 [[prototype]] 属性所引用的对象链。
function Obj1(param) {
// 给创建的对象添加一个名为 x 的属性并将赋值形参 param
this.x = param
}
function Obj2(param) {
// 给创建的对象添加一个名为 y 的属性并将赋值形参 param
this.y = param
}
// 用 Obj1 类的实例替换与 Obj2 类的实例相关联的原型。而且,为 Obj1 构造函数传递了参数 1
Obj2.prototype = new Obj( 1 )
// 将2作为构造函数的第一个参数,创建一个 Obj2 的实例,并赋给变量 a
const a = new Obj2( 2 )
// 被变量 a 所引用的 Obj2 的实例拥有一个原型链。该链中的第一个对象是在创建后被指定给 Obj2 构造函数的 prototype 属性的 Obj1
的一个实例。
// Obj1 的实例也有一个原型,即与 Object.prototype 所引用的对象对应的默认的 Object 对象的原型。
// 最后, Object.prototype 有一个值为 null 的原型,这条原型链到此结束。
// 因为 a 所引用的 Obj2 的实例有一个名为 y 的属性,因此被设置为 2 的该属性的值被赋给了变量 val。
let val = a.y
// 不能从 Obj2 实例自身中读取到 x 命名属性值,因为该实例没有这个属性。
// 然而,在该实例中查找相应的命名属性失败后,解释程序会继续检查其原型对象。
// 而该实例的原型对象是 Obj1 的实例有一个名为 x 的属性并且值为 1,所以这个属性访问器最后会取得值 1。
val = a.x
// 当对象的 a 中没有 toString 属性时,会搜索其原型对象,而当原型对象中不存在该属性时,则会继续搜索原型的原型。
// 而原型链中最终的原型是 Object.prototype,这个对象确实有一个 toString 方法,因此该方法的引用被返回。
val = a.toString
val = a.z
// 返回 undefined,因为在搜索原型链的过程中,直至 Object.prototype 的原型
null,都没有找到任何对象有名为 z 的属性,因此最终返回 undefined。
a.x = 3
// 此时 a 自身也会创建一个名为 x 的属性。这时属性访问器不会再进一步搜索原型链,Obj1 实例的 x
属性值也不会被修改,这种行为叫作遮蔽(shadowing)属性。
原型操作
构造器对象可以通过一个公共的 prototype 属性,来与内部的 [[prototype]] 属性对应的原型对象进行访问和设置。访问对象原型的标准方法是 Object.getPrototypeOf(),也可以用私有属性__proto__。
通过 Object.hasOwn() 静态方法,可以区分属性是否是自身属性(own properties):
const obj = { x: 1 }
Object.hasOwn(obj, 'x')
// 不推荐 hasOwnProperty 方式,其使用的是原型链
Object.prototype.hasOwnProperty()
obj.hasOwnProperty('x')