Object.defineProperty用法解析

596 阅读3分钟

1、作用

顾名思义,Object.defineProperty可以定义一个对象,但这不仅仅是它的最主要功能,它的主要功能在于他可以配置定义的属性,也可以使用setget做数据劫持,实现响应式数据,vue2中则是利用它实现了更新数据,即可更新dom的响应式原理。

2、用法

Object.defineProperty(obj, property, descriptor)有三个属性:

  • 第一个参数:obj是要定义属性的原对象

  • 第二个参数:property是定义的属性名或者symbol

  • 第三个参数:要定义属性的描述配置

    • configurable: 可配置属性,控制descriptor是否可以被重新改变和obj定义属性的删除,默认值为false
    • enumerable:可枚举属性,控制对象的property是否可遍历,默认为false
    • writable:是否可修改,控制属性是否可以被赋值运算符修改,默认为false
    • value:属性的初始值,默认为undefined
    • set:setter函数,可以拦截obj.prop = 'xxx'的操作,在其中做一些逻辑处理
    • get:getter函数,可以拦截obj.prop的操作,在读取运算中做一些逻辑处理
  • 返回值:返回定义属性和添加描述配置后的obj

3、例子

configurable

configurable如果首次定义为false,那么它会禁止在此修改此属性,一旦定义就无法修改,只有首次定义为true时候,可以在此修改。此外,只有此属性为true时,才可以删除定义的属性。

 // 例1
 const obj = {};
 Object.defineProperty(obj, "name", {
     // 默认为false 可以不写,这里只是用例
     configurable: false,
     value: 'jack'
 });
 console.log(obj.name); // 'jack'
 ​
 // 下面修改报错 Cannot redefine property: name
 Object.defineProperty(obj, "name", {
     configurable: true,
     value: "tom"
 });
 ​
 // 例2
 const obj = {};
 Object.defineProperty(obj, "name", {
     // 默认为false 可以不写,这里只是用例
     configurable: true,
     value: 'jack'
 });
 console.log(obj.name); // 'jack'
 delete obj.name;
 console.log(obj.name); // undefined
 ​
 // 可以再次修改配置
 Object.defineProperty(obj, "name", {
     value: "tom"
 });
 console.log(obj.name);// 'tom'

enumerable

控制该属性是否可以被遍历,是否可以在for..in..Object.keys()中枚举

 // 例1 
 const obj = {
     age: 20,
     gender: "男",
     address: "北京"
 };
 Object.defineProperty(obj, "name", {
   // 当设置可枚举为true
     enumerable: true,
     value: 'jack'
 });
 console.log(Object.keys(obj)); // [ 'age', 'gender', 'address', 'name' ]
 for (key in obj) {
     console.log(key); // 'age', 'gender', 'address', 'name'
 }
 ​
 // 例2
 const obj = {
     age: 20,
     gender: "男",
     address: "北京"
 };
 Object.defineProperty(obj, "name", {
     enumerable: false,
     value: 'jack'
 });
 console.log(Object.keys(obj)); // [[ 'age', 'gender', 'address' ]
 for (key in obj) {
     console.log(key); // 'age', 'gender', 'address'
 }

writable:

控制定义的属性是否可以重新赋值

 // 例1
 const obj = {};
 Object.defineProperty(obj, "name", {
     writable: true,
     value: 'jack'
 });
 console.log(obj.name); // 'jack'
 obj.name = "tom";
 console.log(obj.name);// 'tom'
 ​
 // 例2
 const obj = {};
 Object.defineProperty(obj, "name", {
     writable: false, // 将可修改属性设为false
     value: 'jack'
 });
 console.log(obj.name);// 'jack'
 obj.name = "tom";
 console.log(obj.name);// 'jack'

setget:

设置setter函数,拦截属性赋值操作,设置getter函数可以拦截属性值的读取。

⚠️注意:如果一个描述符同时拥有 valuewritablegetset 键,则会产生一个异常。会报错: Cannot both specify accessors and a value or writable attribute,不能同时指定访问器和值或可写属性。

⚠️注意:set函数中this的指向是赋值的对象,由于继承关系,get函数里面的this不一定指向定义的对象。

 // 创建一个新的对象 参数作为新对象的__proto__
 const proto = {
     age: 20
 };
 const obj = Object.create(proto);
 console.log(proto); // { age: 20 }
 Object.defineProperty(obj.__proto__, "name", {
     set: (val) => {
         console.log("被赋值了!!!");
         this.name = val;
     },
     get: () => {
         console.log("被读取了!!!");
         return this.name
     }
 });
 obj.name = "jack"; // obj继承了proto的属性和方法 所以obj的改变可以触发set函数执行
 console.log(proto.name); // 触发此时get中的this可以读取proto中的name
 // 输出结果
 // 被赋值了!!!
 // 被读取了!!!
 // jack

下面是第二个例子:

 function Person () {
 };
 const p1 = new Person("tom");
 const p2 = new Person("tom");
 Object.defineProperty(Person.prototype, "name", {
     set: (val) => {
         console.log("属性值被赋值了!!!");
         this.name = val;
     },
     get: () => {
         console.log("属性值被访问了!!!");
         return this.name;
     }
 });
 ​
 p1.name = "jack";
 console.log(Person.prototype.name); // jack
 console.log(p2.name) //jack
 // 输出结果
 // 属性值被访问了!!!
 // jack
 // 属性值被访问了!!!
 // jack
 ​

本文只是前端初学者的个人理解...如有错误请轻喷...