• Object.defineProperty()方法详解,了解vue2的数据代理


    假期第一篇,对于基础的知识点,我感觉自己还是很薄弱的。
    趁着假期,再去复习一遍

    Object.defineProperty(),对于这个方法,更多的还是停留在面试的时候,面试官问你vue2和vue3区别的时候,不免要提一提这个方法,vue2的底层是Object.defineProperty(),vue3是proxy对象

    顺便把两者的区别再罗列一下:

    1、性能优化:Vue 3引入了响应式系统的重写,使其更加高效。Vue 3使用了Proxy对象,相较于Vue 2中的Object.defineProperty,Proxy能够更好地捕获并跟踪对响应式数据的修改,提供了更好的性能。

    2、更小的体积:Vue 3使用了模块化开发的方法,可以按需导入功能模块,从而减小了框架的整体体积大小。

    3、Composition API:Vue 3引入了Composition API,这是一种新的代码组织方式,它允许开发者通过逻辑组合API创建可重用的逻辑块。相比Vue 2中的Options API,Composition API更加灵活和可组合,使得代码更易于维护和重用。

    4、更好的TypeScript支持:Vue 3对TypeScript的支持更加友好,通过ts-check指令和相关的类型声明,可以提供更好的类型检查和代码智能提示。

    5、Teleport组件:Vue 3中引入了Teleport组件,可以方便地将组件渲染到DOM中的其他位置,这在处理模态框、弹出层等情况下非常有用。

    6、更好的递归组件:Vue 3对递归组件的支持进行了改进,可以更好地处理递归组件的更新和渲染。

    现在关于Object.defineProperty()方法,再深入一些

    Object.defineProperty 是 JavaScript 的一个内置方法,用于在一个对象上定义一个新的属性或修改现有属性的特性。

    语法如下:

    Object.defineProperty(obj, prop, descriptor)
    
    • 1

    参数注解:

    obj:要在其上定义属性的对象。
    prop:要定义或修改的属性的名称。
    descriptor:要定义或修改的属性的描述符对象,包含属性的各种特性。
    
    • 1
    • 2
    • 3

    descriptor 对象(也可称为配置项)包含以下可选属性:

    value:属性的值,默认为 undefined。
    writable:属性的值是否可以被修改。默认为 false,即该属性是只读的。
    enumerable:属性是否可枚举(是否出现在对象的枚举属性中)。默认为 false。
    configurable:属性是否可以被删除或修改特性。默认为 false。
    get:读取时内部调用的函数
    set:写入时内部调用的函数
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    通过 Object.defineProperty 方法在 obj 对象上定义了一个名为 “name” 的属性,该属性的初始值为 “John”,并且可写、可枚举和可配置。后续通过对该属性的赋值操作,可以修改属性的值
    示例:

    const obj = {};
    
    // 定义一个名为 "name" 的可写属性
    Object.defineProperty(obj, 'name', {
      value: 'John',
      writable: true,
      enumerable: true,
      configurable: true
    });
    
    console.log(obj.name); // 输出 "John"
    
    obj.name = 'Tom';
    console.log(obj.name); // 输出 "Tom"
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    直接使用set(修改),get(获取)

    const obj = {
      _name: 'John',
      get name() {
        return this._name;
      },
      set name(value) {
        this._name = value;
      }
    };
    
    console.log(obj.name); // 输出 "John"
    
    obj.name = 'Tom';
    console.log(obj.name); // 输出 "Tom"
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    感觉还是有些不明白吗?那就在大白话一点:

    假如已经有一个对象

    let person = {
    name:'莲花',
    gender:'男',
    }
    
    • 1
    • 2
    • 3
    • 4

    现在要添加一个属性age,值38

    可以直接在对象里添加

    let person = {
    name:'莲花',
    gender:'男',
    age:38
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    假如在实际的工作场景中,要添加的属性是未知的,根据某些条件动态添加,那直接添加肯定不合适
    且直接添加和使用Object.defineProperty()去添加还是有区别的,直接添加的可以随意改,随意删,随意被枚举,而Object.defineProperty()添加的可以控制是否允许改,是否允许删,是否允许被枚举。

    示例:

    let person = {
    name:'莲花',
    gender:'男',
    }
    Object.defineProperty(person,'age',{
    value:38,
    
    })
    //参数1:要添加参数的对象,给person对象添加,所以是person
    //参数2,要添加的属性名,age
    //参数3,配置项
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    打印看下直接添加和Object.defineProperty()添加的区别
    在这里插入图片描述
    直接添加,所有属性名的颜色都是一样的,而Object.defineProperty()创建的,age的属性名颜色比其他两个属性名颜色淡一些,这是因为Object.defineProperty()配置项的enumerable属性默认是false,即默认是不可以被枚举的,不参与遍历。
    如果我们console.log(Object.keys(person)),打印由person对象所有属性名组成的数组,会发现,直接添加的属性名都可以打印出来,而Object.defineProperty()创建的则没有age

    在这里插入图片描述
    想要被枚举的话,enumerable设置为true即可

    let person = {
    name:'莲花',
    gender:'男',
    }
    Object.defineProperty(person,'age',{
    value:38,
    enumerable:true,//控制属性是否可以被枚举,默认为false
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    如果我们想修改age的值,直接添加的,通过person.value即可修改,但是Object.defineProperty()创建的是不能直接修改的,这就要说到writable属性,默认是false,即默认不允许修改,设置为true即可

    let person = {
    name:'莲花',
    gender:'男',
    }
    Object.defineProperty(person,'age',{
    value:38,
    enumerable:true,//控制属性是否可以被枚举,默认为false
    writable:true,//控制属性是否可以被修改,默认为false
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    如果我们不想要age这个属性,想删掉,直接添加的,通过delete person.age即可删除,但是Object.defineProperty()创建的是不能直接修改的,这就要说到configurable属性,默认是false,即默认不允许删除,设置为true即可

    let person = {
    name:'莲花',
    gender:'男',
    }
    Object.defineProperty(person,'age',{
    value:38,
    enumerable:true,//控制属性是否可以被枚举,默认为false
    writable:true,//控制属性是否可以被修改,默认为false
    configurable:true,//控制属性是否可以被删除,默认为false
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    如果现在有个number字段,需求是age的值要读取number的值,即动态赋值

    直接添加:

    let number = 18
    let person = {
    name:'莲花',
    gender:'男',
    age:number
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    这样写,确实是把number的值赋给age了,但是如果后期number的值变了,假设变为20,age的值是不会跟着变的,还是原来的18

    因为语句从上向下执行的时候,第一次定义的时候已经走完了,以后再改number的值,都只是number自己的事,第一次定义完了就结束了,person对象的值不会再改变。
    如果手动修改,重新给age赋值,确实会改变age的值,但是如果想number一改变,age的值就变,那就要用到Object.defineProperty()方法了
    //直接手动修改,重新给age赋值示例:

    
    let number = 18
    let person = {
    name:'莲花',
    gender:'男',
    age:number
    }
    number = 20
    person.age = number
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    //Object.defineProperty()示例:

    
    Object.defineProperty(person,'age',{
    //当读取person的age属性时,get(getter)函数就会被调用,且返回值是age的值
    //get:function(){}
    //return number
    //})
    //简写:
    get(){
    return number
    }
    //当修改person的age属性时,set(setter)函数就会被调用,且会收到修改的具体值
    //打印value,当值改变的时候确实value接收到了,但是age并没有改变,这是因为person的age属性接收的是number的值
    //set确实调用了,但是number变量的值没有改变,所以set中给number赋值
    set(value){
    number = value
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    这时候,如果number的值改变了,会触发get,age直接拿number的值

    如果直接修改person.age,会触发set,先修改number的值,再触发get,age去拿number的值

    Object.defineProperty了解了之后,现在开始了解数据代理

    数据代理:
    通过一个对象代理另一个对象中属性的操作,监听属性的变化、拦截属性的读写等

    示例:

    let obj = {
    a:'1'
    }
    
    let obj2 = {
    b:'2'
    }
    //obj2想访问obj.a,且可以修改obj.a的值
    //给obj2新增属性a
    //get中返回obj.a,即obj2新增的属性a拿的是obj.a的值
    //set中修改,当obj2.a值发生改变的时候,再赋值给obj.a,再触发get,obj2.a再去拿obj.a的值
    Object.defineProperty(obj2,'a',{
    get(){
    return obj.a
    }
    set(value){
    obj.a= value
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    第一篇结束~~~

  • 相关阅读:
    ElementUI用el-table实现表格内嵌套表格
    golang保留小数点后两位,不四舍五入
    lua的GC
    SSM整合
    (最新方法)最简单小鹅通视频下载分析及小白工具下载实现
    android U广播详解(二)
    9个值得收藏的WebGL性能优化技巧
    .Net Core Aop之IResourceFilter
    基于Java的汽车票网上预订系统设计与实现(源码+lw+部署文档+讲解等)
    MySQL recursive 递归
  • 原文地址:https://blog.csdn.net/weixin_49668076/article/details/133420814