前言
在JavaScript的世界里,对象是构建程序的基本积木。有没有想过,如果我们能在不改变对象本身的情况下,拦截并自定义它的各种操作,比如属性访问、赋值、删除等,那会带来多大的可能性?JavaScript的内置对象Proxy就是这样一个强大的工具,它能让你像魔法一样控制对象的行为。

在这篇文章中,我们将深入探讨Proxy的工作原理、使用方法以及在实际开发中的应用场景。无论你是前端新手还是有经验的开发者,这篇文章都将帮助你理解并掌握这个强大的特性。
一、什么是Proxy?
Proxy是JavaScript ES6引入的一个内置对象,它允许你创建一个代理对象,用来拦截并可能重定义对目标对象的各种操作。简单来说,Proxy就像是一个中间人,所有对目标对象的操作都会先经过这个中间人,你可以在这个中间过程中做一些手脚。
1.1 Proxy的基本语法
创建一个Proxy非常简单,只需要使用new Proxy()构造函数,并传入两个参数:目标对象和处理器对象。
const target = { name: "目标对象", value: 42 };
const handler = {
// 在这里定义各种拦截方法(陷阱)
};
const proxy = new Proxy(target, handler);
在上面的代码中:
target是你要代理的目标对象handler是一个包含各种拦截方法(也称为"陷阱")的对象proxy是返回的代理对象,所有对它的操作都会被handler拦截处理
1.2 为什么需要Proxy?
在JavaScript发展的早期,我们无法直接拦截对象的基本操作。要实现类似的功能,开发者通常需要手动定义getter和setter方法,或者使用Object.defineProperty(),但这些方法都有局限性。
Proxy的出现解决了这些问题,它提供了一种更优雅、更强大的方式来拦截对象操作。通过Proxy,我们可以实现:
- 数据验证和过滤
- 对象属性的访问控制
- 响应式编程
- 虚拟属性和计算属性
- 日志记录和性能监控
- … 以及更多高级功能
二、Proxy的工作原理
2.1 拦截机制
Proxy的核心是它的拦截机制。当我们对代理对象执行某种操作时,JavaScript引擎会检查处理器对象中是否定义了对应的拦截方法(陷阱)。如果有,就会调用该方法;如果没有,就会默认转发给目标对象。

这种拦截机制使得我们可以在不修改目标对象的情况下,自定义对象的行为。这是一种非侵入式的扩展方式,非常适合用于库和框架的开发。
2.2 Proxy与目标对象的关系
需要注意的是,代理对象和目标对象是两个不同的对象,但它们共享相同的底层数据。当我们通过代理对象修改属性时,实际上修改的是目标对象的属性;反之,直接修改目标对象的属性也会反映到代理对象上。
const target = { name: "目标对象" };
const handler = {};
const proxy = new Proxy(target, handler);
proxy.value = 42; // 通过代理对象设置属性
console.log(target.value); // 输出: 42,目标对象也被修改了
target.status = "active"; // 直接修改目标对象
console.log(proxy.status); // 输出: "active",代理对象也能访问到
三、Proxy陷阱详解
Proxy提供了一系列拦截方法,称为"陷阱"(Traps),每种陷阱对应一种对象操作。下面我们来详细了解这些陷阱的用法。

