Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

重学js —— 普通对象和奇异(怪异)对象行为(普通对象内部方法和内置插槽) #64

Open
lizhongzhen11 opened this issue Jan 7, 2020 · 0 comments
Labels
js基础 Good for newcomers 重学js 重学js系列 规范+MDN

Comments

@lizhongzhen11
Copy link
Owner

lizhongzhen11 commented Jan 7, 2020

普通对象和奇异(怪异)对象行为

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

可以配合重学js —— js数据类型(四):Object 基础介绍(内部方法和内置插槽等)查看。

主要讲了 Object 一些api的内部算法过程以及普通对象的生成过程。

所有的普通对象都有一个叫 [[Prototype]] 的内置插槽。该内置插槽的值要么是 null 要么是一个用来实现继承的对象。该 [[Prototype]] 对象的 数据属性 是为了get 使用而不是 set 使用 而继承(作为子对象的属性可见)的。访问器属性是为 get 使用和 set 使用继承的。

每个普通对象有个布尔值 [[Extensible]] 内置插槽,它用于实现基本内部方法的不变量中指定的与可扩展性相关的内部方法不变量。即,一旦对象的 [[Extensible]] 内部插槽值被设为 false,那么:

  1. 不能向该对象添加新的属性!
  2. 不能修改该对象的 [[Prototype]] 内部插槽值!
  3. 不能将 [[Extensible]] 值改为 true

其实对应 Object.preventExtensions()

在接下来的算法描述中,假设 O 是普通对象,P 是属性键,V 是任何ECMAScript语言值Desc 是属性描述符 record。

每个普通的对象内部方法都委托给一个类似名称的抽象操作。如果一个抽象操作依赖于另一个内部方法,那么该内部方法在 O 上被调用,而不是直接调用类似名称的抽象操作。这些语义确保将普通对象内部方法应用于奇异对象时,将调用奇异对象自己覆盖的内部方法。

[[GetPrototypeOf]] ( ) —— Object.getPrototypeOf(O)

O[[GetPrototypeOf]] 内部方法被调用时:

  1. 返回 ! OrdinaryGetPrototypeOf(O)结果

OrdinaryGetPrototypeOf ( O )

  1. 返回 O.[[Prototype]]

[[SetPrototypeOf]] ( V ) —— Object.setPrototypeOf

  1. 返回调用 ! OrdinarySetPrototypeOf(O, V)结果

