自学内容网 自学内容网

理解defineProperty以及getter、setter


理解defineProperty以及getter、setter,学习它们之间的联系

对象属性注意点

我们一般修改对象通过obj.key这样的方式访问到,要设置/修改对象的属性,只需要obj.key="value"
如果key位于原型上,同时在对象自身设置该key值时,修改的是对象自身这个值,而不是修改原型上的

let arr = [1, 2, 3];
arr.__proto__.test = "我是原型上的"; // 这种写法不推荐,这样写不具有可维护性和可读性
arr.test = "我是自身上的";
console.log(arr); // [ 1, 2, 3, test: '我是自身上的' ]
console.log(arr.__proto__); // Object(0) [ test: '我是原型上的' ]

原型上的属性有时候会被for in给遍历出来:

let arr = [1, 2, 3];
arr.__proto__.test = "我是原型";
for (let i in arr) {
  console.log(arr[i]);
}
// 1
// 2
// 3
// 我是原型

所以用for in,一般都是搭配hasOwnProperty判断当前对象属性中是否具有指定的属性(也就是指定的键),或者直接使用forEach

理解defineProperty

上面__proto__直接修改原型的写法不推荐,不具备可维护性和可读性,__proto__是一个内部属性,并不是ECMAScript属性。
Object.defineProperty()是定义新属性或修改原有属性并且能够设置其特性的标准方法,它可以提供该属性的更多特性,比如可以设置属性是否可写、可枚举和可配置。该方法返回修改后的对象。

// 完整语法
Object.defineProperty(obj, prop, descriptor);
// obj 目标对象
// prop 属性名称
// descriptor 属性描述符
let obj = {};
Object.defineProperty(obj, "age", {
  value: "我不一样", // 属性对应的值
  writable: true, // 是否可写
  enumerable: true, // 可枚举
  configurable: true, // 可配置
});

descriptor这个参数可以分为两类:数据描述符(data descriptor)、访问描述符(accessor descriptor)。

  • 必选项
    1. configurable:表示可配置,当它为true时,该属性的描述符可被修改,并且该属性可被delete删除;同理,为false时,无法再调用defineProperty去修改描述符,也不可通过delete删除
    2. enumerable:表示可枚举,为true时,该属性可被迭代器枚举出来。比如使用for in或者Object.keys
  • 数据描述符
    1. value:该属性的值,通过obj.key访问时返回。
    2. writable:表示该属性是否可写,为false时,属性不可被任何赋值语句重写,但是此时还可以调用defineProperty来修改value,前提是configurable为true。

描述符的原型与默认值

一般先创建一个descriptor对象,然后传给defineProperty方法:

let descriptor = {
  writable: false,
};
Object.defineProperty(obj, "key", descriptor);

上面情况存在一定的风险,如果descriptor的原型上有相关特性,也会通过原型链被访问到,算入在对key的定义中:

descriptor.__proto__.enumerable = true;
Object.defineProperty(obj, "key", descriptor);
Object.getOwnPropertyDescriptor(obj, "key"); //  { value: undefined, enumerable: true, }

为了避免上面的意外情况,官方建议使用Object.freeze冻结对象,或者使用Object.create(null)创建一个新纯净的对象(不含原型)来使用。

let obj = {
    name: "我不是普通人",
    age: 123,
};
obj.__proto__.habit = "晕";
// Object.freeze() 返回值是被冻结的对象,该对象完全等于传入的对象,一般是不需要接收返回值
Object.freeze(obj);
// 不能添加新属性
// 不能删除已有属性
// 不能修改已有属性的值
// 不能修改已有属性的值
// 不能修改原型
// 不能修改已有属性的可枚举性、可配置性、可写性

// 如果不知道一个对象是否被冻结 可以使用 Object.isFrozen() 来判断
console.log(Object.isFrozen(obj)); // true

VueObject.freeze在data中可以这样写:

// 对于纯展示的大数据,可以提升性能
data() {
  return {
      list: Object.freeze({
          name: "我不需要改变",
      })
  };  
},

一个对象属性描述符的默认值,一般都为true:

let obj = {};
obj.value = "看看默认值";
Object.getOwnPropertyDescriptor(obj, "value");
// { value: 123, writable: true, enumerable: true, configurable: true }

如果使用defineProperty定义的属性,默认值就不是这样的:

let obj = {};
Object.defineProperty(obj, "key", {
    value: 123,
});
console.log(Object.getOwnPropertyDescriptor(obj, "key"));
// { value: 456, writable: false, enumerable: false, configurable: false }

getter和setter

这两个就是上面提到的访问描述符,这只是两个概念,并没有这样的属性。

  • get

    是一个函数,访问该属性时会自动调用,函数的返回值为该属性的value。默认值为undefined。

  • set

    是一个函数,该属性赋值时会自动调用,新值会被当做参数传入。

Vue2框架中数据监控用到的核心原理就是这个。


原文地址:https://blog.csdn.net/weixin_45663702/article/details/142764820

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!