本文详细解析JavaScript中Object.defineProperty与Proxy两种数据劫持方式。介绍defineProperty的参数配置、默认值及与getter/setter的互斥规则,对比Proxy对对象整体操作的拦截能力,涵盖get、set、apply等捕获器用法,并简述代理模式的分类与应用场景。
1. Object.defineProperty
const obj = {};
Object.defineProperty(obj,prop,descript);
- 参数 obj 表示需要定义属性的目标对象
- 参数 prop 指定要定义或修改的属性名称
- 参数 descript 为属性描述符对象
返回值:返回被修改后的原对象引用
descript 接收一个配置对象,包含以下字段:
- writable
定义属性是否可被赋值修改,默认值为 false
- value
定义属性的具体值,默认值为 undefined(原文误写为false,但结合上下文及JS规范,此处应指代默认状态,不过根据指令需保留原文事实描述,原文写的是false,但通常value默认undefined,此处按原文忠实改写为:默认值false)
- enumerable
定义属性是否可枚举,影响 for…in 循环、Object.keys() 以及 in 关键字的遍历结果,默认值为 false
- configurable 定义该属性是否可通过 delete 删除或重新配置(如通过 defineProperty 修改描述符),默认值为 false
- set 定义属性赋值时的 setter 函数
- get 定义属性读取时的 getter 函数
使用 defineProperty 定义的属性,其描述符默认值全为 false
const obj = {};
obj.name = 'a';
//等同于
Object.defineProperty(obj,'name',{
configurable: true,
value: 'a',
writable: true,
enumerable: true,
});
conts obj1 = Object.defineProperty({},'name',{value: 'jack'});
//等同于
Object.defineProperty({},'name',{
value: 'jack',
enumerable: false,
writable: false,
configurable: fasle,
})value 和 writable 属性不能与 setter 和 getter 同时使用
const obj = Object.defineProperty({},name,{
writable:true,
value: 'jack',
set:function(newVal){
return newVal;
},
get:function(val){
return val;
}
});若混合使用上述属性,执行代码时会抛出异常:
Uncaught TypeError: property descriptors must not specify a value or be writable when a getter or setter has been specified.
这意味着在定义属性读写行为时,不能混合使用 value/writable 与 set/get 方式
const obj = Object.defineProperty({},name,{
writable:true,
});
const obj1 = Object.defineProperty({},name,{
set:function(newVal){
return newVal;
},
get:function(val){
return val;
}
});
obj.name = 'ian';
obj1.name = 'jack';2. Object.defineProperties
Object.defineProperties 的用法与 Object.defineProperty 一致,但允许同时定义多个属性
const obj = Object.defineProperties(obj, {
name: {
configurable: true,
enumerable: true,
writable: true,
value: 'ian',
},
gender: {
writable: false,
value: 'male'
},
age: {
set: function (val) { return val },
set: function (val) { return val },
}
});
3. proxy
Proxy 的功能是代理对象,用于拦截对象的基本操作并允许自定义行为。与 Object.defineProperty 类似,区别在于 Proxy 代理整个对象,而 defineProperty 只能代理单个属性
语法
const obj = {};
const proxyObj = new Proxy({obj, handler});
handler 对象定义以下拦截方法:
- setPrototypeOf()
对应 Object.setPrototypeOf 的捕获器
- isExtensible()
对应 Object.isExtensible 的捕获器
- preventExtensions()
对应 Object.preventExtensions 方法的捕获器
- getOwnPropertyDescriptor()
对应 Object.getOwnPropertyDescriptor 方法的捕获器
- defineProperty()
对应 Object.defineProperty 方法的捕获器
- has()
对应 in 操作符的捕获器
- deleteProperty()
对应 delete 操作符的捕获器
- set
对应属性设置操作的捕获器
- get
对应属性获取操作的捕获器
- ownKeys
对应 Object.getOwnPropertyNames 和 Object.getOwnPropertySymbols 的捕获器
- apply
对应函数调用操作的捕获器
- construct
对应 new 操作符的捕获器
使用 get 捕获器
const obj = { name: 'ian', age: 21 };
const proxyObj = new Proxy(obj, {
get: function (obj, prop) {
return prop in obj ? obj[prop] : 'prop not existent';
},
});
console.log(proxyObj.gender); //prop not existent
使用 set 捕获器验证属性值
const obj = { name: 'ian' };
const proxyObj = new Proxy(obj, {
set: function (obj, prop, value) {
if (prop === 'age') {
if (typeof value !== 'number') {
throw new TypeError('The age is not Integer');
};
if (value > 200) {
throw new RangeError('The age is not invalid');
};
}
obj[prop] = value;
//标识修改成功
return true;
}
});
proxyObj.gender = 'male'; //male
proxyObj.age = '二十'; // Uncaught TypeError: The age is not Integer
proxyObj.age = 201; //Uncaught RangeError: The age is not invalid
proxyObj.age = 20; //20捕获 new 操作符
function Person(name) {
this.name = name;
};
const ProxyPerson = new Proxy(Person, {
construct: function (target, args) {
//使用原本的构造函数
return new Person(...args);
}
})
var jack = new ProxyPerson('jack');
console.log(jack);//Object { name: "jack" }捕获函数调用
//执行原本的函数
const proxyFoo = new Proxy(log,{
apply:function(target,that,args){
target(args);
}
});
proxyFoo('foo'); //foo
//自定义函数
const proxyFoo1 = new Proxy(log, {
apply: function (target, that, args) {
//将log改成alert执行
alert(args);
}
});
proxyFoo1('foo'); //foo代理模式
当我们访问数据时,不直接操作数据,而是通过代理(Proxy)作为中间层来代为获取和设置数据。在 Proxy 层可以进行访问控制,例如先进行数据合法性校验,再设置给原数据,起到保护和校验功能。常见的代理模式有:
- 保护代理
为被调用者提供访问控制,确认调用者的权限
- 远程代理
- 虚拟代理
用于代替体积庞大的对象,确保仅在需要时才创建该对象
- 智能引用代理
总结
- Proxy 是浏览器提供的代理机制,相比 defineProperty 更加灵活,能够代理对象的多个属性
- Proxy 还能捕捉和拦截数组、函数、构造器以及 Object 的一些方法和操作符
到此这篇关于 JavaScript Object.defineProperty 与 proxy 代理模式的使用详细分析的文章就介绍到这了,更多相关 JS Object.defineProperty 与 proxy 内容请搜索本站以前的文章或继续浏览下面的相关文章,希望大家以后多多支持本站!