OrdinarySetPrototypeOf ( O, V )

  1. 断言:V 的类型要么是对象要么是 Null.

  2. 定义 current 变量,值为 O.[[Prototype]].

  3. 如果 SameValue(V, current) 结果为 true,返回 true

  4. 定义 extensible 变量,值为 O.[[Extensible]].

  5. 如果 extensiblefalse,返回 false.(不可扩展

  6. 定义 p 变量,值为 V.

  7. 定义 done,值为 false.

  8. 如果 donefalse 的话,重复以下步骤:

    8.1 如果 pnull,将 done 设为 true

    8.2 否则如果 SameValue(p, O) 结果为 true,返回 false这一步防止对象将自身设为自己的原型对象

    8.3 否则,

    1. 如果 p.[[GetPrototypeOf]] 不是普通对象内部方法,将 done 设为 true
    2. 否则,把 p.[[Prototype]] 的值设为 p
  9. O.[[Prototype]] 的值设为 V

  10. 返回 true

步骤8中的循环保证在任何原型链中都不会有循环,该原型链仅包括使用 [[GetPrototypeOf]][[SetPrototypeOf]] 的普通对象定义的对象。

省去[[IsExtensible]] ( )等相关

[[GetOwnProperty]] ( P ) —— Object.getOwnPropertyDescriptor()

  1. 返回 ! OrdinaryGetOwnProperty(O, P) 结果

OrdinaryGetOwnProperty ( O, P )

O 是对象,P是属性名。目的拿属性描述符!

  1. 断言:IsPropertyKey(P)true

  2. 如果 O 自身没有属性 P,返回 undefined

  3. 定义 D,值为新创建且没有任何字段的属性描述符

  4. 定义 X,值为 O.P

  5. 如果 X数据属性

    5.1 设置 D.[[Value]]X[[Value]] 属性值

    5.2 设置 D.[[Writable]]X[[Writable]] 属性值

  6. 否则,

    6.1 断言 X 为访问器属性

    6.2 设置 D.[[Get]]X[[Get]] 属性值

    6.3 设置 D.[[Set]]X[[Set]] 属性值

  7. 设置 D.[[Enumerable]]X[[Enumerable]] 属性值

  8. 设置 D.[[Configurable]]X[[Configurable]] 属性值

  9. 返回 D

[[DefineOwnProperty]] ( P, Desc ) —— Object.defineProperty()

  1. 返回 ? OrdinaryDefineOwnProperty(O, P, Desc) 结果

OrdinaryDefineOwnProperty ( O, P, Desc )

  1. 定义 current,值为 ? O.[[GetOwnProperty]](P)
  2. 定义 extensible,值为 ? IsExtensible(O) 结果(看它是否可扩展)
  3. 返回 ValidateAndApplyPropertyDescriptor(O, P, extensible, Desc, current)结果

IsCompatiblePropertyDescriptor ( Extensible, Desc, Current )

  1. 返回 ValidateAndApplyPropertyDescriptor(undefined, undefined, Extensible, Desc, Current)结果

ValidateAndApplyPropertyDescriptor ( O, P, extensible, Desc, current )

注意:如果 Oundefined,仅执行校验,不会执行对象更新!

  1. 断言:如果 O 不是 undefined,那么IsPropertyKey(P)结果为 true

  2. 如果 currentundefined

    a. 如果 extensiblefalse,返回 false

    b. 断言:extensibletrue

    c. 如果 IsGenericDescriptor(Desc) 结果是 true 或者 IsDataDescriptor(Desc) 结果是 true

    1. 如果 O 不是 undefined,为 O 创建一个自有的数据属性 P,其属性描述符由 Desc 描述。如果 Desc 中某个属性字段缺少值,将 默认值 赋值给 P 属性描述符中 对应的缺少值的 那个属性。

    d. 否则

    1. 断言:! IsAccessorDescriptor(Desc) 结果为 true
    2. 如果 O 不是 undefined,为 O 创建一个 访问器属性 P,其属性描述符由 Desc 描述。如果 Desc 中某个属性字段缺少值,将 默认值 赋值给 P 属性描述符中 对应的缺少值的 那个属性。

    e. 返回 true

  3. 如果 Desc 每个字段都缺失,返回 true

  4. 如果 current.[[Configurable]]false

    a. 如果 Desc.[[Configurable]] 存在且值为 true,返回 false

    b. 如果 Desc.[[Enumerable]] 存在且 ! SameValue(Desc.[[Enumerable]], current.[[Enumerable]]) 结果为 false,返回 false

  5. 如果 IsGenericDescriptor(Desc) 结果是 true

    a. 注意:无需进一步验证

  6. 如果 ! SameValue(! IsDataDescriptor(current), ! IsDataDescriptor(Desc)) 结果为 false

    a. 如果 current.[[Configurable]]false,返回 false
    b. 如果 IsDataDescriptor(current) 结果为 true

    1. 如果 O 不是 undefined,将 O 对象的 P 属性从 数据属性 转变为 访问器属性。保存转换后的 [[Configurable]][[Enumerable]] 属性的现有值,并将属性的其余部分设置为默认值。

    c. 否则

    1. 如果 O 不是 undefined,将 O 对象的 P 属性从 访问器属性 转变为 数据属性。保存转换后的 [[Configurable]][[Enumerable]] 属性的现有值,并将属性的其余部分设置为默认值。
  7. 如果 IsDataDescriptor(current)IsDataDescriptor(Desc) 结果都是 true

    a. 如果 current.[[Configurable]]false 并且 current.[[Writable]] 也是 false,

    1. 如果 Desc.[[Writable]] 存在并且 Desc.[[Writable]]true,返回 false
    2. 返回 true
  8. 否则,

    a. 断言:! IsAccessorDescriptor(current)! IsAccessorDescriptor(Desc) 都为 true

    b. 如果 current.[[Configurable]]false

    1. 如果 Desc.[[Set]] 存在并且 SameValue(Desc.[[Set]], current.[[Set]]) 结果为 false,返回 false
    2. 如果 Desc.[[Get]] 存在并且 SameValue(Desc.[[Get]], current.[[Get]]) 结果为 false,返回 false
    3. 返回 true
  9. 如果 O 不是 undefined

    a. Desc 每个字段都存在,将对象 OP 属性描述符中相应属性设置为字段的值

  10. 返回 true

[[Get]] ( P, Receiver )

获取对象属性值。

  1. 返回 ? OrdinaryGet(O, P, Receiver) 结果

OrdinaryGet ( O, P, Receiver )

该方法就是各种文章博客说的,数据属性直接返回 [[Value]] 值,访问器属性返回 [[Get]] 调用后的结果值

以前看网上某些文章,讲到访问器对象时,会冒出 getter 这个名词,我当时打印控制台愣是找不到它,心想是哪位大佬定义的然后流传开来,今天才知道,规范就这么定义的!

  1. 断言:P 是属性名
  2. 定义 desc,值为 ? O.[[GetOwnProperty]](P) 的结果(P 的属性描述符)
  3. 如果 descundefined
    1. 定义 parent,值为 ? O.[[GetPrototypeOf]]() 结果(找原型对象)
    2. 如果 parentnull,直接返回 undefined
    3. 返回 ? parent.[[Get]](P, Receiver) 结果 (递归调用去原型链上找)
  4. 如果 desc 是数据属性描述符,返回 desc.[[Value]]
  5. 断言:desc 是访问器属性描述符,
  6. 定义 getter,值为 desc.[[Get]]我就说有些文章动不动冒出来个 getter,我以前看的时候还懵逼,心想这又是哪个人创造的
  7. 如果 getterundefined,返回 undefined
  8. 返回 ? Call(getter, Receiver) 结果

[[Set]] ( P, V, Receiver )

  1. 返回 ? OrdinarySet ( O, P, V, Receiver ) 结果

OrdinarySet ( O, P, V, Receiver )

  1. 断言:P 是属性名
  2. 定义 ownDesc,值为 ? O.[[GetOwnProperty]](P) 结果 (属性描述符)
  3. 返回 OrdinarySetWithOwnDescriptor(O, P, V, Receiver, ownDesc) 结果

OrdinarySetWithOwnDescriptor ( O, P, V, Receiver, ownDesc )

  1. 断言:P 是属性名
  2. 如果 ownDescundefined
    1. 定义 parent,值为 ? O.[[GetPrototypeOf]]() 结果 (拿原型对象)
    2. 如果 parent 不是 null
      1. 返回 ? parent.[[Set]](P, V, Receiver) 结果
    3. 否则,
      1. 设置 ownDescPropertyDescriptor { [[Value]]: undefined, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true }
  3. 如果 ownDesc 是数据属性描述符,
    1. 如果 ownDesc.[[Writable]]false,返回 false
    2. 如果 Receiver 不是对象,返回 false
    3. 定义 existingDescriptor,值为 ? Receiver.[[GetOwnProperty]](P) 结果
    4. 如果 existingDescriptor 不是 undefined
      1. 如果 existingDescriptor 是访问器属性描述符,返回 false
      2. 如果 existingDescriptor.[[Writable]]false,返回 false
      3. 定义 valueDescPropertyDescriptor { [[Value]]: V }
      4. 返回 ? Receiver.[[DefineOwnProperty]](P, valueDesc) 结果
    5. 否则,
      1. 断言:Receiver 目前没有属性 P
      2. 返回 ? CreateDataProperty(Receiver, P, V) 结果
  4. 断言:ownDesc 是访问器属性
  5. 定义 setterownDesc.[[Set]]
  6. 如果 setterundefined,返回 false
  7. 执行 ? Call(setter, Receiver, « V »)
  8. 返回 true

[[Delete]] ( P )

  1. 返回 ? OrdinaryDelete ( O, P ) 结果

OrdinaryDelete ( O, P )

  1. 断言:P 是属性名
  2. 定义 desc,值为 ? O.[[GetOwnProperty]](P) 结果
  3. 如果 descundefined,返回 true
  4. 如果 desc.[[Configurable]]true
    1. 从对象 O 中移除自有的属性 P
    2. 返回 true
  5. 返回 false

[[OwnPropertyKeys]] ( ) —— Object.getOwnPropertyNames()

  1. 返回 ! OrdinaryOwnPropertyKeys(O) 结果

OrdinaryOwnPropertyKeys(O)

  1. 定义 key 为一个新的空 List
  2. 对于作为数组索引的 O 的每个属性键 P,按升序数字索引,
    1. P 作为最后一个元素添加到 keys
  3. 对于字符串类型且不是数组索引的每个属性键 P,按时间顺序递增,
    1. P 作为最后一个元素添加到 keys
  4. 对于 Symbol 类型的每个属性键 P,按时间顺序递增,
    1. P 作为最后一个元素添加到 keys
  5. 返回 keys

OrdinaryObjectCreate ( proto [ , additionalInternalSlotsList ] )

带参数 proto(对象或null)的抽象操作 OrdinaryObjectCreate 用于指定新的普通对象的运行时创建。可选参数 additionalInternalSlotsList 是必须定义为对象一部分的除 [[Prototype]] 和 [[Extensible]] 之外其他内部插槽名称的 List。如果该 List 未提供,将会使用一个新的空 List

  1. 定义 internalSlotsList« [[Prototype]], [[Extensible]] »
  2. 如果 additionalInternalSlotsList 存在,把它每个元素加到 internalSlotsList
  3. 定义 O! MakeBasicObject(internalSlotsList)
  4. O.[[Prototype]] 赋值为 proto
  5. 返回 O

OrdinaryCreateFromConstructor ( constructor, intrinsicDefaultProto [ , internalSlotsList ] )

该抽象操作创建一个普通对象,如果构造器的 "prototype" 属性存在,那么该对象的 [[Prototype]] 值从它取得。否则,将 internalDefaultProto 用于 [[Prototype]]。可选的 internalSlotsList 是一个 List,其必须是对象的一部分其它内部插槽的名称。

  1. 断言:intrinsicDefaultProto 是字符串,它是此规范的 固有对象 名称。对应的对象 必须是一个固有对象,该固有对象将用作对象的 [[Prototype]] 值。
  2. 定义 proto? GetPrototypeFromConstructor(constructor, intrinsicDefaultProto)结果
  3. 返回 OrdinaryObjectCreate(proto, internalSlotsList) 结果

GetPrototypeFromConstructor ( constructor, intrinsicDefaultProto )

确定用于创建与特定构造函数相对应的对象的 [[Prototype]] 值。

注意:如果 constructor 不提供 [[Prototype]] 值,使用的默认值是从构造函数的领域而不是从正在运行的执行上下文中获取的。

  1. 断言: intrinsicDefaultProto 是字符串,并且是此规范某个 固有对象 名称。对应的对象 必须是一个固有对象,该固有对象将用作对象的 [[Prototype]] 值。
  2. 断言: constructor 可被调用
  3. 定义 proto? Get(constructor, "prototype") 结果
  4. 如果 proto 不是对象
    1. 定义 realm? GetFunctionRealm(constructor) 结果
    2. 设置 protorealm 的固在对象 intrinsicDefaultProto
  5. 返回 proto

and so on...

@lizhongzhen11 lizhongzhen11 added js基础 Good for newcomers 重学js 重学js系列 规范+MDN labels Jan 7, 2020
@lizhongzhen11 lizhongzhen11 changed the title 重学js —— 普通对象和奇异对象行为(普通对象内部方法和内置插槽) 重学js —— 普通对象和奇异(怪异)对象行为(普通对象内部方法和内置插槽) Jan 11, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
js基础 Good for newcomers 重学js 重学js系列 规范+MDN
Projects
None yet
Development

No branches or pull requests

1 participant