Object.defineProperty
该方法是ES5引入的方法,可以在一个对象上新增一个属性或者直接修改某个属性。该方法接收三个参数
- 某对象
- 要定义或修改的属性的名称或
Symbol - 要定义或修改的属性描述符。
以下是最简易的调用方式:
const obj = Object.defineProperty({},'p',{})
console.log(obj) // {p: undefined}
属性描述符
属性描述符的种类主要有两种
- 数据描述符
- 存取描述符
其实所谓的数据描述符和存取描述符指的都是传递给defineProperty方法第三个参数中对象的属性。
代表数据描述符的属性
value属性对应的值。可以是任何有效的 JavaScript 值(数值,对象,函数等)。 默认为undefined。writable当且仅当该属性的writable键值为true时,属性的值,也就是上面的value,才能被赋值运算符变。 默认为false。
如:
const obj = {}
Object.defineProperty(obj,'p',{
value: 'property',
writable: true
})
console.log(obj) //{p: 'property'}
obj.p = 'newProperty'
console.log(obj) //{p: 'newProperty'}
代表存取描述符的属性
- get
属性的 getter 函数,如果没有 getter,则为
undefined。当访问该属性时,会调用此函数。执行时不传入任何参数,但是会传入this对象。该函数的返回值会被用作属性的值。 - set
属性的 setter 函数,如果没有 setter,则为
undefined。 当属性值被修改时,会调用此函数。该方法接受一个参数(也就是被赋予的新值),会传入赋值时的this对象。 默认为undefined。 如
既可以代表数据描述符又可以代表存取描述符的属性
- configurable
当且仅当该属性的
configurable键值为true时,该属性的描述符才能够被改变,同时该属性也能从对应的对象上被删除。 默认为false。 - enumerable
当且仅当该属性的
enumerable键值为true时,该属性才会出现在对象的枚举属性中。 默认为false。 如
const obj = {}
Object.defineProperty(obj,'newP',{
configurable : true, //这里让属性描述符可改变
writable: true
})
obj.newP = 'setP';
console.log(obj.newP) //setP
Object.defineProperty(obj,'newP',{
writable: false
})
obj.newP = 'wP'
console.log(obj.newP)//这里输入setP说明属性描述符被改变了
数据描述符和存取描述符不可共存,如
vue2的响应式主要是通过Object.defineProperty实现的,其过程如下
- vue实例化的时候,会遍历data的所有属性,为其添加getter和setter
- 当读取数据的时候,getter会被触发,vue2会将其加入依赖收集器当中
- 当给数据赋值的时候,setter会被触发,vue2会在setter中通知依赖收集器更新
Proxy
Proxy是ES6提供的新对象,可以通过它代理对象,并且拦截该对象的所有操作,其只能被new关键字调用,如
new Proxy(target, handler)
其中
- target指的是被代理的对象,它可以是任何类型的对象,包括内置的数组,函数甚至是另一个代理对象。
- handler同样是对象,它的属性提供了某些操作发生时所对应的处理函数。比较常用的有get和set
handler get
handler.get() 方法用于拦截对象的读取属性操作。其会拦截的情况有以下三种:
- 访问属性:
proxy[foo] 和``proxy.bar - 访问原型链上的属性:
Object.create(proxy)[foo] Reflect.get()
get接收三个参数,target,prop,receiver
其中
- target为被代理的对象
- prop为被捕获的属性名
- Proxy或者继承Proxy的对象(即包装代理对象的对象),如
get返回值同样会被当成属性的值
get约束
- 如果要访问的目标属性是不可写(writable false)并且不可配置的(configurable false),则返回的值必须与该目标属性的值相同。如
handler set
handler.set() 方法是设置属性值操作的捕获器。其会拦截得情况有以下三种
- 指定属性值:
proxy[foo] = bar和proxy.foo = bar - 指定继承者的属性值:
Object.create(proxy)[foo] = bar Reflect.set()
handler接收4个参数,target,prop,value,receiver
其中
- target为被代理的对象
- prop为被捕获的属性名
- value为新设置的值
- Proxy或者继承Proxy的对象(即包装代理对象的对象),如
set() 方法应当返回一个布尔值。(在非严格模式下似乎不返回也没什么问题)
- 返回
true代表属性设置成功。 - 在严格模式下,如果
set()方法返回false,那么会抛出一个TypeError异常。
set约束
若目标属性是一个不可写及不可配置的数据属性,则不能改变它的值。如
vue3就是利用Proxy实现响应式
- 在vue3中当初始化一个vue实例的时候,对创建一个Proxy对象,拦截对数据的所有操作
- 在访问数据的时候,代理会收集当前的响应式依赖(如通过get拦截)
- 在操作数据时,代理会通知响应式依赖进行更新(如通过set拦截)