Object-Advanced笔记

发布时间 2023-04-24 09:02:56作者: 泡椒不辣

JavaScript对象进阶

数据劫持

1. 概念

一旦访问或者修改对象的属性时 拦截这个行为(访问/修改) 并对其添加除这个行为外的操作 最后返回结果


2. 实现

2.1 getter和setter

在对象中通过在对象中设置 gettersetter 方法拦截属性的 访问 / 修改

  • get方法无参数 但是有返回值

  • set方法有参数 但是无返回值

  const obj = {
    // 在属性前面加上 _ 代表该属性私有 能被外部访问 但是不应该被外部直接访问
    _name:"bob",
    // 这里的方法名不能和_name相同 否则会无限递归
    get name(){
      console.log(this._name + '被人访问了')
      return this._name;
    },
    set name(value){
      console.log(this._name + '被修改为' + value)
      this._name = value;
    }
  }

  //这里应该使用getter 和 setter 设置的属性名操作
  //而不是对象中的私有属性

  // 在访问有get语法的属性时,调用绑定的函数
  obj.name // bob被人访问了
  // 在改变有set语法的属性时,调用绑定的函数
  obj.name = 'tom' // bob被修改为tom

缺点

  • 每有一个需要劫持的对象属性就要设置一对getter和setter

  • 对象属性和get set后面设置的属性名不能相同 否则会造成无限递归


2.2 Object.defineProperty

Object.defineProperty(obj, prop, descriptor)

  • obj: 要定义属性的对象

  • prop: 要定义或修改的属性的名称或 Symbol

  • descriptor: 要定义或修改的属性描述符

  • 返回值: obj(传递给函数的对象)

descriptor描述符

  • configurable
    表示对象的属性是否可以被删除 以及除 value 和 writable 特性外的其他特性是否可以被修改
    const object = {}
    Object.defineProperty(object, 'name', {
      get() {
        return name;
      },
      set(value) {
        name = value;
      },
      // 属性可以被删除 默认不可删除 configurable : false
      configurable : true
    });
    
    object.name = 'tom';
    console.log(object.name)// tom
    delete object.name
    console.log(object.name)// undefined

  • enumerable
    定义了对象的属性是否可以在 for...in 循环和 Object.keys() 中被枚举
    const object = {}
    Object.defineProperty(object, 'name', {
      value : 'tom',
      enumerable : true
    });
    Object.defineProperty(object, 'age', {
      // 默认无法被枚举 enumerable = false
      value : 18
    });

    for (i in object) {
      console.log(i)
    }

  • value
    该属性对应的值 可以是任何有效的 JavaScript 值(数值 对象 函数等) 默认为 undefined
const object = {};

Object.defineProperty(object, 'name', {
  // name默认值为tom
  value : 'tom'
});

console.log(object.name);//tom

  • writable
    当且仅当该属性的 writable 键值为 true 时,属性的值 也就是上面的 value,才能被赋值运算符改变 默认为 false
    const object = {};

    Object.defineProperty(object, 'name', {
      get(){
        console.log(name + '被访问了');
        return name;
      },
      // object中的name不可被修改
      writable : false;
    });

    console.log(object.name);//tom

  • get
    当访问该属性时 会调用此函数 执行时不传入任何参数 但是会传入 this 对象(由于继承关系 这里的this并不一定是定义该属性的对象) 该函数的返回值为属性的值 默认为 undefined
    const object = {};

    Object.defineProperty(object, 'name', {
      // 访问劫持
      get(){
        console.log(name + '被访问了');
          return name;
      }
    });

    console.log(object.name); //空

  • set
    当属性值被修改时 会调用此函数 该方法接受一个参数(也就是被赋予的新值)会传入赋值时的 this 对象 默认为 undefined
    const object = {};

    Object.defineProperty(object, 'name', {
      // 访问劫持
      get(){
        console.log(name + '被访问了');
        return name;
      },
      // 修改劫持
      set(value){
        name = value
      }
    });

    object.name = 'tom';
    console.log(object.name);//tom

注意:

对象里目前存在的属性描述符有两种主要形式:数据描述符和存取描述符。数据描述符是一个具有值的属性,该值可以是可写的,也可以是不可写的。存取描述符是由 getter 函数和 setter 函数所描述的属性。一个描述符只能是这两者其中之一;不能同时是两者

描述符可拥有的键值

configurable enumerable value writable get set
数据描述符 可以 可以 可以 可以 不可以 不可以
存取描述符 可以 可以 不可以 不可以 可以 可以

2.3 Object.defineProperties

Object.defineProperty(obj, prop1:{
...descriptor
},prop2:{
...descriptor
},...
)

  • obj: 要定义属性的对象

  • prop: 要定义或修改的属性的名称或 Symbol

  • descriptor: 要定义或修改的属性描述符

  • 返回值: obj(传递给函数的对象)

用法和Object.defineProperty大致相同

    var obj = {};
    Object.defineProperties(obj, {
      // obj第一个属性
      'property1': {
        value: true,
        writable: true
      },
      // obj第二个属性
      'property2': {
        value: 'Hello',
        writable: false
      }
    });
Object.defineProperty 区别
  • Object.defineProperty每个属性都要写一遍
  • Object.defineProperties多个属性可以写在一起

2.4 数据代理Proxy

ES6新增本地对象 new Proxy(target, handler)

  • target: 要代理的目标对象
  • handler: 描述符( 以函数作为属性的对象,各属性中的函数分别定义了在执行各种操作时代理的行为 )
  • 返回值: 代理后的对象
    const obj = { name: 'Jack', age: 18 }
    // 开始代理
    const result = new Proxy(obj, {
      // 配置 get 进行代理设置
      get (target, property) {
        // target:要代理的目标对象
        // property:该对象内的每一个属性,自动遍历
        return target[property];
      },
      // 配置 set 进行修改
      set (target, property, val) {
        // target:要代理的目标对象
        // property:该对象内要修改的属性
        // val:要修改的属性的值
        target[property] = val

        // 注意:简单代理需要返回:true
        return true;
      }
    })

Proxy方法和handler方法参考MDN:[https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Proxy]