深入理解数据双向绑定方法 Object.defineProperty()

1、Object.defineProperty() 是什么?

Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象,Vue 的底层中很多地方用到了这个方法,比如数据代理计算属性等等,下面让我们开始掌握它 👌。

2、Object.defineProperty() 使用方法

Object.defineProperty(obj, prop, descriptor)
  • obj:要添加或者修改属性的对象
  • prop: 要添加或者修改属性的名称或 Symbol
  • descriptor: 要添加或者修改的属性描述符

例:

let person = {
	name: '张三',
	gender: '男'
};
object.defineProperty(person, 'age', {
	value: '18'
});

console.log(person) // => { name: '张三', gender: '男', age: 18 }

在ES6中,由于 Symbol 类型的特殊性,用Symbol类型的值来做对象的key与常规的定义或修改不同,而Object.defineProperty 是定义key为Symbol的属性的方法之一。

3、 属性描述符(descriptor)

属性描述符选项实际上就是一个对象。一共有 6 个可选键值,这些可选键值又可分为两类,数据描述符存取描述符,又称数据属性描述符访问器属性描述符。这两种描述符可看作一个独立的对象。它们分别拥有以下可选键值 :

  • configurable
    configurable 属性表示对象的属性是否可以被删除,以及除 valuewritable 特性外的其他特性是否可以被修改。

  • enumerable:只有当该属性的 enumerable 键值为 true 时,才可以在 for...in 循环和 Object.keys() 中被枚举。

  • value:该属性对应的值。可以是任何有效的 JavaScript 值(数值,对象,函数等)。

  • writable:只有该属性的 writable 键值为 true 时,属性的值,也就是上面的 value,才能被 赋值运算符 (zh-CN) 改变,当 writable 属性设置为 false 时,该属性被称为“不可写的”。它不能被重新赋值。

  • get:属性的 getter 函数,如果没有 getter,则为 undefined。当访问该属性时会调用此函数。执行时不传入任何参数,但是会传入 this 对象(由于继承关系,这里的this并不一定是定义该属性的对象)。该函数的返回值会被用作属性的值。

  • set:属性的 setter 函数,如果没有 setter,则为 undefined。当属性值被修改时,会调用此函数。该方法接受一个参数(也就是被赋予的新值),会传入赋值时的 this 对象。

configurableenumerablevaluewritablegetset
数据描述符可以可以可以可以不可以不可以
存取描述符可以可以不可以不可以可以可以
默认值falsefalseundefinedfalseundefinedundefined

默认值是指在使用 Object.defineProperty() 定义属性时的默认值

如果一个描述符配置了数据描述符独有的键值,那么将不能再使用存取描述符独有的键值,也就是不能同时拥有 value 或 writableget 或 set 键,否则程序将抛出下列错误:在这里插入图片描述

4、与普通字面量定义的对象属性作对比

4.1、可枚举性(enumerable)

true 时可枚举,为 false 时不可枚举。

在这里插入图片描述
在这里插入图片描述

/** 字面量定义 ———————————————*/
let person = {
	name: '张三',
	gender: '男'
}
console.log(Object.keys(person)) // => ['name', 'gender']


/** 通过 Object.defineProperty() 修改描述符为不可枚举后 ———————————————*/
Object.defineProperty(person, 'gender', {
	enumerable: false
})
console.log(Object.keys(person)) // => ['name']


/** Object.defineProperty() 定义, enumerable 默认为 false ———————————————*/
let person2 = {
	name: '法外狂徒'
}
Object.defineProperty(person2, 'gender', {
	value: '男',
	enumerable: false // => 写 false 与不写描述符时效果相同,默认 false
})
console.log(Object.keys(person2)) // => ['name']

for(let key in person2) {
    console.log(key) // => name
}


/** Object.defineProperty() 定义, enumerable: true ———————————————*/
let person3 = {
	name: '张三年'
}
Object.defineProperty(person3, 'gender', {
	value: '男',
	enumerable: true
})
console.log(Object.keys(person3)) // => ['name', 'gender']

for(let key in person3) {
    console.log(key) // => name, gender
}
4.2、可配置性(configurable)

true 时可以重复使用 Object.defineProperty() 进行对属性描述符的修改,false 时不可修改,再次定义或修改将抛异常。

/** 字面量定义 ———————————————*/
let person = {
    name: '张三',
    age: 18
}
delete person.age // => true
console.log(person) // => { name: '张三' }