3.1 get陷阱
get陷阱用于拦截属性访问操作,它在读取代理对象的属性值时触发。
const target = { name: "小明", age: 25 };
const handler = {
get(target, prop, receiver) {
console.log(`访问属性: ${prop}`);
// 可以做一些额外的处理,比如属性不存在时提供默认值
return prop in target ? target[prop] : `属性${prop}不存在`;
}
};
const proxy = new Proxy(target, handler);
console.log(proxy.name); // 输出: "访问属性: name" 和 "小明"
console.log(proxy.gender); // 输出: "访问属性: gender" 和 "属性gender不存在"
3.2 set陷阱
set陷阱用于拦截属性设置操作,它在给代理对象的属性赋值时触发。
const target = { name: "小明", age: 25 };
const handler = {
set(target, prop, value, receiver) {
console.log(`设置属性: ${prop} = ${value}`);
// 可以做一些验证,比如年龄必须是数字且在合理范围内
if (prop === 'age' && (typeof value !== 'number' || value < 0 || value > 150)) {
throw new Error('年龄必须是0-150之间的数字');
}
target[prop] = value;
return true; // 必须返回true表示设置成功
}
};
const proxy = new Proxy(target, handler);
proxy.name = "小红"; // 输出: "设置属性: name = 小红"
proxy.age = 26; // 输出: "设置属性: age = 26"
// proxy.age = 200; // 抛出错误: "年龄必须是0-150之间的数字"
3.3 deleteProperty陷阱
deleteProperty陷阱用于拦截删除属性操作,它在使用delete操作符删除代理对象的属性时触发。
const target = { name: "小明", age: 25, _id: "user123" };
const handler = {
deleteProperty(target, prop) {
console.log(`尝试删除属性: ${prop}`);
// 可以保护某些关键属性不被删除
if (prop.startsWith('_')) {
throw new Error(`不能删除私有属性: ${prop}`);
}
delete target[prop];
return true;
}
};
const proxy = new Proxy(target, handler);
delete proxy.age; // 输出: "尝试删除属性: age",删除成功
// delete proxy._id; // 抛出错误: "不能删除私有属性: _id"
3.4 has陷阱
has陷阱用于拦截in操作符,它在检查属性是否存在于对象中时触发。
const target = { name: "小明", age: 25 };
const handler = {
has(target, prop) {
console.log(`检查属性是否存在: ${prop}`);
// 可以隐藏某些属性,使其在in操作符中不可见
if (prop === 'age') {
return false;
}
return prop in target;
}
};
const proxy = new Proxy(target, handler);
console.log('name' in proxy); // 输出: "检查属性是否存在: name" 和 true
console.log('age' in proxy); // 输出: "检查属性是否存在: age" 和 false,但实际上age属性是存在的
3.5 apply陷阱
apply陷阱用于拦截函数调用,它在调用代理函数时触发。
function add(a, b) {
return a + b;
}
const handler = {
apply(target, thisArg, args) {
console.log(`调用函数: ${target.name}(${args.join(', ')})`);
// 可以在函数调用前后做一些处理
console.log('计算开始...');
const result = target.apply(thisArg, args);
console.log('计算结束...');
return result;
}
};
const proxyAdd = new Proxy(add, handler);
console.log(proxyAdd(1, 2)); // 输出一系列日志,并返回3
3.6 construct陷阱
construct陷阱用于拦截new操作符,它在使用new创建代理对象的实例时触发。
function Person(name, age) {
this.name = name;
this.age = age;
}
const handler = {
construct(target, args, newTarget) {
console.log(`创建实例: ${target.name}(${args.join(', ')})`);
// 可以在创建实例前后做一些处理,比如参数验证
if (args.length < 2) {
throw new Error('需要提供name和age两个参数');
}
// 使用new操作符创建实例
return new target(...args);
}
};
const ProxyPerson = new Proxy(Person, handler);
const person = new ProxyPerson('小明', 25); // 创建成功
console.log(person.name); // 输出: "小明"
// const person2 = new ProxyPerson('小红'); // 抛出错误
3.7 其他陷阱
除了上面介绍的常用陷阱外,Proxy还提供了其他一些陷阱,用于拦截对象的各种操作:
getPrototypeOf: 拦截获取原型对象的操作,如Object.getPrototypeOf()setPrototypeOf: 拦截设置原型对象的操作,如Object.setPrototypeOf()getOwnPropertyDescriptor: 拦截获取属性描述符的操作,如Object.getOwnPropertyDescriptor()defineProperty: 拦截定义属性的操作,如Object.defineProperty()ownKeys: 拦截获取对象所有属性键的操作,如Object.keys()、Object.getOwnPropertyNames()等preventExtensions: 拦截阻止对象扩展的操作,如Object.preventExtensions()isExtensible: 拦截检查对象是否可扩展的操作,如Object.isExtensible()
四、实际应用场景
Proxy在实际开发中有广泛的应用,下面我们来看看一些常见的应用场景。

