Object.defineProperty

发布时间 2023-07-27 11:44:08作者: 加利福尼亚的阳光

ES5提供了Object.defineProperty方法,该方法可以在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回这个对象。

语法

/* 
  参数1: 操作的对象
  参数2: 要操作或修改的对象的键
  参数3: 将被定义或修改的属性的描述符
*/
Object.defineProperty(obj, prop, descriptor)

// 栗子
Object.defineProperty(obj, 'age', {
  value: 18,  // 值, 默认为undefined
  writable: true, // 是否能被赋值运算符改变(+、+=、/等), 默认为false
  enumerable: true, // 是否能被改变, 默认为 false 
  configurable: true, // 是否能被枚举, 默认为false

  /* 
    存取描述符
    不可和 value writable 时出现
  */
  get () { }, // get 方法返回键的值, 默认为 undefined
  set (val) { } // set 方法接收新的val参数,修改键的值,默认为 undefined
});

新增属性栗子

let obj = {}
let firstName = 'JOJO'

Object.defineProperty(obj,'firstName', {
  get() {
    console.log('触发了get')
    return firstName
  },
  set(val) {
    console.log('触发了set')
    firstName = val
  }
})
obj.firstName = '鸡哥';

console.log(obj.firstName);
/* 
  打印输出: 
    触发了set // obj.firstName = '鸡哥'; 触发
    触发了get // obj.firstName; 触发
    鸡哥  // console.log(obj.firstName); 触发
*/

修改属性栗子

let obj = {
  _firstName: 'JOJO'  // 定义一个私有变量存值
};

// 新键值 避免栈溢出
Object.defineProperty(obj, 'firstName', {
  get () {
    console.log('get');
    return this._firstName;
  },
  set (val) {
    console.log('set');
    this._firstName = val;
  }
});

obj.firstName = '鸡哥';

console.log(obj.firstName);
/* 
  打印输出:
    set
    get
    鸡哥
*/

深度侦听对象、侦听数组

const obj = {
  firstName: 'JOJO',
  age: 18,
  children: {
    obj2: {
      firstName: '鸡哥'
    },
    obj3: {
      firstName: '坤坤'
    }
  },
  hobby: ['吃饭', '睡觉'],
};

function ob (params) {
  if (typeof params != 'object') return;
  for (const key in params) {
    f(params, key, params[key]);
  }
}

function f (obj, key, value) {
  // 多层嵌套对象
  ob(value);
  Object.defineProperty(obj, key, {
    get () {
      console.log('get', key, value);
      return value;
    },
    set (val) {
      // 避免修改的值是一个对象
      ob(val);
      console.log('set', key, val);
      value = val;
    }
  });
}

ob(obj);

obj.children.obj2.firstName = '被修改了'  
/* 
  分别输出: 逐级遍历
    get children {...}
    get obj2 {...}
    set firstName 被修改了 
*/

obj.hobby[0] = '被修改了'
/* 
  分别输出:根据数组索引修改元素能被监测到
  get hobby [...]
  get 0 吃法
  get 1 睡觉
  set 0 被修改了
*/

obj.hobby.push('新增的')
/* 
  分别输出: 使用方法监测不到 vue 重写了数组部分方法
  get hobby [...]
  get 0 吃法
  get 1 睡觉
*/

监控数据栗子 类似 watch

function Archiver () {
  let val = null;
  // 新旧值
  let archiver = [];

  Object.defineProperty(this, 'firstName', {
    get () {
      console.log('get');
      return val;
    },
    set (value) {
      console.log('set');
      val = value;
      archiver.unshift({ '值': value });
    }
  });

  this.getArchiver = () => archiver;
}

// 构造函数
const obj = new Archiver();

obj.firstName = 'JOJO';

obj.firstName = '鸡哥';

console.log(obj.getArchiver());
/* 
  打印输出: 
    set
    set
    [ { '值': '鸡哥' }, { '值': 'JOJO' } ]
*/