Open nfssuzukaze opened 3 years ago
configurable
false
enumerable
value
undefined
writable
get
set
const obj1 = {} const obj2 = {} Object.defineProperty(obj1, 'name', { value: 'saber', // 数据描述符, 表示该属性的值 writable: true // 数据描述符, 表示该属性(此处是指 obj1.name 属性)是可以修改的 }) Object.defineProperty(obj2, 'name', { value: 'saber', writable: false }) console.log(obj1.name) //=> saber console.log(obj2.name) //=> saber obj1.name = 'archer' console.log(obj1.name) //=> archer obj2.name = 'berserker' console.log(obj2.name) //=> saber
const obj = {} obj._number = 0 Object.defineProperty(obj, 'number', { get() { // 当 obj 的 number 属性被读取时, 触发该函数 console.log('getter is touched') return obj._number }, set(value) { // 当 obj 的 number 属性被修改时, 触发该函数 console.log('setter is touched') obj._number = value } }) console.log(obj.number) //=> getter is touched, 0 obj.number = 1 //=> setter is touched console.log(obj.number) //=> getter is touched, 1
const obj = {} obj.age = 18 Object.defineProperty(obj, 'name', { value: 'saber', writable: true, enumerable: false, // 属性不能在 for...in 和 Object.keys() 中被枚举 configurable: false // 除 value 和 writable 以外的描述符都不可更改 }) for (let key in obj) { console.log(key + ":" + obj[key]) } //=> age:18 console.log(Object.keys(obj)) //=> ["age"] obj.name = 'archer' console.log(obj.name) //=> archer Object.defineProperty(obj, 'name', { enumerable: true }) //=> Uncaught TypeError: Cannot redefine property: name
关于后续的 defineProperty 是否会改变其他描述符的值
defineProperty
const obj = {} Object.defineProperty(obj, 'name', { get() {return 'saber'}, configurable: true }) console.log(Object.getOwnPropertyDescriptor(obj, 'name')) //=> {set: undefined, enumerable: false, configurable: true, get: ƒ} Object.defineProperty(obj, 'name', { set(value) {return false} }) console.log(Object.getOwnPropertyDescriptor(obj, 'name')) //=> {enumerable: false, configurable: true, get: ƒ, set: ƒ}
可知, 后续的 defineProperty 并不会改变除设置之外的描述符
<template> <div> <div @click="addN1"> {{n1}} </div> <div @click="addN2"> {{n2}} </div> <div> {{result}} </div> </div> </template> <script> export default { data() { return { n1: 0, n2: 0 } }, methods: { addN1() { this.n1 ++ }, addN2() { this.n2 ++ } }, computed: { result() { return this.n1 + this.n2 } } } </script> <style scoped></style>
在此组件中, 第三个 div 中的数字会随着前两个 div 的数字的变化而变化. 这就是通过上面的 Object.defineProperty 定义 getter/setter 实现所实现的
div
Object.defineProperty
getter/setter
如 Vue.js 文档所说:
Vue.js
当你把一个普通的 JavaScript 对象传入 Vue 实例作为 data 选项,Vue 将遍历此对象所有的 property,并使用 Object.defineProperty 把这些 property 全部转为 getter/setter 每个组件实例都对应一个 watcher 实例,它会在组件渲染的过程中把“接触”过的数据 property 记录为依赖。之后当依赖项的 setter 触发时,会通知 watcher,从而使它关联的组件重新渲染。
当你把一个普通的 JavaScript 对象传入 Vue 实例作为 data 选项,Vue 将遍历此对象所有的 property,并使用 Object.defineProperty 把这些 property 全部转为 getter/setter
data
每个组件实例都对应一个 watcher 实例,它会在组件渲染的过程中把“接触”过的数据 property 记录为依赖。之后当依赖项的 setter 触发时,会通知 watcher,从而使它关联的组件重新渲染。
let vm = new Vue({ data: { a: 1 } }) vm.b = 2 // vm.b 并不是响应式的, 因为 Vue 并没有将其记录为依赖(没有 setter 和 getter)
解决方法一 : 在 data 中就定义该属性
let vm = new Vue({ data: { a: 1, b: undefined } }) vm.b = 2 // 此时的 vm.b 是响应式的
解决方法二 : 使用 Vue 提供的 Vue.set 方法 或 vm.$set 实例方法
Vue
Vue.set
vm.$set
let vm = new Vue({ data: { obj: { a: 1 } } }) vm.$set(vm.obj, 'b', 2) 或 Vue.set(vm.obj, 'b', 2) // 不能直接在 data 对象上修改
let vm = new Vue({ data: { items: ['a', 'b', 'c'] } }) vm.items[1] = 'd' // 不是响应式 vm.items.length = 5 // 不是响应式
解决方法一 : 通过 Vue.set 或 vm.$set 为数组设置(更改或新增)属性
Vue.set(vm.items, property, newValue)
解决方法二 : 通过 vm.someArr.splice 方法来设置(更改或新增)属性
vm.someArr.splice
vm.someArr.splice(index, count, newValue)
Vue 不允许动态添加根级响应式 property , 所以必须在初始化实例之前声明所有的根级响应式 property , 哪怕只是一个空值
property
从 JS 的 Object.defineProperty 到 Vue 的数据响应式
1. Object.defineProperty 的用法
configurable
false
enumerable
false
value
undefined
writable
false
get
undefined
set
undefined
1.1 数据描述符
1.2 访问器描述符
3. 公用描述符
关于后续的
defineProperty
是否会改变其他描述符的值可知, 后续的
defineProperty
并不会改变除设置之外的描述符2. Vue 的数据响应式
在此组件中, 第三个
div
中的数字会随着前两个div
的数字的变化而变化. 这就是通过上面的Object.defineProperty
定义getter/setter
实现所实现的如
Vue.js
文档所说:2.1 Vue 不能保证数组和对象的响应式
2.1.1 对于对象
解决方法一 : 在
data
中就定义该属性解决方法二 : 使用
Vue
提供的Vue.set
方法 或vm.$set
实例方法2.1.2 对于数组
解决方法一 : 通过
Vue.set
或vm.$set
为数组设置(更改或新增)属性解决方法二 : 通过
vm.someArr.splice
方法来设置(更改或新增)属性2.1.3 注意
Vue
不允许动态添加根级响应式property
, 所以必须在初始化实例之前声明所有的根级响应式property
, 哪怕只是一个空值