4.1 数据验证
使用Proxy的set陷阱,我们可以轻松实现数据验证功能,确保对象的属性值符合特定的规则。
const userValidator = {
set(target, prop, value) {
// 姓名必须是字符串且长度在2-20之间
if (prop === 'name' && (!value || typeof value !== 'string' || value.length < 2 || value.length > 20)) {
throw new Error('姓名必须是2-20个字符的字符串');
}
// 年龄必须是数字且在0-150之间
if (prop === 'age' && (typeof value !== 'number' || value < 0 || value > 150)) {
throw new Error('年龄必须是0-150之间的数字');
}
// 邮箱必须符合邮箱格式
if (prop === 'email' && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)) {
throw new Error('请输入有效的邮箱地址');
}
target[prop] = value;
return true;
}
};
const user = new Proxy({}, userValidator);
user.name = '小明'; // 成功
user.age = 25; // 成功
user.email = 'xiaoming@example.com'; // 成功
// user.age = 200; // 抛出错误
4.2 响应式编程
Proxy是实现响应式编程的绝佳工具,它可以在数据变化时自动触发相应的回调函数。这也是很多现代前端框架(如Vue 3)实现响应式系统的核心机制。
function reactive(obj, callback) {
return new Proxy(obj, {
set(target, prop, value) {
const oldValue = target[prop];
// 如果值没有变化,不触发回调
if (oldValue === value) {
return true;
}
// 更新值
target[prop] = value;
// 触发回调,通知数据变化
callback(prop, oldValue, value);
return true;
},
deleteProperty(target, prop) {
const hadProperty = prop in target;
const oldValue = target[prop];
delete target[prop];
// 如果属性存在并被成功删除,触发回调
if (hadProperty) {
callback(prop, oldValue, undefined, 'delete');
}
return true;
}
});
}
// 使用示例
const state = reactive({ count: 0 }, (key, oldVal, newVal, type = 'update') => {
console.log(`属性 ${key} 从 ${oldVal} 变为 ${newVal} (${type})`);
// 这里可以更新UI或执行其他副作用
});
state.count += 1; // 输出: "属性 count 从 0 变为 1 (update)"
delete state.count; // 输出: "属性 count 从 1 变为 undefined (delete)"
4.3 属性保护
使用Proxy,我们可以实现对象属性的保护,比如只读属性、私有属性等。
const user = {
name: '小明',
age: 25,
_password: 'secret123', // 私有属性,约定以下划线开头
__config: { debug: false } // 内部配置,约定以双下划线开头
};
const handler = {
get(target, prop) {
// 禁止访问内部配置
if (prop.startsWith('__')) {
throw new Error(`无法访问内部属性: ${prop}`);
}
// 私有属性需要特殊方法才能访问
if (prop.startsWith('_')) {
console.warn(`警告: ${prop} 是私有属性,建议通过方法访问`);
}
return target[prop];
},
set(target, prop, value) {
// 内部配置为只读
if (prop.startsWith('__')) {
throw new Error(`无法修改内部属性: ${prop}`);
}
// 可以为某些属性设置只读
if (prop === 'name') {
throw new Error('姓名为只读属性');
}
target[prop] = value;
return true;
},
deleteProperty(target, prop) {
// 禁止删除任何属性
throw new Error('不能删除对象的属性');
}
};
const protectedUser = new Proxy(user, handler);
console.log(protectedUser.name); // 输出: "小明"
// protectedUser.name = '小红'; // 抛出错误: "姓名为只读属性"
// delete protectedUser.age; // 抛出错误: "不能删除对象的属性"
// console.log(protectedUser.__config); // 抛出错误
4.4 延迟加载
Proxy可以帮助我们实现延迟加载(也称为惰性加载),即只有在真正需要访问属性时才计算或加载其值。这对于处理大型数据集或昂贵的计算操作非常有用。
const lazyObject = {
// 这个属性的值计算起来很昂贵
get expensiveData() {
console.log('计算昂贵的数据...');
// 模拟耗时操作
setTimeout(() => {
console.log('计算完成');
}, 1000);
return '昂贵的计算结果';
}
};
// 使用Proxy实现延迟加载
const lazyProxy = new Proxy({}, {
get(target, prop) {
// 如果目标对象中没有该属性,但有对应的getter方法
if (!(prop in target) && typeof lazyObject[`get_${prop}`] === 'function') {
// 调用getter方法计算值并缓存
target[prop] = lazyObject[`get_${prop}`]();
}
return target[prop];
}
});
// 另一种延迟加载实现
const lazyLoaded = new Proxy({}, {
get(target, prop) {
// 检查是否已经加载过
if (!(prop in target)) {
console.log(`加载属性: ${prop}`);
// 模拟从服务器或其他地方加载数据
target[prop] = `加载的${prop}数据`;
}
return target[prop];
}
});
console.log(lazyLoaded.name); // 输出: "加载属性: name" 和 "加载的name数据"
console.log(lazyLoaded.name); // 直接输出: "加载的name数据",不再重新加载
4.5 日志记录和性能监控
使用Proxy,我们可以轻松地为对象的操作添加日志记录和性能监控功能。
function createLoggedObject(target, name = 'Object') {
const startTimeMap = new Map();
return new Proxy(target, {
get(target, prop) {
const start = performance.now();
const result = Reflect.get(target, prop);
const end = performance.now();
console.log(`[${name}] 访问属性 ${prop}: 耗时 ${(end - start).toFixed(3)}ms`);
return result;
},
set(target, prop, value) {
const start = performance.now();
const result = Reflect.set(target, prop, value);
const end = performance.now();
console.log(`[${name}] 设置属性 ${prop}=${value}: 耗时 ${(end - start).toFixed(3)}ms`);
return result;
},
// 其他陷阱也可以类似地添加日志
});
}
// 使用示例
const data = createLoggedObject({ name: '小明', age: 25 }, 'UserData');
data.name; // 输出访问日志
4.6 代理链
我们可以创建多个代理,形成一个代理链,每个代理负责处理不同的关注点。这种方式符合单一职责原则,使代码更加清晰和可维护。
// 第一个代理:数据验证
function createValidationProxy(target, validators) {
return new Proxy(target, {
set(target, prop, value) {
if (validators[prop] && !validators[prop](value)) {
throw new Error(`属性 ${prop} 的值 ${value} 无效`);
}
return Reflect.set(target, prop, value);
}
});
}
// 第二个代理:日志记录
function createLoggingProxy(target) {
return new Proxy(target, {
get(target, prop) {
console.log(`访问: ${prop}`);
return Reflect.get(target, prop);
},
set(target, prop, value) {
console.log(`设置: ${prop} = ${value}`);
return Reflect.set(target, prop, value);
}
});
}
// 第三个代理:属性保护
function createProtectionProxy(target, readOnlyProps = []) {
return new Proxy(target, {
set(target, prop, value) {
if (readOnlyProps.includes(prop)) {
throw new Error(`属性 ${prop} 是只读的`);
}
return Reflect.set(target, prop, value);
}
});
}
// 创建代理链
const user = {};
const validators = {
age: (value) => typeof value === 'number' && value >= 0 && value <= 150,
name: (value) => typeof value === 'string' && value.length > 0
};
// 从内到外依次是:验证 -> 日志 -> 保护
const proxy = createProtectionProxy(
createLoggingProxy(
createValidationProxy(user, validators)
),
['name'] // name属性是只读的
);
// 使用代理链
proxy.name = '小明'; // 设置成功,输出日志
// proxy.name = '小红'; // 抛出错误:name是只读的
proxy.age = 25; // 设置成功,输出日志
// proxy.age = 200; // 抛出错误:age的值无效
五、常见使用误区和解决方法
虽然Proxy非常强大,但在使用过程中也有一些需要注意的地方,下面我们来看看一些常见的使用误区和解决方法。
5.1 忘记返回值
在set陷阱中,如果忘记返回true,可能会导致一些意外的行为,特别是在严格模式下。
错误用法:
const handler = {
set(target, prop, value) {
target[prop] = value;
// 忘记返回true
}
};
正确用法:
const handler = {
set(target, prop, value) {
target[prop] = value;
return true; // 必须返回true表示设置成功
}
};
5.2 直接修改目标对象
在使用Proxy时,应该始终通过代理对象来操作数据,而不是直接修改目标对象。直接修改目标对象可能会绕过代理的拦截逻辑,导致预期外的行为。
错误用法:
const target = { count: 0 };
const handler = {
set(target, prop, value) {
console.log(`设置 ${prop} = ${value}`);
target[prop] = value;
return true;
}
};
const proxy = new Proxy(target, handler);
target.count = 1; // 直接修改目标对象,绕过了代理的拦截逻辑
正确用法:
const target = { count: 0 };
const handler = {
set(target, prop, value) {
console.log(`设置 ${prop} = ${value}`);
target[prop] = value;
return true;
}
};
const proxy = new Proxy(target, handler);
proxy.count = 1; // 通过代理对象修改,会触发拦截逻辑
5.3 递归陷阱
在陷阱中访问代理对象的属性可能会导致无限递归。为了避免这种情况,应该使用Reflect对象或直接访问目标对象。
错误用法:
const handler = {
get(target, prop) {
console.log(`访问 ${prop}`);
return proxy[prop]; // 递归调用,会导致栈溢出
}
};
const proxy = new Proxy({}, handler);
正确用法:
const handler = {
get(target, prop) {
console.log(`访问 ${prop}`);
return target[prop]; // 直接访问目标对象
// 或者使用Reflect:return Reflect.get(target, prop);
}
};
const proxy = new Proxy({}, handler);
5.4 this指向问题
在使用Proxy时,需要注意方法中的this指向。默认情况下,通过代理对象调用的方法中的this指向的是代理对象,而不是目标对象。
示例:
const target = {
name: '小明',
getName() {
return this.name;
}
};
const handler = {};
const proxy = new Proxy(target, handler);
console.log(target.getName()); // 输出: "小明",this指向target
console.log(proxy.getName()); // 输出: "小明",this指向proxy,但proxy继承了target的属性
// 修改proxy的name属性
proxy.name = '小红';
console.log(proxy.getName()); // 输出: "小红"
如果需要确保this始终指向目标对象,可以在陷阱中使用bind、call或apply方法来修改this的指向。
5.5 性能考虑
虽然Proxy非常强大,但它的性能开销比直接操作对象要大。因此,在性能敏感的场景中,应该谨慎使用Proxy,或者只在必要的地方使用。
优化建议:
- 对于频繁访问的属性,可以考虑缓存结果
- 尽量减少陷阱中的复杂逻辑
- 在性能关键路径上,考虑使用其他更高效的方式
- 使用
Reflect对象来简化拦截逻辑,它通常比手动实现更高效
六、高级应用技巧
除了基本用法外,Proxy还有一些高级应用技巧,可以帮助我们解决更复杂的问题。
6.1 使用Reflect对象
Reflect是ES6引入的另一个内置对象,它提供了一组与Proxy陷阱对应的静态方法。使用Reflect可以简化Proxy的拦截逻辑,使其更加清晰和可维护。
const handler = {
get(target, prop, receiver) {
console.log(`访问: ${prop}`);
return Reflect.get(target, prop, receiver);
},
set(target, prop, value, receiver) {
console.log(`设置: ${prop} = ${value}`);
return Reflect.set(target, prop, value, receiver);
},
// 其他陷阱也可以使用对应的Reflect方法
};
6.2 代理数组
Proxy不仅可以代理普通对象,还可以代理数组。这使得我们可以拦截数组的各种操作,如元素访问、添加、删除等。
const arr = [1, 2, 3];
const handler = {
get(target, prop, receiver) {
console.log(`访问数组的 ${prop} 属性`);
return Reflect.get(target, prop, receiver);
},
set(target, prop, value, receiver) {
console.log(`设置数组的 ${prop} 属性为 ${value}`);
return Reflect.set(target, prop, value, receiver);
}
};
const proxyArr = new Proxy(arr, handler);
proxyArr.push(4); // 会触发多个拦截操作
console.log(proxyArr[0]); // 输出: "访问数组的 0 属性" 和 1
6.3 代理函数
我们还可以代理函数,拦截函数的调用、new操作符等。
function greet(name) {
return `Hello, ${name}!`;
}
const handler = {
apply(target, thisArg, args) {
console.log(`调用函数: ${target.name}(${args.join(', ')})`);
return target.apply(thisArg, args);
},
construct(target, args, newTarget) {
console.log(`使用new创建实例: ${args.join(', ')}`);
return new target(...args);
}
};
const proxyFunc = new Proxy(greet, handler);
console.log(proxyFunc('小明')); // 输出调用日志和 "Hello, 小明!"
6.4 虚拟属性
使用Proxy,我们可以实现虚拟属性,即这些属性并不实际存在于目标对象中,而是通过计算或其他方式动态生成的。
const user = {
firstName: '张',
lastName: '三'
};
const handler = {
get(target, prop) {
// 如果访问的是fullName属性,计算并返回全名
if (prop === 'fullName') {
return `${target.firstName}${target.lastName}`;
}
// 其他属性正常返回
return Reflect.get(target, prop);
},
set(target, prop, value) {
// 如果设置的是fullName属性,分解并设置firstName和lastName
if (prop === 'fullName' && typeof value === 'string') {
const parts = value.split(' ');
if (parts.length >= 2) {
target.firstName = parts[0];
target.lastName = parts[1];
return true;
}
}
// 其他属性正常设置
return Reflect.set(target, prop, value);
},
has(target, prop) {
// 让虚拟属性在in操作符中也可见
if (prop === 'fullName') {
return true;
}
return Reflect.has(target, prop);
}
};
const proxy = new Proxy(user, handler);
console.log(proxy.fullName); // 输出: "张三"
proxy.fullName = '李 四';
console.log(proxy.firstName); // 输出: "李"
console.log(proxy.lastName); // 输出: "四"
console.log('fullName' in proxy); // 输出: true
6.5 实现不可变对象
使用Proxy,我们可以实现真正的不可变对象,禁止任何对对象的修改。
function createImmutableObject(target) {
// 递归地为嵌套对象创建代理
if (typeof target === 'object' && target !== null) {
// 为数组创建代理
if (Array.isArray(target)) {
const immutableArray = [];
for (let i = 0; i < target.length; i++) {
immutableArray[i] = createImmutableObject(target[i]);
}
target = immutableArray;
}
// 为普通对象创建代理
else {
const immutableObj = {};
for (const key in target) {
if (Object.prototype.hasOwnProperty.call(target, key)) {
immutableObj[key] = createImmutableObject(target[key]);
}
}
target = immutableObj;
}
}
// 创建代理,禁止任何修改
return new Proxy(target, {
set() {
throw new Error('Cannot modify immutable object');
},
deleteProperty() {
throw new Error('Cannot delete properties from immutable object');
},
defineProperty() {
throw new Error('Cannot define properties on immutable object');
},
setPrototypeOf() {
throw new Error('Cannot change prototype of immutable object');
}
});
}
// 使用示例
const data = createImmutableObject({
name: '小明',
age: 25,
address: {
city: '北京',
street: '朝阳区'
},
hobbies: ['读书', '运动']
});
// 尝试修改将抛出错误
// data.name = '小红';
// data.address.city = '上海';
// data.hobbies.push('音乐');
七、总结与展望
JavaScript的Proxy是一个非常强大的特性,它为我们提供了一种优雅、灵活的方式来拦截和自定义对象的行为。通过Proxy,我们可以实现数据验证、响应式编程、属性保护、延迟加载等各种高级功能。
Proxy的设计思想体现了JavaScript的元编程能力,它允许我们在运行时修改对象的行为,这为库和框架的开发提供了极大的便利。事实上,很多现代前端框架(如Vue 3、MobX等)都在内部使用了Proxy来实现其核心功能。
随着JavaScript语言的不断发展,我们可以期待Proxy在未来会有更多的应用场景和更强大的功能。作为开发者,掌握Proxy的使用技巧,将有助于我们编写更加灵活、高效和可维护的代码。
无论你是前端开发新手,还是有经验的老司机,我都希望这篇文章能帮助你更好地理解和应用JavaScript的Proxy特性。让我们一起探索这个神奇的对象,解锁更多JavaScript的编程技巧吧!
附录:Proxy陷阱速查表
为了方便查阅,下面是所有Proxy陷阱及其触发时机的速查表:
| 陷阱 | 触发时机 | 说明 |
|---|---|---|
| get | obj.prop 或 obj[prop] | 拦截属性访问 |
| set | obj.prop = value 或 obj[prop] = value | 拦截属性设置 |
| deleteProperty | delete obj.prop 或 delete obj[prop] | 拦截删除属性 |
| has | prop in obj | 拦截 in 操作符 |
| apply | target(…args) 或 target.apply/call() | 拦截函数调用 |
| construct | new target(…args) | 拦截 new 操作符 |
| getPrototypeOf | Object.getPrototypeOf(obj) | 获取原型对象 |
| setPrototypeOf | Object.setPrototypeOf(obj, proto) | 设置原型对象 |
| getOwnPropertyDescriptor | Object.getOwnPropertyDescriptor(obj, prop) | 获取属性描述符 |
| defineProperty | Object.defineProperty(obj, prop, desc) | 定义属性 |
| ownKeys | Object.keys(obj), Object.getOwnPropertyNames(obj) 等 | 获取所有属性键 |
| preventExtensions | Object.preventExtensions(obj) | 阻止对象扩展 |
| isExtensible | Object.isExtensible(obj) | 检查对象是否可扩展 |
最后,创作不易请允许我插播一则自己开发的“数规规-排五助手”(有各种趋势分析)小程序广告,感兴趣可以微信小程序体验放松放松,程序员也要有点娱乐生活,搞不好就中个排列五了呢?
感兴趣的可以微信搜索小程序“数规规-排五助手”体验体验!或直接浏览器打开如下链接:
https://www.luoshu.online/jumptomp.html
可以直接跳转到对应小程序
如果觉得本文有用,欢迎点个赞👍+收藏🔖+关注支持我吧!

被折叠的 条评论
为什么被折叠?



