目录

Proxy

概述

基本用法

Proxy 实例方法

1.get(target, propKey, receiver)

2、set(target, propKey, value, receiver)

3、apply(target, ctx, args)

4、has(target, propKey)

5、construct(target, args, newTarget)

6、deleteProperty(target, propKey)

7、defineProperty(target, propKey, descriptor)

8、getOwnPropertyDescriptor(target, propKey)

9、getPrototypeOf(target)

10、 isExtensible(target)

11、 ownKeys(target)

12、preventExtensions(target)

13、 setPrototypeOf(target, proto)

Proxy.revocable()

Reflect

概述

静态方法

1、Reflect.get(target, name, receiver)

2、Reflect.set(target, name, value, receiver)

3、Reflect.has(obj, name)

4、Reflect.deleteProperty(obj, name)

5、Reflect.construct(target, args)

6、 Reflect.getPrototypeOf(obj)

7、Reflect.setPrototypeOf(obj, newProto)

8、Reflect.apply(func, thisArg, args)

9、Reflect.defineProperty(target, propertyKey, attributes)

10、Reflect.getOwnPropertyDescriptor(target, propKey)

11、Reflect.isExtensible(target)

12、Reflect.preventExtensions(target)

13、Reflect.ownKeys(target)

实例:使用Proxy实现观察者模式


Proxy

概述

Proxy是 ES6 为了操作对象引入的 API ,Proxy 可以对目标对象的读取、函数调用等操作进行拦截,然后进行操作处理。它不直接操作对象,而是像代理模式,通过对象的代理对象进行操作,在进行这些操作时,可以添加一些需要的额外操作。

proxy这个词的意思是"代理",在这里表示由它来"代理"一些操作,可以译为"代理器"。

基本用法

Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。

let target = { name: "Tom", age: 2 };
let handler = {get: (target, key) => {console.log("getting %s!", key);return target[key];},set: (target, key, value) => {console.log("setting %s 的值为 %s!", key, value);target[key] = value;},
};
let proxy = new Proxy(target, handler);
proxy.name; // getting name!  实际执行 handler.get 方法
proxy.name = "Jerry"; // setting name 的值为 Jerry!  实际执行 handler.set 方法

请看上面代码,target参数就是要拦截的目标对象,handler参数就是拦截对应的操作。

handler可以设置为空对象,相当于没有设置任何拦截,等同于直接访问原对象。

let proxy = new Proxy({ name: "Tom", age: 2 }, {});
console.log(proxy.name); // Tom

Proxy 实例方法

1.get(target, propKey, receiver)

用于拦截目标的读取操作,接受三个参数,target表示目标对象、propKey表示属性名、receiver表示proxy实例本身。第三个参数非必须。