/** Object.defineProperty() 定义, configurable默认为 false ———————————————*/
let person2 = {
    name: '隔壁老王'
}
Object.defineProperety(person2, 'age', {
	value: 18
})

delete person2.age // => false
console.log(person2) // => { name: '隔壁老王', age: 18 }

try{
  Object.defineProperty(person2, 'age', {
	  value: 80
  })
} catch(err) {
  console.log(err) // => 抛错,无法修改属性的描述符,包括: value, configurable, enumerable 等
  /** 
	TypeError: Cannot redefine property: address
    at Function.defineProperty (<anonymous>)
    at index.html:27
  */
}


/** Object.defineProperty() 定义, configurable: true ———————————————*/
let person3 = {
    name: '老罗'
}
Object.defineProperty(person3, 'age', {
	value: 18,
	configurable: true
})
console.log(person3) // => { name: '老罗', age: 18 }

Object.defineProperty(person3, 'age', {
	value: 80,
	enumerable: true
})
console.log(person3) // => { name: '老罗', age: 80 }
console.log(Object.keys(person3)) // => ['name', 'age']
  
delete person3.age // => true
console.log(person3) // => { name: '老罗' }
4.3、可重写性(writable)

true 时可使用字面量定义属性值,反之无法使用。

/** 字面量定义 ———————————————*/
let person = {
    name: '张三',
    age: 18
}
person.address = '广州'
console.log(person) // => { name: '张三', age: 18, address: '广州' }


/** Object.defineProperty() 定义, enumerable 默认为 false ———————————————*/
let person2 = {
    name: '李四',
    age: 18
}
Object.defineProperty(person2, 'address', {
	value: '广州'
})
console.log(person2.address) // => '广州'

person2.address = '深圳'
console.log(person2.address) // => '广州'


/** Object.defineProperty() 定义, enumerable: true ———————————————*/
let person3 = {
    name: '王五',
    age: 18
}
Object.defineProperty(person3, 'address', {
	value: '广州',
	enumerable: true
})
console.log(person3.address) // => '广州'

person3.address = '深圳'
console.log(person3.address) // => '深圳'
4.4、查找属性描述符(Object.getOwnPropertyDescriptor)

Object.getOwnPropertyDescriptor() 方法返回指定对象上一个自有属性对应的属性描述符。(自有属性指的是直接赋予该对象的属性,不需要从原型链上进行查找的属性)
参数:

  • obj:需要查找的对象
  • prop:目标对象内属性名称

字面量定义的属性默认描述符:

let person2 = {
  name: '张三',
  age: 18
}
console.log(Object.getOwnPropertyDescriptor(person2, 'age'))

在这里插入图片描述
Object.defineProperty() 定义的属性默认描述符:

let person2 = {
  name: '张三'
}
Object.defineProperty(person2, 'age', {
	value: 8
})
console.log(Object.getOwnPropertyDescriptor(person2, 'age'))

在这里插入图片描述

字面量定义的对象属性默认可枚举,可配置删除,可写的原因就在于,它是一个键值默认都为 true 的数据描述符,除默认值与 Object.defineProperty() 定义的数据描述符不同外,使用方法一致。

5、getter + setter 实现数据绑定

在字面量定义对象属性时,如果我们的一个属性依赖另一个变量,需要做到数据更新,代码如下:

let address= '广州'
let person = {
	name: '小李子',
	address  // 名称相同时简写,等同 address: address
}
console.log(person) // => { name: '小李子', address: '广州' }

address = '深圳'
console.log(address) // => 深圳
console.log(person) // => { name: '小李子', address: '广州' }

person.address= address
console.log(person) // => { name: '小李子', address: '深圳' }

而在 Object.defineProperty() 中,我们只需要使用存取描述符 getset,就能实现数据的双向绑定:

let address = '广州'
let person = {
	name: '小李子'
}
Object.defineProperty(person, 'address', {
  get() {
    return address
  },
  set(val) {
    address = val
  }
})
console.log(person.address) // => '广州'

address = '深圳'
console.log(address) // => '深圳'
console.log(person.address) // => '深圳'

person.address = '北京'
console.log(address) // => '北京'
console.log(person.address) // => '北京'

在严格模式下 getter setter 必须是成对出现的,否则会抛异常。

以上就是我对于 Object.defineProperty() 方法的个人理解,觉得写的不错,别忘了来个点赞和收藏,你的鼓励是我坚持的动力~~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

超喜欢你呦

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值