js两种自定义修改对象属性的方式

前言

先看下面一段常规的object赋值代码:

const people = {
    name: 'Rookie',
    age: 18
};
people.age = 20;// set
people.age;// get

现在我们想要自定义实现people的set和get,那么应该如何实现呢?
下面我们介绍两种实现方式:Object.defineProperty()Proxy

Object.defineProperty()

Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。
使用语法:

Object.defineProperty(obj, prop, descriptor)

在descriptor中允许我们自定义obj的set和get方式。
有关Object.defineProperty()的更多用法可以看官方文档:文档资料
那么我们接下来使用Object.defineProperty()来重构前言的代码:

function defineReactive(data, key, val) {
    Object.defineProperty(data, key, {
        enumerable: true,
        configurable: true,
        get: function defineGet() {
            console.log(`get key: ${key} val: ${val}`);
            return val;
        },
        set: function defineSet(newVal) {
            console.log(`set key: ${key} val: ${newVal}`);
            val = newVal;
        }
    })
}

function observe(data) {
    Object.keys(data).forEach(function (key) {
        defineReactive(data, key, data[key]);
    })
}

let people = {
    name: 'Rookie',
    age: 18
};

observe(people);

people.age = 20;// set key: age val: 20
people.age;// get key: age val: 20
people.sex = 'man';

由于people中含有多个属性,所以这里需要一个observe函数将people中的属性逐一拆解并进行defineReactive。从结果中看出对于people中已有的属性可以进行自定义操作,但是对于people中没有的属性却无法进行自定义操作。那么有什么解决方案呢?细心的朋友应该发现,Vue2的数据双向绑定就是通过此方式实现的,对于此问题的解决方案,我们可以重新生成people属性然后再次进行observe即可。
升级后的代码:

function defineReactive(data, key, val) {
    Object.defineProperty(data, key, {
        enumerable: true,
        configurable: true,
        get: function defineGet() {
            console.log(`get key: ${key} val: ${val}`);
            return val;
        },
        set: function defineSet(newVal) {
            console.log(`set key: ${key} val: ${newVal}`);
            val = newVal;
        }
    })
}

function observe(data) {
    Object.keys(data).forEach(function (key) {
        defineReactive(data, key, data[key]);
    })
}

let people = {
    name: 'Rookie',
    age: 18
};

observe(people);

people.age = 20;// set key: age val: 20
people.age;// get key: age val: 20
people.sex = 'man';
observe(people);
people.sex = 'woman';

不难发现,Object.defineProperty()是劫持对象的属性,新增元素需要再次 definedProperty。。而 Proxy 劫持的是整个对象,不需要做特殊处理,Proxy也就是Vue3实现数据双向绑定的实现原理。

Proxy

Proxy 对象用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)。
使用语法:

const p = new Proxy(target, handler)

在handler中允许我们自定义target的set和get方式。
有关Proxy的更多用法可以看官方文档:proxy文档
那么我们接下来使用Proxy来重构前言的代码:

let people = {
    name: 'Rookie',
    age: 18
};
let person = new Proxy(people, {
    get: function (obj, prop) {
        // 附加一个属性
        console.log(`get key: ${prop} val: ${obj[prop]}`);
        // 默认行为是返回属性值
        return obj[prop];
    },
    set: function (obj, prop, value) {
        // 附加属性
        obj[prop] = value;
        console.log(`set key: ${prop} val: ${value}`);
        // 表示成功
        return true;
    }
});
person.age = 20;
person.age;
person.sex = 'man';
people; //{name: "Rookie", age: 20, sex: "man"}

Proxy校验器

下面是一个使用proxy实现的一个对象拦截校验器的案例:

const target = {
    _id: '1024',
    name: 'vuejs'
}
const validators = {
    name(val) {
        return typeof val === 'string';
    },
    _id(val) {
        return typeof val === 'number' && val > 1024;
    }
};
const createValidator = (target, validator) => {
    return new Proxy(target, {
        _validator: validator,
        set(target, prop, value, proxy) {
            let validator = this._validator[prop](value)
            if (validator) {
                console.log(`set key: ${prop} val: ${value}`);
                return Reflect.set(target, prop, value, proxy)
            } else {
                console.error(`Cannot set ${prop} to ${value}. Invalid type.`);
            }
        }
    });
}
const proxy = createValidator(target, validators);
proxy.name = 'vue-js.com' // vue-js.com
proxy.name = 10086 // Cannot set name to 10086. Invalid type.
proxy._id = 1025 // 1025
proxy._id = 22  // Cannot set _id to 22. Invalid type 

结语

虽然Object.defineProperty()和proxy都能实现对象属性的自定义,但是proxy作为es6新出现的特性,它的功能比defineProperty 更加丰富使用起来也更加灵活。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值