【JavaScript】数据劫持(代理)详解

💻 【JavaScript】数据劫持(代理)详解 🏠专栏:JavaScript
👀个人主页:繁星学编程🍁
🧑个人简介:一个不断提高自我的平凡人🚀
🔊分享方向:目前主攻前端,其他知识也会阶段性分享🍀
👊格言:☀️没有走不通的路,只有不敢走的人!☀️
👉让我们一起进步,一起成为更好的自己!!!🎁

【JavaScript】数据劫持(代理)

所谓数据代理(也叫数据劫持),指的是在访问或者修改对象的某个属性时,通过一段代码拦截这个行为,进行额外的操作或者修改返回结果。比较典型的是 Object.defineProperty() 和 ES2015 中新增的 Proxy 对象。而在前端框架中vue2.0使用的是Object.defineProperty()、vue3.0使用的是Proxy。当然今天主要讲述的JS和ES中的数据劫持,vue中的小编后续会安排上。

一. Object.defineProperty()

语法:Object.defineProperty(数据, 属性名, {配置项})

配置项

  • value:设置属性名对应的属性值

  • writable:设置的属性是否可以修改

    值:true/false(默认)

  • enumerable:设置的属性是否可以遍历

    值:true/false (默认)

  • configurable:设置的属性是否可以删除

    值:true/false(默认)

  • getter、setter:不允许和value, writable连用,连用会报错

    • get 获取的时候可以触发的方法
    • set 设置的时候可以触发的方法

:value、writable、enumerable、configurable可以单独使用也可以连用

针对上述配置项的使用情况

(1) value

const obj = { name: "zs" }
Object.defineProperty(obj, "age", {
    // 设置属性名对应的属性值
    value: 20;
});
console.log(obj); // {name: 'zs', age: 20}
obj.age = 30;
console.log(obj); // {name: 'zs', age: 20}

(2) writable

const obj = { name: "zs" }
Object.defineProperty(obj, "age", {
    // 设置的属性可以修改 
    writable: true
})
console.log(obj); // { name: 'zs', age: undefined }
obj.age = 30;
console.log(obj); // {name: 'zs', age: 20}

(3) enumerable

const obj = { name: "zs" }
Object.defineProperty(obj, "age", {
    value: 20,
    // 设置的属性可以遍历 
    enumerable: true
});
for (let k in obj) {
    console.log(k, obj[k]); // name zs age 20
}

(4) configurable

const obj = { name: "zs" }
Object.defineProperty(obj, "age", {
    // 设置的属性可以删除  
    configurable: true
});
delete obj.name;
console.log(obj); // {age: undefined}

(5) get set定义属性

const obj = { name: "zs" }
Object.defineProperty(obj, "age", {
    get() {
        return 20; // 当我们设置一个返回值的时候,就表示该属性被设置了值
    },
    set(val) {
        // 可以监听到设置的值
        console.log(val); // 19
    }
});
obj.age = 19; // 设置的时候,会触发 set方法
console.log(obj.age); // 20 (获取的时候, 会触发 get方法)
console.log(obj);

二. 数据劫持

当访问或者修改对象的某个属性的时候,通过 getter setter 拦截这个行为,进行额外的操作
将原始的数据复制一份,通过复制的数据操作原始数据

<div id="box"></div>
<input type="text" id="ipt1">
<input type="text" id="ipt2">
<!-- 分割线 --> 
// 原始数据
const obj = {
    name: 'zs',
    age: 20
}
// 目标数据
const target = {}
// 通过数据劫持的方法,把原始数据复制到目标中
Object.defineProperty(target, 'name', {
    get() {
        return obj.name
    },
    set(val) {
        obj.name = val
        box.innerHTML = `你好, 我叫${target.name} , 我今年${target.age}`
    }
})
Object.defineProperty(target, 'age', {
    get() {
        return obj.age
    },
    set(val) {
        obj.age = val
        box.innerHTML = `你好, 我叫${target.name} , 我今年${target.age}`
    }
})
box.innerHTML = `你好, 我叫${target.name} , 我今年${target.age}`
ipt1.onchange = function () {
    target.name = this.value
}
ipt2.onchange = function () {
    target.age = this.value
}

三. 封装函数实现数据劫持

<div id="box"></div>
<input type="text" id="ipt1">
<input type="text" id="ipt2">
<script>
    // 原始对象
    const obj = {
        name: 'zs',
        age: 20
    }
    // 进行封装函数实现数据劫持
    function encapsulation(obj, cb) {
        // 目标对象
        // 通过数据劫持的方法,把原始数据复制到目标中
        let target = {};
        // 遍历原始对象,拿到对象中的每一个值
        for (let k in obj) {
            Object.defineProperty(target, k, {
                get() {
                    return obj[k];
                },
                set(val) {
                    obj[k] = val;
                    cb(target);
                }
            });
        }
        cb(target);
        return target;
    }
	// 调用封装函数
    let app = encapsulation(obj, (target) => {
        box.innerHTML = `你好, 我叫${target.name} , 我今年${target.age}`;
    });
    // 获取input框中输入的值
    ipt1.onchange = function () {
        app.name = this.value;
    }
    ipt2.onchange = function () {
        app.age = this.value;
    }
</script>

效果图

请添加图片描述

四. 数据代理(Proxy)

在数据劫持这个问题上,Proxy 可以被认为是 Object.defineProperty() 的升级版。外界对某个对象的访问,都必须经过这层拦截。因此它是针对 整个对象,而不是 对象的某个属性,所以也就不需要对 keys 进行遍历。

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

语法

const p = new Proxy(target, handler)

参数

  • target

    要使用 Proxy 包装的目标对象(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理)。

  • handler

    一个通常以函数作为属性的对象,各属性中的函数分别定义了在执行各种操作时代理 p 的行为。

简单的数据代理案例

const obj = {
    name: 'tom',
    age: 18
}
// 开始代理
const res = new Proxy(obj, {
    // 访问数据的时候 会触发
    get(target, property) {
        return target[property];
    },
    // 设置数据的时候会触发
    set(target, property, val) {
        // target 就是要代理的目标对象
        // property 就是要修改的属性
        // val 修改的属性值
        target[property] = val;
        // 必须写,简单代理必须返回true
        return true;
    }
})
res.name = 'jerry';
// 给对象新添加一个数据
res.sex = '女';
console.log('原始数据', obj); // 原始数据 {name: 'jerry', age: 18, sex: '女'}
console.log('代理结果', res); // 代理结果 Proxy {name: 'jerry', age: 18, sex: '女'}

五. Proxy和Object.defineProperty的区别

  1. Proxy是对整个对象的代理,而Object.defineProperty只能代理某个属性。
  2. 对象上新增属性,Proxy可以监听到,Object.defineProperty不能。
  3. 数组新增修改,Proxy可以监听到,Object.defineProperty不能。
  4. 若对象内部属性要全部递归代理,Proxy可以只在调用的时候递归,而Object.definePropery需要一次完成所有递归,性能比Proxy差。
  5. Proxy不兼容IE,Object.defineProperty不兼容IE8及以下
  6. Proxy使用上比Object.defineProperty方便多。

结束语

希望对您有一点点帮助,如有错误欢迎小伙伴指正。
👍点赞:您的赞赏是我前进的动力!
⭐收藏:您的支持我是创作的源泉!
✍评论:您的建议是我改进的良药!
一起加油!!!💪💪💪

  • 2
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

繁星学编程

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值