【前端学习】ES6(三)proxy

本文详细解析了Proxy对象的get、set、apply等核心方法,展示了如何拦截属性读写和函数调用,以及在私有变量、构造函数、原型链等方面的应用。实例演示了Proxy如何影响`this`指向和处理对象操作的权限控制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

十四、Proxy

Proxy 实例的方法
get()
set()
apply()
has()
construct()
deleteProperty()
defineProperty()
getOwnPropertyDescriptor()
getPrototypeOf()
isExtensible()
ownKeys()
preventExtensions()
setPrototypeOf()

注意:
handler可以写在函数里面,如果需要复用或者需要创建proxy时简便一些可以单独声明。第一个参数也是如此。

var handler = {
    get:function(){},
    set:function(){}
}
var obj = {}
var proxy1 = new Proxy(obj,handler)
var proxy2 = new Proxy(obj,{
    get:function(){},
    set:function(){}
})

1、get方法用于拦截某个属性的读取操作,可以接受三个参数,依次为目标对象、属性名和 proxy 实例本身(严格地说,是操作行为所针对的对象),其中最后一个参数可选。

var person = {
  name: "周子越"
};
var proxy = new Proxy(person, {
  get: function(target, propKey, p) {
    console.log('目标对象->',target) // 目标对象->,{"name":"周子越"}
    console.log('键名->',propKey) // 键名->,name
    return target[propKey]
  }
});
proxy.name // 打印的话就是 周子越
/**
 *  目标对象->,{"name":"周子越"}
 *  键名->,name
 */
proxy.age // 打印的话就是undefined 
/**
 *  目标对象->,{"name":"周子越"} 
 *  键名->,age // 虽然没有但是还是打出了传入的键名
 */
var childProxy = Object.create(proxy) // 以proxy为原型创建一个对象并返回
childProxy.sex
/**
 *  目标对象->,{"name":"周子越"} 
 *  键名->,sex
 *  可见get被继承了
 */

2、set方法用来拦截某个属性的赋值操作,可以接受四个参数,依次为目标对象、属性名、属性值和 Proxy 实例本身,其中最后一个参数可选。

var person = {
  name: "周子越"
};
var proxy = new Proxy(person, {
  get: function(target, propKey, p) {
    console.log('目标对象->',target) // 目标对象->,{"name":"周子越"}
    console.log('键名->',propKey) // 键名->,name
    return target[propKey]
  },
  set: function(target, propKey, value, p){
    target[propKey] = value
    console.log(`${propKey}对应的值变为${value}`)
    return true // 注意如果没有返回true 在严格模式下会报错
  }
});

proxy.name = '123' // name对应的值变为123
proxy.name
/**
 * 目标对象->,{"name":"123"}
 * 键名->,name
 */
proxy.sex = '男' // sex对应的值变为男 
proxy.sex
/**
 * 目标对象->,{"name":"123","sex":"男"}
 * 键名->,sex
 */

3、apply方法拦截函数的调用、call和apply操作。
apply方法可以接受三个参数,分别是目标对象、目标对象的上下文对象(this)和目标对象的参数数组。

var target = function(){
    return 'I am the target'
}
var handler = {
    apply:function(){
        return 'I am the hanlder'
    }
}
var proxy = new Proxy(target,handler)
console.log(proxy()) // I am the handler
console.log(proxy.call())// I am the handler
console.log(proxy.apply())// I am the handler

尽管proxy作为一个object而非function,但当他作为函数调用、call调用、apply调用时,被proxy拦截,
4、has方法用来拦截HasProperty操作,即判断对象是否具有某个属性时,这个方法会生效。典型的操作就是in运算符。
has方法可以接受两个参数,分别是目标对象、需查询的属性名。
下面是隐藏私有变量的两种handler的实现

var handler1 = {
  has (target, key) {
    if (key[0] === '_') {
      return false;
    }
    return key in target;
  }
};
var handler2 = {
  has (target, key) {
    if (key.search(/^_/)>-1) {
      return false;
    }
    return key in target;
  }
};
var target = { _prop: 'foo', prop: 'foo' };
var proxy = new Proxy(target, handler2);
console.log('_prop' in proxy) // false

注意has的拦截对for in不生效
5、construct()方法用于拦截new命令,下面是拦截对象的写法。
construct方法可以接受三个参数。
target:目标对象
args:构造函数的参数对象
newTarget:创造实例对象时,new命令作用的构造函数(下面例子的p)

var handler = {
  construct (target, args, newTarget) {
    return new target(...args);
  }
};

6、deleteProperty()方法用于拦截delete操作,如果这个方法抛出错误或者返回false,当前属性就无法被delete命令删除。
拦截删除私有变量的操作。

var handler = {
    deleteProperty(target, key) {
        if (key.search(/^_/) > -1) {
            console.log('不可删除私有变量')
            return false
        }
        delete target[key];
        console.log('已经删除变量'+key)
        return true;
    }

};
var target = { _prop: 'foo', prop: 'foo' };
var proxy = new Proxy(target, handler);
delete proxy._prop //不可删除私有变量

7、defineProperty()
defineProperty()方法拦截了Object.defineProperty()操作。

var handler = {
    defineProperty(target, key, descriptor) {
        return false;
    }
};
var target = {};
var proxy = new Proxy(target, handler);
proxy.foo = 'bar'// 不会生效
console.log(proxy.foo) // undefined

因为每次都返回false,所以任何对属性的定义都无效。
8、getOwnPropertyDescriptor()
getOwnPropertyDescriptor()方法拦截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 }

9、getPrototypeOf()
getPrototypeOf()方法主要用来拦截获取对象原型。具体来说,拦截下面这些操作。只接受一个target对象
Object.prototype._ _ proto _ _
Object.prototype.isPrototypeOf()
Object.getPrototypeOf()
Reflect.getPrototypeOf()
instanceof

var proto ={};
var p =newProxy({},{
  getPrototypeOf(target){
return proto;
}
});
Object.getPrototypeOf(p)=== proto // true

9、isExtensible()
isExtensible()方法拦截Object.isExtensible()操作。
10、ownKeys()
ownKeys()方法用来拦截对象自身属性的读取操作。具体来说,拦截以下操作。
Object.getOwnPropertyNames()
Object.getOwnPropertySymbols()
Object.keys()
for…in循环
11、preventExtensions()
preventExtensions()方法拦截Object.preventExtensions()。该方法必须返回一个布尔值,否则会被自动转为布尔值。
12、setPrototypeOf()
setPrototypeOf()方法主要用来拦截Object.setPrototypeOf()方法。

Proxy.revocable()
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

this问题
虽然 Proxy 可以代理针对目标对象的访问,但它不是目标对象的透明代理,即不做任何拦截的情况下,也无法保证与目标对象的行为一致。主要原因就是在 Proxy 代理的情况下,目标对象内部的this关键字会指向 Proxy 代理。

const target = {
  m: function () {
    console.log(this === proxy);
  }
};
const handler = {};
const proxy = new Proxy(target, handler);
target.m() // false
proxy.m()  // true

上面代码中,一旦proxy代理target.m,后者内部的this就是指向proxy,而不是target。
下面是一个例子,由于this指向的变化,导致 Proxy 无法代理目标对象。

const _name = new WeakMap();
class Person {
  constructor(name) {
    _name.set(this, name);
  }
  get name() {
    return _name.get(this);
  }
}
const jane = new Person('Jane');
jane.name // 'Jane'
const proxy = new Proxy(jane, {});
proxy.name // undefined
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值