Object.defineProperty
3个参数
- obj: 可以理解为目标对象。
- prop: 目标对象的属性名。
- descriptor: 对属性的描述。
descriptor可分为数据属性和访问器属性两类
//4个数据描述符value,configurable,enumerable,writablelet obj = {};Object.defineProperty(obj, "name", {value: 'ddd',// 这三个参数默认都为falseconfigurable: true, // 是否可删除enumerable: true, // 是否可枚举writable: true, // 是否可修改})
//访问器描述符的含义是:包含该属性的一对 getter/setter方法的对象let obj = {};Object.defineProperty(obj, 'name', {configurable: false, //是否可删除enumerable: false,// 是否可枚举get() {},set(newValue) {}})
注意:
1,使用访问器描述符中 getter或 setter方法的话,不允许使用 writable 和 value 这两个配置项。
2,不能在set方法里设置当前的属性值.会报栈溢出.原因是会造成死循环。
// 例1 用对象中不存在的属性,借助get和set实现获取和设置对象中存在的属性let obj = { name_: '小红' };Object.defineProperty(obj, 'name', {get() {console.log("get被拦截")return this.name_},set(newValue) {console.log("set被拦截")this.name_ = newValue}})obj.name = "小明"console.log(obj.name) //小明console.log(obj) //{name_:'小明'}
简单实现数据双向绑定
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><h1>简单实现数据双向绑定</h1><input type="text" id="inp"><p></p></head><body><script>const inp = document.getElementById('inp')const p = document.getElementsByTagName('p')[0]const obj = { text: '' }inp.oninput = (e) => {obj.text = e.target.value}Object.defineProperty(obj, 'text', {set(v) {inp.value = vp.innerHTML = v}})</script>
</body></html>
对数组的监听
const obj = {}let a = 1;Object.defineProperty(obj, 'arr', {get() {return a},set(v) {console.log('set执行', v)a = v}})obj.arr = [] //set执行obj.arr = [1, 2, 3] // 给obj中的arr属性添加1,2,3, 会执行set方法obj.arr[0] = 3 //set不执行obj.arr.push(4) // set不执行obj.name.length = 5 // 也不会执行set方法
如上执行结果我们可以看到,当我们使用 Object.defineProperty 对数组赋值有一个新对象的时候,会执行set方法,但是当我们改变数组中的某一项值的时候,或者使用数组中的push等其他的方法,或者改变数组的长度,都不会执行set方法。也就是如果我们对数组中的内部属性值更改的话,都不会触发set方法。因此如果我们想实现数据双向绑定的话,我们就不能简单地使用 obj.name[1] = newValue; 这样的来进行赋值了。那么对于vue这样的框架,那么一般会重写 Array.property.push方法,并且生成一个新的数组赋值给数据,这样数据双向绑定就触发了