面试准备-对象

292 阅读5分钟

引言

在前面面试准备-JS类型文章中,我们就已经基本介绍了对象类型的基本组成,这篇文章主要介绍,针对普通对象的内部方法以及内部插槽,以及对象的一些基本操作方法。

普通对象内部方法和内部插槽

所有普通对象都有一个称为[[Prototype]]的内部插槽。这个内部槽的值要么是空,要么是一个对象,用于实现继承。 [[Prototype]] 对象的数据属性被继承(并且作为子对象的属性可见)用于获取访问,父对象的获取SetGet访问会按照父对象的访问器属性。

每个普通对象都有一个布尔值 [[Extensible]] 内部槽,用于实现可扩展性相关的内部方法常量。即,一旦对象的 [[Extensible]] 内部槽的值被设置为 false,就不能再向对象添加属性,修改对象的 [[Prototype]] 内部槽的值。

看完了对象的基本介绍,我们来了解一下原型继承以及原型链的大致情况

原型

基于我们之前的学习,我们知道所有的对象都是拥有一个[[Prototype]]原型用于被继承,那么我们在查看[[Construct]]的时候其中OrdinaryCreateFromConstructor就是用于给新构建的对象做原型引用的,那么我们来看看这个方法的内部实现吧

OrdinaryCreateFromConstructor ( constructorintrinsicDefaultProto [ , internalSlotsList ] )

抽象操作 OrdinaryCreateFromConstructor 采用参数构造函数和 intrinsicDefaultProto(字符串)和可选参数 internalSlotsList(内部槽的名称列表),并返回包含 Object 的正常完成或突然完成。它创建一个普通对象,其[[Prototype]] 值是从构造函数的“prototype”属性(如果存在)中检索的。否则,由 intrinsicDefaultProto(在常见的构造函数下,这里传进来字符串"%Object.prototype%") 命名的内部函数将用于 [[Prototype]]internalSlotsList 包含必须定义为对象一部分的其他内部插槽的名称。如果未提供内部插槽列表,则使用新的空列表。调用时,它会执行以下步骤:

  1. 断言:内在默认Proto是此规范的固有对象的名称。相应的对象必须是要用作对象的 [[Prototype]] 值的内部函数。

  2. proto(变量)成为 ?GetPrototypeFromConstructor(constructor, intrinsicDefaultProto).

  3. 返回OrdinaryObjectCreate(proto,internalSlotsList)

我们可以看到 OrdinaryCreateFromConstructor方法内有两个核心函数分别是GetPrototypeFromConstructorOrdinaryObjectCreate,那我们来看看这两个函数的内部实现是怎么样的吧

GetPrototypeFromConstructor( constructorintrinsicDefaultProto )

抽象操作 GetPrototypeFromConstructor 采用参数构造函数(函数对象)和 intrinsicDefaultProto(字符串),并返回包含对象的正常完成或突然完成。它确定应用于创建与特定构造函数对应的对象的 [[Prototype]] 值。该值将从构造函数的“prototype”属性(如果存在)中检索。否则,由 intrinsicDefaultProto 命名的内部函数将用于 [[原型]] 。调用时,它会执行以下步骤:

  1. 断言:内在默认Proto是此规范的固有对象的名称。相应的对象必须是要用作对象的 [[Prototype]] 值的内部函数

  2. 设置proto成为 ?Get( constructor,'prototype')。

  3. 如果 Type(proto) 不是 Object,则

    3.1 让realm成为?GetFunctionRealm(constructor).

    3.2 将 proto 设置为realmintrinsicDefaultProto的固有对象。

  4. 返回proto

OrdinaryObjectCreate( proto [ , additionalInternalSlotsList ] )

抽象操作 OrdinaryObjectCreate 采用参数proto(对象或 null)和可选参数additionalInternalSlotsList(内部插槽名称列表)并返回一个对象。它用于指定新普通对象的运行时创建。additionalInternalSlotsList包含必须定义为对象一部分的其他内部插槽的名称,除了 [[Prototype]] 和 [[Extensible]] 之外。如果未提供additionalInternalSlotsList,则使用新的空列表。调用时,它会执行以下步骤:

  1. internalSlotsList 成为 « [[Prototype]], [[Extensible]] »。
  2. 如果存在additionalInternalSlotsList,请将其每个元素附加到internalSlotsList
  3. Let O be MakeBasicObject(internalSlotsList)
  4. 将 O.[[Prototype]] 设置为proto
  5. 返回 O。

这里我们可以基本认为实际上只有两个步骤:

  1. 根据插槽列表创建一个基本对象
  2. 增加原型引用

那么接下来我们看看是如何通过 插槽列表构建新的一个基础对象的

MakeBasicObject#  ( internalSlotsList )

抽象操作 MakeBasicObject 采用参数 internalSlotsList(内部插槽名称列表)并返回一个 Object。它是所有通过算法创建的 ECMAScript 对象的源代码,包括普通对象和奇异对象。它排除了创建所有对象时使用的常见步骤,并集中了对象创建。调用时,它会执行以下步骤:

  1. 让 obj 成为一个新创建的对象,并包含internalSlotsList中所有的内部插槽
  2. 将 obj 的基本内部方法设置标准对象的定义方法。
  3. 断言:如果调用方不会覆盖 obj 的 [[GetPrototypeOf]] 和 [[SetPrototypeOf]] 的基本内部方法,则 internalSlotsList 包含 [[Prototype]]。
  4. 断言:如果调用方不会覆盖 obj 的所有 [[SetPrototypeOf]]、[[IsExtensible]] 和 [[PreventExtensions]] 基本内部方法,则 internalSlotsList 包含 [[Extensible]]。
  5. 如果 internalSlotsList 包含 [[Extensible]],请将 obj.[[Extensible]] 设置为 true。
  6. 返回 obj。

OK,到这里实际上大致的一个对象的构造过程就清晰啦,我们也大致清楚了

分析

那么实际上我们可以将整体的步骤分成:

  1. 获取为proto构造函数prototype对象的数据
  2. 创建一个新的对象
  3. 把新的对象的[prototype]设置成proto

是不是很简单哈哈哈,当然其实大部分场景下,我们需要分析的是多级继承,那么根据上述对象的叙述,每一个子对象都有都有访问[[prototype]]上数据对象的能力,基于Get访问属性。那么实际上多层继承下的子对象依旧有访问根节点的数据属性的能力,而大部分对象的(包含标准怪异对象)的根对象都是Object.prototype

那么基本对象的知识就到这里了,我们到这里就结束了

完美!

下一章: 面试准备-事件