Object.defineProperty
3个参数
obj: 可以理解为目标对象。
prop: 目标对象的属性名。
descriptor: 对属性的描述。
descriptor可分为数据属性和访问器属性两类
//4个数据描述符value,configurable,enumerable,writable let obj = {}; Object.defineProperty(obj, "name", { value: 'ddd', // 这三个参数默认都为false configurable: 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 = v p.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方法,并且生成一个新的数组赋值给数据,这样数据双向绑定就触发了