通过Object.defineProperty给对象的属性添加拦截

在之前的博文中,曾经总结过vue2数据监听的实现原理,本篇和之前其实也差不多,都是给对象添加get、set的存取器属性以达到数据拦截处理的目的.

为什么要给对象添加这种存取器的属性呢?是为了达到对对象中某些属性的读取进行监听的目的,方便在对对象进行读写时做一些特殊的处理.

下面通过代码来演示一下Object.defineProperty给对象添加属性的几种情况

1、给对象添加常规属性

let obj = { name: 'mike' }
Object.defineProperty(obj, 'age', {
    configurable: true,
    enumerable: true,
    value: 18,
    writeable: true
})
Object.keys(obj)
['name', 'age']

这里比较简单,只需要注意添加的属性几种描述就好

configurable: 是否允许该属性被重复定义还有是否允许该属性被删除
enumerable: 是否允许该属性被遍历
value: 该属性的值,如果为false那么将被初始化为undefined
writeable: 是否允许该属性被修改

除了value,其余三个默认都为false

2、给对象添加存取器属性

Object.defineProperty(obj, 'sex', {
    get(){
        return this.sex;
    },
    set(value){
        this.sex = value;
    }
})
obj.sex
//

Uncaught RangeError: Maximum call stack size exceeded

我们看到,我们给obj添加了sex的存取器属性,但是当我们对这个sex属性调用时发生了死循环的报错

这里需要注意的是,我们不能通过this.sex对sex属性进行读取和赋值,我们需要借助于一个临时的变量

修改一下上面的代码

let obj = { name: 'mike' }
let privateData = 'man' //临时变量
Object.defineProperty(obj, 'sex', {
    get(){
        return privateData;
    },
    set(value){
       privateData = value 
    }
})
obj.sex // 'man'

记住这里读取新增的属性要借助临时变量哦!

3、value 和 writable 属性不能与 get、set共存

let myLike = 'fish'
Object.defineProperty(obj, 'like', {
    configurable: true,
    enumerable:true,
    value: 'apple',
    writeable: true,
    get(){
        return myLike;
    },
    set(value){
       myLike = value 
    }
})
//Uncaught TypeError: Invalid property descriptor. Cannot both specify accessors and a value or writable attribute

由上面的代码演示可以看到value 和 writable 属性不能与 get、set共存,两种情况只能二选一

4、构造函数的原型添加存取器属性

function Person(name){
    this.name = name;
    this.privateAge = 18;
}

Object.defineProperty(Person.prototype, 'age', {
    configurable: true,
    enumerable: true,
    get(){
        return this.privateAge;
    },
    set(value){
        this.privateAge = value;
    }
})

let person2 = new Person('lili')
let person1 = new Person('mike')
person2.age //18
person2.age = 28
person2.age // 28
person1.age //18
person1.age = 16
person1.age // 16

通过构造函数的原型可以给所有实例话的对象添加存取器属性,很方便

5、get、set没有成对出现的时候不能对该属性进行读写

function Person(name, sex){
    this.name = name;
    this.sex = sex;
    console.log(this.age)
}

Object.defineProperty(Person.prototype, 'sex', {
    configurable: true,
    enumerable: true,
    get(){
        return 'man'
    }
})
Object.defineProperty(Person.prototype, 'age', {
    configurable: true,
    enumerable: true,
    set(value){
        this.age = value
    }
})

let person = new Person('mike', 'women') 
person.sex //'man'
person.age = 18 
//Uncaught RangeError: Maximum call stack size exceeded

1、我们在构造函数中添加了this.age和this.sex,并且想通过传参的形式来给this.sex初始化值
2、我们通过Object.defineProperty给构造函数的原型分别添加了sex和age的get和set的属性
3、通过Object.defineProperty新增的sex读属性,没有写,说明这个sex是只读的,所以this.sex的写不起作用
4、通过Object.defineProperty新增的age写属性中不能对使用this.age,否则会发生死循环

6、类中使用get、set

class Person {
    constructor(name){
        this.name = name;
        this.privateAge = 18;
    }
    get age(){
        return this.privateAge;
    }
    set age(value){
        this.privateAge = value;
    }
}

let person = new Person('mike')
person.age //18
person.age = 28
person.age //28

类中使用get、set其实和通过Object.defineProperty给构造函数的原型添加get、set是一样的,仅仅是写法上有些区别,类的形式看上去更为简洁方便

7、类中get、set不成对的时候,不能对这个属性进行赋值

class Person {
    constructor(age){
        this.age = age;
    }
    get age(){
        return 18;
    }
}
let person = new Person(28) 
//Uncaught TypeError: Cannot set property age of #<Person> which has only a getter

不同于构造函数的是,在类中et、set不成对的时候,不能对这个属性进行赋值,连构造方法中的初始化都不行

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值