let proxy = new Proxy({ name: "Tom", age: 2 },{get: (target, propKey) => {// 拦截操作console.log("正在读取 %s 属性", propKey);return target[propKey];},}
);
proxy.name; // 正在读取 name 属性// get方法可继承
let obj = Object.create(proxy);
obj.name; // 正在读取 name 属性

2、set(target, propKey, value, receiver)

用于拦截 target 对象上的 propKey 的赋值操作。接受四个参数,target表示目标对象、propKey表示属性名、value表示属性值、receiver表示proxy实例本身。第四个参数非必须。

let handler = {set: (target, propKey, value) => {if (propKey === "age" && !Number.isInteger(value)) {throw new TypeError("The age is not a integet");}if (propKey === "age" && value > 20) {throw new RangeError("The age seems invalid");}target[propKey] = value;return true;},
};
let proxy = new Proxy({ name: "Tom", age: 2 }, handler);
proxy.age = 4;
console.log(proxy.age); // 4
proxy.age = 21; // RangeError: The age seems invalid
proxy.age = "4岁"; // TypeError: The age is not a integet

第四个参数 receiver 表示原始操作行为所在对象,一般是 Proxy 实例本身。

贴一个第四个参数的例子。

let handler = {set: (target, propKey, value, receiver) => {target[propKey] = receiver;return true;},
};
let proxy = new Proxy({}, handler);
proxy.name = "Tom";
console.log(proxy.name === proxy); // true

如果目标对象自身的某个属性,不可写且不可配置,那么set方法将不起作用。

let target = {};
Object.defineProperty(target, "age", {value: 4,writable: false, // 属性是否可以被修改
});
let handler = {set: (target, propKey, value, receiver) => {target[propKey] = receiver;},
};
let proxy = new Proxy(target, handler);
console.log(proxy.age); // 4
proxy.age = 5;
console.log(proxy.age); // 4

3、apply(target, ctx, args)

用于拦截函数的调用、call 和 apply 操作。接受三个参数,target 表示目标对象,ctx 表示目标对象上下文,args 表示目标对象的参数数组。

每当执行proxy函数(直接调用或callapply调用),就会被apply方法拦截。

let target = (x, y) => x + y;
let handler = {apply(target, ctx, args) {return Reflect.apply(...arguments) * 2;},
};
let proxy = new Proxy(target, handler);
console.log(proxy(1, 2)); // 6
console.log(proxy.call(null, 3, 4)); // 14
console.log(proxy.apply(null, [5, 6])); // 22

4、has(target, propKey)

用来拦截hasProperty方法,即判断对象是否具有某个属性时,会被这个方法方法拦截。典型的操作就是in运算符。接受两个属性,分别是目标对象和要查询的属性名。has方法不判断是自身属性还是继承属性。注意:此方法对 for...in 不生效。

let handler = {has(target, propKey) {if (propKey === "age" && target[propKey] < 0.3) {console.log(`${target.name}是幼猫, 还不可以吃零食`);return false;}console.log("handler has");return propKey in target;},
};
let proxy = new Proxy({ name: "Tom" }, handler);
console.log("name" in proxy); // handler has  true
let Tom = { name: "Tom", age: 2 };
let yuanDan = { name: "yuanDanDan", age: 0.2 };
let cat1 = new Proxy(Tom, handler);
let cat2 = new Proxy(yuanDan, handler);
console.log("age" in cat1); // handler has  true
console.log("age" in cat2); // yuanDanDan是幼猫, 还不可以吃零食  false
// for...in 不拦截
for (let k in cat2) {console.log(cat2[k]); // yuanDanDan  0.2
}

5、construct(target, args, newTarget)

用于拦截 new 命令,接受三个参数,分别是目标对象、构造函数参数数组,new命令作用的构造函数。construct返回的必须是对象,否则会报错。因为construct拦截的是构造函数,他的目标对象必须是函数,否则也会报错。construct方法中this指向hanldler,不是实例对象。

let handler = {construct(target, args, newTarget) {console.log(this === handler);return Reflect.construct(target, args, newTarget);},
};
class Cat {constructor(name) {this.name = name;}
}
let proxy = new Proxy(Cat, handler);
let cat = new proxy("Tom");
console.log(cat); // true  Cat {name: 'Tom'}
console.log(cat.name); // Tom

6、deleteProperty(target, propKey)

用于拦截delete操作,如果返回false或者抛出错误,则propKey无法被delete命令删除。

let handler = {deleteProperty(target, propKey) {if (propKey[0] === "_") {throw new Error(`Invalid attempt to delete private "${propKey}" property`);}delete target[propKey];return true;},
};
let proxy = new Proxy({ name: "Tom", _age: 2 }, handler);
delete proxy.name;
console.log(proxy.name); // undefined
delete proxy._age; // Error: Invalid attempt to delete private "_age" property

7、defineProperty(target, propKey, descriptor)

用于拦截Object.defineProperty方法的操作。

let handler = {defineProperty(target, propKey, descriptor) {return false;},
};
let proxy = new Proxy({}, handler);
proxy.name = "Tom";
console.log(proxy.name); // undefined

8、getOwnPropertyDescriptor(target, propKey)

用于拦截Object.getOwnPropertyDescriptor方法,返回一个属性描述对象或者undefined。

let handler = {getOwnPropertyDescriptor(target, key) {let descriptor = Object.getOwnPropertyDescriptor(target, key);console.log(descriptor);return descriptor;},
};
let proxy = new Proxy({ name: "Tom" }, handler);
Object.getOwnPropertyDescriptor(proxy, "name"); // {value: 'Tom', writable: true, enumerable: true, configurable: true}

9、getPrototypeOf(target)

主要用于拦截获取对象原型的操作。 具体以下几种:

  • Object.prototype.__proto__
  • Object.prototype.isPrototypeOf()
  • Object.getPrototypeOf()
  • Reflect.getPrototypeOf()
  • instanceof
let target = {}, handler = {getPrototypeOf(target) {return target;}
}
let proxy = new Proxy(target, handler)
console.log(Object.getPrototypeOf(proxy) === target); // true

注意:getPrototypeOf 方法的返回值必须是对象或者 null,否则就会报错。如果目标对象不可扩展(non-extensible),getPrototypeOf 方法必须返回目标对象的原型对象。

10、 isExtensible(target)

用于拦截Object.isExtensible操作。

注意:它的返回值必须与目标对象的isExtensible属性保持一致,否则会抛出错误。

let proxy = new Proxy({},{isExtensible(target) {console.log("handler isExtensible");return true;},}
);
Object.isExtensible(proxy); // handler isExtensible
let proxy1 = new Proxy({},{isExtensible(target) {return false;},}
);
Object.isExtensible(proxy1); //TypeError: 'isExtensible' on proxy: trap result does not reflect extensibility of proxy target (which is 'true')

11、 ownKeys(target)

用来拦截对象自身属性的读取操作。具体拦截以下几种:

  • Object.keys()
  • Object.getOwnPropertySymbols()
  • Object.getOwnPropertyNames()
  • for...in
// 拦截第一个字符为下划线的属性名
let target = { name: "Tom", _age: 2, _sex: "小公猫" };
let handler = {ownKeys(target) {return Reflect.ownKeys(target).filter((el) => el[0] !== "_");},
};
let proxy = new Proxy(target, handler);
console.log(Object.keys(proxy)); // ['name']

使用Object.keys()方法时,有三类属性回呗ownKeys()方法自动过滤,不会返回。

  • 目标对象不存在的属性
  • 属性名为Symbol值
  • 不可枚举属性
let target = { name: "Tom", [Symbol.for("friend")]: "Jerry" };
let handler = {ownKeys(target) {return ["name", Symbol.for("friend"), "sex", "age"];},
};
Object.defineProperty(target, "age", {enumerable: false,value: 2,
});
let proxy = new Proxy(target, handler);
console.log(Object.keys(proxy)); // ["name"]
// 打印出来只有一个name属性,其他不存在的属性和Sybmol和不可枚举属性都被自动过滤掉

方法返回的数组成员,只能是字符串或 Symbol 值。如果有其他类型的值,或者返回的根本不是数组,就会报错。

let proxy = new Proxy({},{ownKeys(target) {return [null];},}
);
Object.keys(proxy); // TypeError: null is not a valid property name

如果目标对象自身包含不可配置的属性,则该属性必须被ownKeys()方法返回,否则报错。

let target = { name: "Tom" };
Object.defineProperty(target, "sex", {configurable: false,value: "男",
});
let proxy = new Proxy(target, {ownKeys(target) {return ["name"];},
});
Object.keys(proxy); // TypeError: 'ownKeys' on proxy: trap result did not include 'sex'

如果对象是不可扩展的 (not-extensible),此时ownKeys()方法返回的数组必须包含目标对象的所有属性,且不能包含其他多余的属性,否则就会报错。

let target = { name: "Tom", age: 2, sex: "男" };
let handler = {ownKeys(target) {return ["name", "age", "sex", "friend"];},
};
Object.preventExtensions(target);
let proxy = new Proxy(target, handler);
// 包含多余属性 friend 所以报错
Object.keys(proxy); //  TypeError: 'ownKeys' on proxy: trap returned extra keys but proxy target is non-extensible

12、preventExtensions(target)

用于拦截 Object.preventExtensions 操作。返回一个 Boolean 值,否则会自动转为 Boolean 值。

let proxy = new Proxy({},{preventExtensions(target) {return true;},}
);
Object.preventExtensions(proxy); // TypeError: 'preventExtensions' on proxy: trap returned truish but the proxy target is extensible

注意:只有目标对象不可扩展时,才可以返回 true,否则就会报错。所以,需要在 preventExtensions() 方法中把目标对象改成不可扩展才可以返回 true。

let proxy = new Proxy({},{preventExtensions(target) {console.log("handler preventExtensions");Reflect.preventExtensions(target);return true;},}
);
console.log(Object.preventExtensions(proxy)); // handler preventExtensions  Proxy {}

13、 setPrototypeOf(target, proto)

用于拦截 Object.setPrototypeOf() 方法。返回一个 Boolean 值,否则会自动转为 Boolean 值。如果目标对象不可扩展(not-extensible),setPrototypeOf() 方法不得改变目标对象的原型。

let proto = {};
let proxy = new Proxy(function () {}, {setPrototypeOf(target, proto) {// 只要修改target的原型对象,就抛出错误。throw new Error("禁止更改原型 												

ES6 Proxy和Reflect相关推荐

  1. ES6 Proxy 和 Reflect 的理解

    Vue中的数据绑定 ps:观察者模式 (下面有重点) Vue作为前端框架的三驾马车之一,在众多前端项目中具有极其重要的作用. Vue中具有一个重要的功能点--"数据绑定".使用者无 ...

  2. ES6 Proxy和Reflect (上)

    Proxy概述 Proxy用于修改某些操作的默认行为,等同于在语言层面做出修改,所以属于一种"元编程"(meta programming),即对编程语言进行编程. Proxy可以理 ...

  3. 深入实践 ES6 Proxy Reflect

    原文: https://zhuanlan.zhihu.com/p/60126477 引言 Vue中的数据绑定 Vue作为前端框架的三驾马车之一,在众多前端项目中具有极其重要的作用. Vue中具有一个重 ...

  4. Proxy和Reflect内容详解

    ES6中的Proxy和Reflect内容详解 监听对象的操作 我们先来看一个需求:有一个对象,我们希望监听这个对象中的属性被设置或获取的过程 通过我们前面所学的知识,能不能做到这一点呢? 其实是可以的 ...

  5. Proxy和Reflect详解

    Proxy和Reflect详解 之前一直没有理解proxy代理是啥意思,只是感觉很深奥的样子,最近正好在研究vue3的响应式原理,发现vue3是使用proxy完成响应式的,因此仔细的研究了一下prox ...

  6. 1.20(Proxy、Reflect、异步任务、同步任务)

    1.20(Proxy.Reflect.异步) 一.代理 概念:比如对一个对象的属性进行读写操作时,代理就是对这个操作执行前的一拦截操作 1.es5代理 let obj1 = {}; let newVa ...

  7. 通过Proxy和Reflect实现vue的响应式原理

    vue3通过Proxy+Reflect实现响应式,vue2通过defineProperty来实现 Proxy Proxy是什么 Proxy是ES6中增加的类,表示代理. 如果我们想要监听对象的操作过程 ...

  8. ES6 Proxy 性能之我见

    ES6 Proxy 性能之我见 本文翻译自https://thecodebarbarian.com/thoughts-on-es6-proxies-performance Proxy是ES6的一个强力 ...

  9. 用es6 (proxy 和 reflect)轻松实现 观察者模式

    js中 观察者 之前我们一般通过事件机制完成 ex: 注册监听 Event.listen('changeName', name => console.log(name)) 派发事件 Event. ...

最新文章

  1. 一小时学会用 Opencv 做贪吃蛇游戏(Python版)
  2. python获取重复元素
  3. BZOJ1688|二进制枚举子集| 状态压缩DP
  4. ffmpeg流文件合并concat
  5. mysql库操作、表操作
  6. Hadoop的SequenceFile读写实例
  7. 洛谷P4292:重建计划(点分治、单调队列)
  8. Oracle ——如何确定性能差的 SQL
  9. HashSet存储自定义对象保证元素唯一性图解原理及代码优化
  10. 51nod-1422:沙拉酱前缀
  11. VS2010 用户自定义工具箱控件的制作方法
  12. android开发完全退出activity
  13. win10cmd计算机管理界面,Win10命令提示符cmd在哪 Win10以管理员身份运行方法
  14. 干货,分享!后台信息管理HTML静态网页模版
  15. 你还为给自己的IT团队起名字,写口号烦恼吗?(较为流行的团队名称)
  16. PaddleClas-SSLD知识蒸馏方法梳理(82.4%ResNet50系列模型养成之路)
  17. 描写火车站场景_形容车站人多的句子(车站场景写一段话)
  18. 计算机校本研修报告,在信息技术环境下校本研修的“一三四模式”探索与
  19. Efficient RANSAC for Point-Cloud Shape Detection 点云形状检测的高效RANSAC法
  20. 《Real-Time Rendering 3rd》提炼总结 RTR3读书笔记

热门文章

  1. 逻辑回归(吴恩达机器学习笔记)
  2. 腾讯免费企业邮箱服务器,怎样使用免费的腾讯企业邮箱
  3. springboot集成阿里云短信验证码
  4. 卧槽!终于知道涛哥我为啥赚不到钱了
  5. Linux安装Tomcat(非Docker安装、开放端口)
  6. win7“找不到该项目”的错误原因及解决方法
  7. 高中计算机应用基础知识课件,计算机应用基础(windows 7+office 2010)课件 第一章 计算机基础知识.ppt.pdf-汇文网...
  8. Wifi模块-ESP-01s
  9. 使IT慢如蜗牛的十二个不良习惯
  10. Linux运维,到底如何入门?常用linux操作指令盘点!