一、理解Object.defineProperty()
Vue中的在其他位置(非data函数内) 比如methods使用Object.defineProperty() 创建对象 并不是响应式的,这是因为在渲染挂载阶段,每个data函数中的对象都创建了dep实例对象,当data函数中的数据发生变化时,就会通知watcher执行notify函数进行数据的重新渲染,而在其他位置并不会创建dep实例对象,所以就有了$set解决这一问题
Object.defineProperty()
的作用就是直接在一个对象上定义一个新属性,或者修改一个已经存在的属性
Object.defineProperty(obj, prop, desc)
obj:需要定义属性的当前对象
prop:当前需要定义的属性名
desc:属性描述符
一般通过为对象的属性赋值的情况下,对象的属性可以修改也可以删除,但是通过Object.definePeoperty()定义属性,通过描述符的设置可以进行更精确的控制对象属性
通过Object.defineProperty()为对象定义属性,有两种形式,且不能混合使用,分别为数据描述符、存取描述符
1、数据描述符 --特有的两个属性(value、writable)
注意:当使用了writable和value属性,不允许使用getter或setter这两个方法
-
writable: 描述对象是否可写(是否只读)
-
当我们之间在一个对象上定义某个属性时, writable默认为true
-
当我们通过属性描述符定义一个属性时, writable默认为false
-
let Person = {}; Object.defineProperty(Person, "name", { value: "QQ", // writable默认值是false, 不能改变属性的值 }); Person.name = "qq"; console.log(Person.name); // QQ let Person = {}; Object.defineProperty(Person, "name", { value: "QQ", writable: true, // 可以改变value值 }); Person.name = "qq"; console.log(Person.name); // qq
2、存储描述符 --是由一对 getter、setter函数功能来描述的属性
注意:当使用了getter或setter方法,不允许使用writable和value这两个属性
get
:一个给属性提供getter
的方法,如果没有getter
则为undefined
。该方法返回值被用作属性值。默认为undefined
。 set
:一个给属性提供setter
的方法,如果没有setter
则为undefined
。该方法将接受唯一参数,并将该参数的新值分配给该属性。默认值为undefined
。
getter/setter:
当设置或获取对象的某个属性的值的时候,可以提供getter/setter方法。
-
getter 是一种获得属性值的方法
-
setter是一种设置属性值的方法。
在特性中使用get/set属性来定义对应的方法。
通俗的讲:在访问属性的时候会触发getter函数,在属性值被修改的时候会触发setter函数
var obj = { a: 1 } let v = obj.a Object.defineProperty(obj, 'a', { get() { console.log("读取") return v }, set(val) { console.log("修改") v = val } }) obj.a = 9 //修改 obj.a //读取
注意:get或set不是必须成对出现,任写其一就可以。如果不设置方法,则get和set的默认值为undefined。一般情况下都成对出现的,缺少get获取不到属性值,缺少set可以修改,只是不是通过Object.defineProperty的方式进行修改
当使用存取器描述属性的特性的时候,允许设置一下特性属性:
var obj = {}; Object.defineProperty(obj, "newKey", { get: function () {} | undefined, set: function (value) {} | undefined, configurable: true | false, enumerable: true | false, });
数据描述符和存取描述均具有以下描述符:
configrable 描述属性是否配置,以及可否可以通过delete删除(是否可配置)
当我们之间在一个对象上定义某个属性时, configrable默认为true
当我们通过属性描述符定义一个属性时, configrable默认为false
enumerable 描述属性是否会出现在for in 或者 Object.keys()的遍历中(是否枚举)
当我们之间在一个对象上定义某个属性时, enumerable默认为true
当我们通过属性描述符定义一个属性时, enumerable默认为false
附:
Object.defineProperty()和proxy()最大的区别就是前者监听的是属性而且不可监听变化,而后者监听的是对象而且可以监听变化