Object.defineProperty()介绍及应用[vue]

176 阅读3分钟

Object.getOwnPropertyDescriptor(obj,'a'):

先来执行一个代码:

let obj = {
        a:"1212"
    }
      
console.log("来自ES6的属性描述符对象",Object.getOwnPropertyDescriptor(obj,'a'));

image.png

这个方法返回一个属性描述符对象,该对象可能包含以下属性:

  • value:属性的值。该属性的值可以是任何 JavaScript 类型的值。对于通过 Object.defineProperty 或 Object.create 创建的数据属性(即直接赋值而非通过 getter/setter 定义的属性),这个属性会被返回。
  • writable:一个布尔值,表示属性的值是否可以被改变(即是否可以被重新赋值)。
  • get:一个给属性提供 getter 的方法,或者 undefined(如果属性没有 getter)。当读取属性时,这个方法会被调用(不带参数),并返回属性的值。
  • set:一个给属性提供 setter 的方法,或者 undefined(如果属性没有 setter)。当属性值被修改时,这个方法会被调用,并传入新值作为参数。
  • enumerable:一个布尔值,表示属性是否能在 for...in 循环和 Object.keys 方法中被枚举。
  • configurable:一个布尔值,表示属性的描述符是否可以被改变,或者属性是否可以从对象上删除。

如果指定的属性在对象上不存在,Object.getOwnPropertyDescriptor 会返回 undefined

👇🏻 那么这个方法返回的属性描述符对象 就是Object.defineProperty可以显示设置的属性描述符对象属性列表!

Object.defineProperty 数据劫持 :

在 JavaScript 中,使用 Object.defineProperty 方法定义或修改对象的属性时,可以传入一个属性描述符(descriptor)对象来指定该属性的各种特性。

Object.defineProperty() 提供了比简单的赋值操作更强大的功能,允许更细致地控制对象属性的行为。

如果属性是通过普通的赋值操作(如 obj.prop = value)创建的,那么该属性默认是可写的(writable: true),并且可以通过后续的操作重新赋值。而 Object.defineProperty 提供了更细粒度的控制,允许你显式地设置这些属性特性。

一句话:Object.defineProperty 提供了比普通赋值更强大的功能,可以显示设置是否可写等属性

注意:

Uncaught TypeError: Invalid property descriptor. Cannot both specify accessors and a value or writable attribute, ...

确保你在定义对象属性时
要么只使用valuewritable来定义一个非访问器(非getter/setter)属性,
要么只使用getset来定义一个访问器属性。
如果你需要同时定义访问器属性非访问器属性,你需要分开定义它们。

let obj = {a:"xxx"};
let year = 1992;
Object.defineProperty(obj,'a',{
    get(){
        return year;
    },
    set(newValue){
        console.log("数据发生了劫持", newValue);
        if(newValue=='0000'){
            year = 2004
        }
    }
});
obj.a="0000";
console.log(obj.a)

应用:Vue2.x 的数据劫持

建议反复阅读下面4句话:

Vue2 中使用Object.defineProperty 实现数据劫持。

vue遍历递归数据对象所有属性,并使用Object.defineProperty为每个属性设置getter和setter。

当数据读取时候,getter会触发依赖收集;

当数据被修改时候,setter会通知所有依赖于该数据的订阅者watcher,触发相应的update更新逻辑。

缺点:下面三种情况数据修改了但视图没有更新 🖤 😱 !

1、利用索引直接设置一个项时,例如: vm.items[indexOfItem] = newValue

2、修改数组的长度时,例如: vm.items.length = newLength

3、直接给对象赋值新属性,例如: vm.items.newKey = newValue

原因:

受ES5限制,vuejs不能检测到对象属性的添加或删除。

因为vuejs在初始化实例时将属性转为getter/setter。

所以属性必须在data对象上才能让vue转换他,才能是响应的。

这也是问:动态给vue的data添加一个新的属性时为什么页面不更新的答案

解决方案: ❤ 😄 💃🏻

vue 内部通过重写这个函数解决了这个问题:通过使用 Vue.set(target, key, value)
但是在vue3.0中已经不使用这种方式了,而是通过Peoxy对对象进行代理,从而实现数据劫持。缺点是是ES6的语法,有兼容性问题。

还可以使用

  • Object.assign():要生成新的对象
  • $forcecUpdated(): 不建议使用