目录
defineProperty:
众所周知,vue2是通过obj.defineProperty来实现数据劫持的。那么obj.defineProperty是如何来工作以及vue2是如何追踪数据变化的呢?
**obj.defineProperty的工作原理:
obj.defineProperty是ES5提供的一个静态方法,用来直接在一个对象上定义一个新的属性,或者修改其新的属性并将其返回,使用方法:
obj.defineProperty(obj,prop,descriptor)
*obj:目标对象
*prop:一个字符串或 Symbol,指定了要定义或修改的属性键
*descriptor:要定义或修改的属性的描述符
configurable (公用) |
是否可以修改默认属性,默认为false |
enumerable (公用) |
是否可以被枚举,默认为false |
writable (数据描述符) |
是否可以修改这个属性的值,默认为false |
value (数据描述符) |
初始值。可以是任何有效的 JavaScript 值(数字、对象、函数等)默认为undefined |
get (访问描述符) |
用作属性 getter 的函数,如果没有 getter 则为 undefined。当访问该属性时,将不带参地调用此函数,并将 |
set (访问描述符) |
用作属性 setter 的函数,如果没有 setter 则为 undefined。当该属性被赋值时,将调用此函数,并带有一个参数(要赋给该属性的值),并将 this 设置为通过该属性分配的对象。默认值为undefined |
对象中存在的属性描述符主要有两种类型:数据描述符和访问器描述符。数据描述符是一个具有可写或不可写值得属性;访问描述符是由 getter/setter 函数对描述的属性。两种描述符只能去其一。
使用示例:
// 数据描述符
let obj = {
name: "小明"
}
Object.defineProperty(obj,'age',{
configurable: true, //是否可以被delete
enumerable: true, //是否可以被for in枚举或者使用Object.keys()获取到,
writable: true, //定义是否可以被修改,
value: 100
})
console.log("增加属性===",obj.age) //增加属性=== 100
Object.defineProperty(obj,'name',{
configurable: true, //是否可以被delete
enumerable: true, //是否可以被for in枚举或者使用Object.keys()获取到,
writable: true, //定义是否可以被修改,
value: "小李"
})
console.log("修改属性===",obj.name) //修改属性=== 小李
//访问描述符
let tmp = '测试'
// 访问器描述符
Object.defineProperty(obj,'child', {
configurable: true,
enumerable: true,
set(value) {
console.log("set---")
tmp = value
},
get() {
console.log("get---")
return tmp
}
})
console.log("访问描述符==get",obj.child) //get--- 访问描述符== 测试
obj.child = "小花"
console.log("访问描述符==set",obj.child)
//set---
//get---
//访问描述符==set 小花
至于为神魔 obj.defineProperty不能监听数组和对象,可以参考
https://blog.csdn.net/XH_jing/article/details/120413904
总结一下:监听数组或者对象变化,想能消耗过大,所以在2.0这个方案被放弃了
**vue2是如何使用obj.defineProperty
当把一个普通的 JavaScript 对象传入 Vue 实例作为 data
选项,Vue 将遍历此对象所有的 property,并使用obj.defineProperty把这些 property 全部转为 getter/setter,从而在内部让vue能够追踪依赖,在 property 被访问和修改时通知变更。
proxy:
随着vue3的出现,vue的深度响应由obj.defineProperty改为了proxy,proxy又是什么呢
Proxy 对象用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)
let p = new Proxy(target, handler);
*target:要使用 Proxy
包装的目标对象(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理)
*handler:一个通常以函数作为属性的对象,各属性中的函数分别定义了在执行各种操作时代理 p
的行为