内部方法

在ECMAScript中,每个对象内部都有一系列不暴露给程序员的内部方法。对象不同,内部方法也会不同,但每个对象都会包含一系列必要的内部方法:

内部方法签名描述
[[GetPrototypeOf]]( ) → Object / Null获取该对象继承的对象
返回null:没有继承属性
相当于getPrototypeOf()
[[SetPrototypeOf]](Object / Null) → Boolean为该对象关联原型
参数null:没有继承属性
返回true:操作成功
返回false:操作失败
相当于setPrototypeOf()
[[IsExtensible]]( ) → Boolean判断是否允许向该对象添加其他属性
返回true:允许
返回false:不允许
相当于isExtensible()
[[PreventExtensions]]( ) → Boolean判断该对象当前是否能添加属性
返回true:能
返回false:不能
如果返回false,即使[[IsExtensible]]为true,添加新属性操作也会失败
相当于preventExtensions()
[[GetOwnProperty]](propertyKey) → Undefined | Property Descriptor返回此对象自身属性的属性描述符
如果没有对应的属性,返回undefined
相当于getOwnPropertyDescriptor()
[[DefineOwnProperty]](propertyKey, PropertyDescriptor) → Boolean创建或更改自己的属性
参数propertyKey:属性名
参数PropertyDescriptor:属性描述符
返回true:操作成功
返回false:操作失败
相当于defineOwnProperty()
[[HasProperty]](propertyKey) → Boolean判断此对象是否有或继承某一属性
参数propertyKey:属性名
返回true:有或继承该属性
返回false:没有且不继承该属性
相当于has() / in关键字
[[Get]](propertyKey, Receiver) → any根据属性名返回对象中的属性值
参数propertyKey:属性名
参数Receiver:按ES规范,它会被当作this来用。
相当于get() / 读取操作
[[Set]](propertyKey, value, Receiver) → Boolean将参数value设置为对象中属性名为参数propertyKey的值
参数Receiver:被当作this用
返回true:操作成功
返回false:操作失败
相当于set() / 赋值操作
[[Delete]](propertyKey) → Boolean根据属性名删除对象中的属性
参数propertyKey:属性名
返回true:删除成功
返回false:删除失败
相当于deleteProperty() / delete关键字
[[OwnPropertyKeys]]( ) → List of propertyKey返回对象自己的所有属性键
相当于ownKeys()

还有两个函数特有的内部方法:[[Call]]和[[Construct]]

这些内部方法是多态的,对象不同,其内部的实现也可能不同

对象

JavaScript一切皆对象,分为常规对象和异质对象两种对象,任何不属于常规对象的都是异质对象

区分不同对象的方法就是看它们的内部方法和内部槽

常规对象必须满足以下三点要求:

  • 必要内部方法根据ECMA规范10.1.x的定义实现
  • [[Call]]根据ECMA规范10.2.1的定义实现
  • [[Construct]]根据ECMA规范10.2.2的定义实现

Proxy

Proxy当然也算一种对象,它的内部实现与普通对象不同,所以它是一个异质对象

例如[[Get]]在Proxy的实现:创建Proxy对象时,如果指定了Get()拦截函数,则为拦截函数,否则为普通对象的[[Get]]

也就是说,Proxy其实就是用来自定义对象行为的

Vue2中使用属性描述符的getter和setter函数,也能满足响应式,但它存在很多反直觉的缺陷,例如新增的属性必须要经过额外处理才能成为响应式、数组本身无法响应必须调用重写的数组方法

而Vue3中使用Proxy,接近JavaScript的底层,那么只需要观察ECMAScript里对一些操作的实现,确定其用到的对象内部方法,就可以有针对性的编写适合它的响应式逻辑了