数据劫持(二)理解Proxy

Proxy

了解Proxy

  • Proxy 对象用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)。
  • new Proxy(target, handler)
    • target。要使用 Proxy 包装的目标对象(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理)。
    • handler。一个通常以函数作为属性的对象,各属性中的函数分别定义了在执行各种操作时代理的行为。handler 对象是一个容纳一批特定属性的占位符对象。它包含有 Proxy 的各个捕获器。所有的捕捉器是可选的。如果没有定义某个捕捉器,那么就会保留源对象的默认行为。

get与set

get
  • 当通过proxy去读取对象里面的属性的时候,会进入到get钩子函数里面
  • get(target, property, receiver) {}
    • this上下文绑定在handler对象上
    • target。目标对象
    • property。被获取的属性名
    • receiver。Proxy或者继承Proxy的对象
  • get方法可以返回任何值
set
  • 当通过proxy去为对象设置修改属性的时候,会进入到set钩子函数里面
  • set(target, property, receiver) {}
    • this上下文绑定在handler对象上
    • target。目标对象
    • property。将被设置的属性名或 Symbol
    • value。新属性值
    • receiver。最初被调用的对象。通常是 proxy 本身,但 handler 的 set 方法也有可能在原型链上,或以其他方式被间接地调用(因此不一定是 proxy 本身)
  • set() 方法应当返回一个布尔值。
    • 返回 true 代表属性设置成功。
    • 在严格模式下,如果 set() 方法返回 false,那么会抛出一个 TypeError 异常。
const targetObj ={
    name: "小陈",
    age: 24
};
let obj = new Proxy(targetObj, {
    get(target, property, receiver) {
    	// 在这里可进行一些其他操作
        console.log("触发了get");  
        return target[property];
    },
    set(target, property, value, receiver) {
    	// 在这里可进行一些其他操作
        target[property] = value;
        console.log("触发了set", target[key]);
        return true;
    }
})
console.log(obj.name);  // 触发了get   小陈  
obj.name = "小王";  // 触发了set 小王
obj.age = 23;  // 触发了set 23
console.log(obj); // Proxy {name: '小王', age: 23}

proxy与Object.defineProperty对比

  • Proxy代理的是整个对象,而不是对象的某个特定属性,不需要我们通过遍历来逐个进行数据绑定,因此解决了Object.defineProperty的不足:
    • 对于一个对象,不需要要遍历来对所有属性监听
    • 对于对象的新增属性,也能监听
    • 对于数组通过push、unshift等操作方法和索引操作,也能监听
    • 访问不存在的属性,仍然可以被get拦截到

对象监听

const person = {
    name: '小陈',
    age: 24,
    friends: {
        name: '小王',
        age: 23,
    }
};
let proxyObj = new Proxy(person, {
    get(target, property) {
        return target[property]
    },
    set(target, property, value) {
        target[property] = value
        return true
    }
});
console.log(proxyObj.name); //小陈
proxyObj.age = 25;
console.log(proxyObj.age); //25
proxyObj.height = '170cm';
console.log(proxyObj.height); // 170cm
console.log(proxyObj.sex); // undefined
// 操作嵌套属性
console.log(proxyObj.friends.name); // 小王
proxyObj.friends.age = 24;
console.log(proxyObj.friends.age); // 24
proxyObj.friends.height = '165cm';  
console.log(proxyObj.friends.height); //165cm
console.log(proxyObj.friends.sex); //undefined

数组监听

const hobby = ['睡觉'];
let proxyArr = new Proxy(hobby, {
    get(target, property) {
        return target[property]
    },
    set(target, property, value) {
        console.log('触发了set');
        target[property] = value
        return true
    }
});
proxyArr.push('干饭');  // 触发了set
proxyArr[3] = '扣脚';  // 触发了set
console.log(proxyArr);

Proxy的其他捕获器

has

  • 针对 in 操作符的代理方法,当使用in判断属性是否在proxy代理对象里面时,会触发has
  • has(target, prop) {}
    • target。目标对象
    • prop。需要检查是否存在的属性.
  • has 方法返回一个 boolean 属性的值
const targetObj = {
    name: "小陈",
    age: 24
};
let obj = new Proxy(targetObj, {
    has(target, prop) {
        console.log("触发了has");
        if (prop === 'age') return false
        return target[prop];
    },
})
console.log('name' in obj);  // 触发了has  true
console.log('age' in obj);  // 触发了has  false

getPrototypeOf

  • 当读取代理对象的原型时,该方法就会被调用
  • getPrototypeOf(target) {}
    • target。被代理的目标对象。
  • getPrototypeOf 方法的返回值必须是一个对象或者 null。
const obj = {};
const proto = {};
const p = new Proxy(obj, {
    getPrototypeOf(target) {
        console.log(target === obj);   // true
        console.log(this === handler); // true
        return proto;
    }
});
console.log(Object.getPrototypeOf(p) === proto);    // true
5 种触发 getPrototypeOf 代理方法的方式
const obj = {};
const p = new Proxy(obj, {
    getPrototypeOf(target) {
        return Array.prototype;
    }
});
console.log(
    Object.getPrototypeOf(p) === Array.prototype,  // true
    Reflect.getPrototypeOf(p) === Array.prototype, // true
    p.__proto__ === Array.prototype,               // true
    Array.prototype.isPrototypeOf(p),              // true
    p instanceof Array                             // true
);
2种情况下的异常
const obj = {};
const p = new Proxy(obj, {
    getPrototypeOf(target) {
        return "foo";
    }
});
Object.getPrototypeOf(p); // TypeError: "foo" is not an object or null
const obj = Object.preventExtensions({});
const  p = new Proxy(obj, {
    getPrototypeOf(target) {
        return {};
    }
});
Object.getPrototypeOf(p); // TypeError: expected same prototype value

setPrototypeOf

  • 主要用来拦截 Object.setPrototypeOf()
  • setPrototypeOf(target, prototype) {}
    • target。被拦截目标对象.
    • prototype。对象新原型或为null.
  • 如果成功修改了[[Prototype]], setPrototypeOf 方法返回 true,否则返回 false
  • 如果不想为对象设置一个新的原型,setPrototypeOf方法可以返回false,也可以抛出异常
const newProto = {}, target = {};
const p1 = new Proxy(target, {
    setPrototypeOf(target, newProto) {
        return false;
    }
});
Object.setPrototypeOf(p1, newProto); // throws a TypeError
Reflect.setPrototypeOf(p1, newProto); // returns false

const newProto = {}, target = {};
const p2 = new Proxy(target, {
    setPrototypeOf(target, newProto) {
        throw new Error('custom error');
    }
});
Object.setPrototypeOf(p2, newProto); // throws new Error("custom error")
Reflect.setPrototypeOf(p2, newProto); // throws new Error("custom error")

isExtensible

  • isExtensible() 方法用于拦截对对象的Object.isExtensible()

preventExtensions

  • 方法用于设置对Object.preventExtensions()的拦截

getOwnPropertyDescriptor

  • 是 Object.getOwnPropertyDescriptor() 的钩子

defineProperty

  • 用于拦截对对象的 Object.defineProperty() 操作

deleteProperty

  • 用于拦截对对象属性的 delete 操作

ownKeys

  • 用于拦截 Reflect.ownKeys()

apply

  • 用于拦截函数的调用

construct

  • 用于拦截new 操作符,为了使new操作符在生成的Proxy对象上生效,用于初始化代理的目标对象自身必须具有[[Construct]]内部方法(即 new target 必须是有效的)
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值