赞
踩
“ ES6的对象拦截器,可以拦截哪些情况呢 ”
自从ES6诞生以来,各种新特性也逐渐显现出来,比如ES6中的Proxy对象,是一个重要的技术。之前Vue的数据双向数据绑定是使用Object.defineProperty()来做的,而现在vue3.0使用的是代理Proxy来编写。原因是前者有一些对象或者数组的变化是监听不了的,但是Proxy可以监听整个对象。
Proxy可以理解为,当你试图访问一个对象的时候必须先经过一个拦截或者代理,你才可以进行对对象的操作。这种机制的好处就是可以对外界的访问进行过滤和改写。
ES6提供一个原生的Proxy构造函数,用于生成Proxy实例。
var proxy = new Proxy ( target, handler ) ;
参数解析:
利用Proxy对象可以拦截的方法有一下13种,下面一一来介绍一下。
get方法是意思是读取对象,那么相应的拦截就是在读取对象的时候进行拦截。
- var person = {
- name:'alanwu'
- }
- var proxy = new Proxy(person, {
- get:function(target, property){
- if(property in target){
- return target[property]
- }else{
- throw new Error('对象属性不存在')
- }
- }
- })
- proxy.name //'alanwu'
- proxy.age //错误
假如上述代码访问不存在的属性的时候,没有抛出错误的话就会返回undefined。
set拦截操作用于拦截对对象赋值的操作。
- const obj = {
- a: 101,
- b: 46,
- c: 200
- }
- var proxy = new Proxy(obj, {
- set(target, key, value) {
- if (value < 100) {
- throw Error(`${value}值不能小于100`)
- }
- target[key] = value
- }
- })
apply方法用于拦截函数的调用、call、apply操作。
- const sum = (num1, num2) => {
- return num1 + num2
- }
- const proxy = new Proxy(sum, {
- apply(target, context, args) {
- // 我们可以通过 Reflect.apply()来调用目标函数
- return Reflect.apply(...arguments) * 2
- }
- })
- proxy(3,4) // 14
has方法用于拦截hasProperty操作,即判断对象是否具有某个属性时,该方法有效。值得注意的是,对for...in循环是没有效果的。has方法可以判断该属性来自继承的属性,不只是自身的属性。下面是拦截访问私有属性“_prop”
- var handler = {
- has(target, key){
- if(key[0]==='_'){
- return false
- }
- return key in target
- }
- }
- var target = {_prop:'foo', prop:'foo'};
- var proxy = new Proxy(target, handler)
- '_prop' in proxy //false
该方法的本意是构造,所以会拦截 new 操作符命令。该方法接受两个参数:
- var handler = {
- construct (target, args, newTarget){
- return new target(...args);
- }
- }
例子:
- var p = new Proxy(function() {}, {
- construct: function(target, argumentsList, newTarget) {
- console.log('called: ' + argumentsList.join(', '));
- return { value: argumentsList[0] * 10 };
- }
- });
-
- console.log(new p(1).value); // "called: 1"
- // 10
deleteProperty方法用于拦截delete操作,如果这个方法抛出错误或者返回false,当前属性就无法被delete命令删除。值得注意的是,目标对象自身的不可配置(configurable)的属性不能被deleteProperty方法删除,否则会报错。
- var handler = {
- deleteProperty(target, key){
- invariant(key, 'delete');
- return true
- }
- }
- function invariant(key, action){
- if(key[0]==='_'){
- throw new Error(`Invalid attemp to ${action} private "${key}" property`)
- }
- }
- var target = {_prop:'foo'}
- var proxy = new Proxy(target, handler);
- delete proxy._prop //Error
该方法用于拦截Object.defineProperty操作。下面操作添加新的属性会触发defineProperty函数,所以会报错。如果目标对象不可扩展(extensible)或不可写(writable)或不可配置(configurable),也不可添加新属性,否则也会报错。
- var handler = {
- defineProperty(target, key, descriptor){
- return false
- }
- }
- var target {};
- var proxy = new Proxy(target, handler);
- proxy.foo = 'bar'
该方法用于拦截Object.getOwnPropertyDescriptor(),返回一个属性描述对象或者undefined。
- var handler = {
- getOwnPropertyDescriptor(target, key){
- if(key[0]==='_'){
- return
- }
- return Object.getOwnPropertyDescriptor(target, key)
- }
- }
- var target = {_foo:'bar', baz:'tar'};
- var proxy = new Proxy(target, handler);
- Object.getOwnPropertyDescriptor(proxy, 'wat'); //undefined
- Object.getOwnPropertyDescriptor(proxy, '_foo'); //undefined
- Object.getOwnPropertyDescriptor(proxy, 'baz'); //{value:'tar', writable:true,enumerable:true,configurable:true}
该方法用于拦截获取对象的原型。有如下几种情况:
- var proto = {}
- var p = new Proxy({}, {
- getPrototypeOf(target){
- return proto
- }
- })
- Object.getPrototypeOf(p) === proto //true
值得注意的是getPrototype方法返回值必须是对象或者null,否则会报错。目标对象不可扩展(extensible),那么该方法必须返回对象的原型对象。
该方法用于拦截Object.isExtensible()操作。
- var p = new Proxy({},{
- isExtensible: function(target){
- return true
- }
- })
该方法返回值必须是布尔值,否则会转成布尔值。如果返回值与目标对象的IsExtensible(target)不一致,也会抛出错误。
该方法用于拦截对象自身属性的读取操作。可以拦截以下操作:
- let target = {
- a:1,
- b:2,
- c:3
- }
- let handler = {
- ownKeys(target){
- return ['a']
- }
- }
- let proxy = new Proxy(target,handler);
- Object.keys(proxy) //['a']
该方法拦截 Object.preventExtensions () ,该方法必须返回个布尔值,否则会被自动转为布尔值。只有目标对象不可扩展时,即 Object.isExtensible(proxy)为false, proxy.preventExtensions 才能返回 true ,否则会报错。
- var p =new Proxy({}, {
- preventExtensions: function(target) {
- return true;
- }
- })
- Object.preventExtensions(p) //报错
该方法主要用于拦截 Object.setPrototypeOf 方法。下面的代码中,只要修改 target 的原型对象就会错。
- var handler = {
- setPrototypeOf (target, proto) {
- throw new Error ('Changing the prototype is forbidden');
- }
- }
- var proto = {} ;
- var target = function () {};
- var proxy = new Proxy(target , handler) ;
- Object.setPrototypeOf(proxy, proto ); //Error : Changing the prototype is forbidden
Proxy.revocable 方法返回 一个可取消 Proxy实例。
- let target = {};
- let handler = {};
- let { proxy, revoke } = Proxy.revocable(target, handler);
- proxy.foo = 123;
- proxy.foo //123
- revoke();
- proxy.foo //TypeError: Revoked
Proxy.revocable 方法返回一个对象,其 proxy 属性是 Proxy 实例, revoke 属性是一个函数,可以取消 Proxy 实例。上面的代码中,当执行 revoke 函数后再访问 Proxy 实例,就会抛出一个错误。
Proxy.revocable 个使用场景是,目标对象不允许直接访问,必须通过代理访问,一旦访问结束,就收回代理权,不允许再次访问。
上述的实例方法在很多地方都有很大的作用,特别是对对象进行拦截的场景的时候。ES6语法在近年来已经很多使用,特别是vue3.0会使用proxy作为数据的响应式写法。
一般面试还都会问你Object.defineProperty有什么缺点,然后问你有没有关注vue3.0,知道3.0中的响应式使用什么写法。还有Proxy有什么方法之类的。
参考文章
欢迎大家关注我的公众号《前端小时》,不定期推送学习心得以及技术文章!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。