Object.defineProperty() 完整指南

Object.defineProperty() 完整指南

1. 基本概念

Object.defineProperty() 方法允许精确地添加或修改对象的属性。默认情况下,使用此方法添加的属性是不可修改的。

1.1 基本语法

Object.defineProperty(obj, prop, descriptor)

参数说明:

  • obj: 要定义属性的对象
  • prop: 要定义或修改的属性名
  • descriptor: 属性描述符对象

2. 属性描述符

2.1 数据描述符

const obj = {};Object.defineProperty(obj, 'name', {value: 'John',          // 属性值writable: true,         // 是否可写enumerable: true,       // 是否可枚举configurable: true      // 是否可配置
});

2.2 访问器描述符

const obj = {_name: 'John'
};Object.defineProperty(obj, 'name', {get() {return this._name;},set(value) {this._name = value;},enumerable: true,configurable: true
});

3. 实际应用示例

3.1 数据劫持(Vue2响应式原理)

function observe(obj) {if (typeof obj !== 'object' || obj === null) {return;}Object.keys(obj).forEach(key => {defineReactive(obj, key, obj[key]);});
}function defineReactive(obj, key, val) {// 递归处理嵌套对象observe(val);Object.defineProperty(obj, key, {get() {console.log(`获取${key}属性`);return val;},set(newVal) {if (val === newVal) return;console.log(`设置${key}属性为${newVal}`);val = newVal;// 触发更新}});
}// 使用示例
const data = {name: 'John',age: 20
};observe(data);
data.name = 'Mike'; // 设置name属性为Mike
console.log(data.name); // 获取name属性 Mike

3.2 私有属性模拟

function Person(name) {let _name = name;Object.defineProperty(this, 'name', {get() {return _name;},set(value) {if (typeof value !== 'string') {throw new Error('Name must be a string');}_name = value;}});
}const person = new Person('John');
console.log(person.name); // John
person.name = 'Mike'; // 正常设置
person.name = 123; // 抛出错误

3.3 计算属性实现

function computed(obj, key, computeFunc) {let value = computeFunc();Object.defineProperty(obj, key, {get() {return value;},set() {console.warn(`${key} is a computed property, cannot be modified`);}});
}const obj = {a: 1,b: 2
};computed(obj, 'sum', () => obj.a + obj.b);
console.log(obj.sum); // 3
obj.sum = 10; // 警告:sum is a computed property, cannot be modified

4. 注意事项和限制

4.1 不可扩展对象

const obj = {};
Object.preventExtensions(obj);// 这将抛出错误
Object.defineProperty(obj, 'name', {value: 'John'
});

4.2 继承属性

const parent = {};
Object.defineProperty(parent, 'name', {value: 'John',writable: false
});const child = Object.create(parent);
// 这将抛出错误
child.name = 'Mike';

4.3 属性描述符限制

const obj = {};// 不能同时指定 value/writable 和 get/set
Object.defineProperty(obj, 'name', {value: 'John',get() {return 'John';}
}); // 抛出错误

5. 性能考虑

5.1 大量属性处理

// 不推荐
const obj = {};
for (let i = 0; i < 1000; i++) {Object.defineProperty(obj, `prop${i}`, {value: i,writable: true});
}// 推荐
const descriptors = {};
for (let i = 0; i < 1000; i++) {descriptors[`prop${i}`] = {value: i,writable: true,configurable: true,enumerable: true};
}
Object.defineProperties(obj, descriptors);

5.2 访问器性能

// 避免在访问器中进行复杂计算
Object.defineProperty(obj, 'name', {get() {// 不推荐return complexCalculation();}
});// 推荐:缓存计算结果
let cachedValue;
Object.defineProperty(obj, 'name', {get() {if (cachedValue === undefined) {cachedValue = complexCalculation();}return cachedValue;}
});

6. 最佳实践

  1. 描述符默认值
// 记住默认值都是 false
Object.defineProperty(obj, 'name', {value: 'John'// writable: false// enumerable: false// configurable: false
});
  1. 使用 TypeScript 类型
interface PropertyDescriptor {configurable?: boolean;enumerable?: boolean;value?: any;writable?: boolean;get?(): any;set?(v: any): void;
}
  1. 错误处理
function safeDefineProperty(obj, prop, descriptor) {try {Object.defineProperty(obj, prop, descriptor);return true;} catch (error) {console.error(`Failed to define property ${prop}:`, error);return false;}
}

7. 总结

Object.defineProperty() 的关键点:

  1. 使用场景

    • 数据劫持
    • 私有属性模拟
    • 计算属性实现
    • 属性访问控制
  2. 注意事项

    • 描述符类型限制
    • 性能考虑
    • 继承关系处理
    • 错误处理
  3. 最佳实践

    • 合理使用缓存
    • 避免复杂计算
    • 注意默认值
    • 做好错误处理

10. 深入理解 Object.defineProperty()

10.1 基础概念详解

Object.defineProperty() 是 JavaScript 中用于在对象上定义新属性或修改现有属性的方法。它允许精确控制属性的特性。

// 基本语法
Object.defineProperty(obj, prop, descriptor)// 参数说明
// obj: 要定义属性的对象
// prop: 要定义或修改的属性名
// descriptor: 属性描述符对象

10.2 属性描述符详解

属性描述符分为两种类型:数据描述符和访问器描述符。

  1. 数据描述符的完整选项:
const obj = {};
Object.defineProperty(obj, 'name', {value: 'John',          // 属性值writable: true,         // 是否可写enumerable: true,       // 是否可枚举configurable: true      // 是否可配置
});
  1. 访问器描述符的完整选项:
const obj = {_name: 'John'
};
Object.defineProperty(obj, 'name', {get() {console.log('Getting value');return this._name;},set(value) {console.log('Setting value to', value);this._name = value;},enumerable: true,configurable: true
});

10.3 常见使用场景

  1. 只读属性:
const obj = {};
Object.defineProperty(obj, 'readonly', {value: 'I am read-only',writable: false,enumerable: true,configurable: false
});obj.readonly = 'New value'; // 无效
console.log(obj.readonly);  // 'I am read-only'
  1. 不可枚举属性:
const obj = {};
Object.defineProperty(obj, 'hidden', {value: 'You cannot see me',enumerable: false
});console.log(Object.keys(obj)); // []
console.log(obj.hidden);       // 'You cannot see me'
  1. 计算属性:
const person = {firstName: 'John',lastName: 'Doe'
};Object.defineProperty(person, 'fullName', {get() {return `${this.firstName} ${this.lastName}`;},set(value) {[this.firstName, this.lastName] = value.split(' ');}
});console.log(person.fullName);    // 'John Doe'
person.fullName = 'Jane Smith';
console.log(person.firstName);   // 'Jane'
console.log(person.lastName);    // 'Smith'
  1. Vue 双向绑定实现:
function observe(obj) {if (!obj || typeof obj !== 'object') return;// 遍历对象的每个属性Object.keys(obj).forEach(key => {let value = obj[key];let dep = new Dep(); // 依赖收集器Object.defineProperty(obj, key, {get() {// 收集依赖if (Dep.target) {dep.addDep(Dep.target);}return value;},set(newValue) {if (value === newValue) return;value = newValue;// 通知所有依赖进行更新dep.notify();}});// 递归观察子属性if (typeof value === 'object') {observe(value);}});
}// 使用示例
const data = {user: {name: 'John',age: 20}
};observe(data);
// 现在 data 对象的所有属性都是响应式的

10.4 注意事项和最佳实践

  1. 描述符限制:
// 不能同时使用数据描述符和访问器描述符
Object.defineProperty(obj, 'prop', {value: 123,get() { return 123; } // 错误!
});
  1. 性能优化:
// 批量定义属性
Object.defineProperties(obj, {prop1: {value: 123,writable: true},prop2: {get() { return this.prop1 * 2; }}
});
  1. 默认值处理:
// 所有描述符属性默认为 false
Object.defineProperty(obj, 'prop', {value: 123// writable: false// enumerable: false// configurable: false
});

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/65783.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

使用云计算开发App 有哪些坑需要避免

当我们在云计算环境下开发App时&#xff0c;往往会觉得一切都变得“轻松”了。毕竟&#xff0c;云计算提供了无限的计算资源、灵活的存储方案&#xff0c;还有自动化的服务&#xff0c;仿佛有了一个万能的工具箱&#xff0c;啥都能搞定。可是&#xff0c;别被这种“轻松”外表骗…

C13.【C++ Cont】初识string类字符串的迭代器

目录 1.迭代器的定义 2.迭代器的作用 3.string类字符串的常用迭代器 4.第3点的两个迭代器的使用 示例代码1:解引用 运行结果 示例代码2 运行结果 示例代码3:用迭代器正序遍历字符串 运行结果 示例代码4:用迭代器逆序遍历字符串 运行结果 示例代码5:用迭代器修改字…

HTML——13.超链接

<!DOCTYPE html> <html><head><meta charset"UTF-8"><title>超链接</title></head><body><!--超链接:从一个网页链接到另一个网页--><!--语法&#xff1a;<a href"淘宝网链接的地址"> 淘宝…

进程间通信的“五大武器”

&#x1f604;作者简介&#xff1a; 小曾同学.com,一个致力于测试开发的博主⛽️&#xff0c;主要职责&#xff1a;测试开发、CI/CD 如果文章知识点有错误的地方&#xff0c;还请大家指正&#xff0c;让我们一起学习&#xff0c;一起进步。 &#x1f60a; 座右铭&#xff1a;不…

【机器学习】分类

文章目录 1. 能否用回归解决分类问题2. 生成模型&#xff08;概率生成&#xff09;3. 判别模型&#xff08;逻辑回归&#xff09;4. 多分类问题 1. 能否用回归解决分类问题 二元分类 数据分布不规律&#xff0c;回归函数会尽量减少误差&#xff0c;导致不合理的偏移离分界较远…

《代码随想录》Day22打卡!

回溯算法 《代码随想录》回溯算法&#xff1a;组合 本题完整题目如下&#xff1a; 本题的完整思路如下&#xff1a; 1.本题使用回溯算法&#xff0c;其实回溯和递归是一样的道理&#xff0c;也是分为三步曲进行&#xff1a; 2.第一步&#xff1a;确定递归函数的返回值和参数&…

鱼眼相机模型与去畸变实现

1.坐标系说明 鱼眼相机模型涉及到世界坐标系、相机坐标系、图像坐标系、像素坐标系之间的转换关系。对于分析鱼眼相机模型&#xff0c;假定世界坐标系下的坐标点,经过外参矩阵的变换转到相机坐标系&#xff0c;相机坐标再经过内参转换到像素坐标&#xff0c;具体如下 进一步进…

[图形渲染]【Unity Shader】【游戏开发】 Shader数学基础17-法线变换基础与应用

在计算机图形学中,法线(normal) 是表示表面方向的向量。它在光照、阴影、碰撞检测等领域有着重要作用。本文将介绍如何在模型变换过程中正确变换法线,确保其在光照计算中的正确性,特别是法线与顶点的变换问题。 1. 法线与切线的基本概念 法线(Normal Vector) 法线(或…

我的2024年度总结2025展望

2025已经到来&#xff0c;下面是我对2024年的个人总结&#xff0c;以及对2025年的未来展望。 2024总结——峰回路转 2024大概是我大学这几年收获最满的一年&#xff0c;我不仅收获了丰富的技术内容&#xff0c;也提高了例如情商、管理能力、团队协作、开源思想等技术之外的事…

Windows 下安装 triton 教程

目录 背景解决方法方法一&#xff1a;&#xff08;治标不治本&#xff09;方法二&#xff1a;&#xff08;triton-windows&#xff09;- 安装 MSVC 和 Windows SDK- vcredist 安装- whl 安装- 验证 背景 triton 目前官方只有Linux 版本&#xff0c;若未安装&#xff0c;则会出…

如何使用网络工具进行网络性能评估

网络评估是对IT基础设施的系统评估&#xff0c;以确保它能够很好地满足企业的核心运营需求&#xff0c;确定了基础设施中需要改进的领域&#xff0c;并定义了改进的范围。 网络评估工具分析IT基础设施的各个方面&#xff0c;它通过评估网络设备、网络性能和安全威胁来仔细检查…

Vue.js组件开发-实现动态切换菜单简单示例

在Vue.js中&#xff0c;实现动态切换菜单通过组件化开发和Vue的响应式数据绑定来实现。 示例&#xff1a; 展示如何创建一个可以动态切换菜单的Vue组件。 首先&#xff0c;需要定义一个Vue组件&#xff0c;该组件将包含菜单项和用于切换菜单的状态。 1. 创建Vue组件 <t…

【笔记_连续发请求的问题】

疑问&#xff1a; 连续给同一个账号下的同一个应用发送三次启动应用请求&#xff0c;顺序是&#xff1a;订单1、订单2、订单3&#xff0c;但是有时候的执行顺序是&#xff1a;订单1、订单3、订单2 不管用不用队列&#xff0c;用不用webhook&#xff0c;都会出现这种情况 回答…

Vue项目整合与优化

前几篇文章&#xff0c;我们讲述了 Vue 项目构建的整体流程&#xff0c;从无到有的实现了单页和多页应用的功能配置&#xff0c;但在实现的过程中不乏一些可以整合的功能点及可行性的优化方案&#xff0c;就像大楼造完需要进行最后的项目验收改进一样&#xff0c;有待我们进一步…

Python、R用深度学习神经网络组合预测优化能源消费总量时间序列预测及ARIMA、xgboost对比...

全文链接&#xff1a;https://tecdat.cn/?p38726 分析师&#xff1a;Qingxia Wang 在能源领域&#xff0c;精准预测能源消费总量对制定合理能源战略至关重要。当前&#xff0c;能源消费预测分析主要运用单一模型&#xff08;如灰色预测法、时间序列分析法等&#xff09;和组合…

STM32使用UART发送字符串与printf输出重定向

首先我们先看STM32F103C8T6的电路图 由图可知&#xff0c;其PA9和PA10引脚分别为UART的TX和RX(注意&#xff1a;这个电路图是错误的&#xff0c;应该是PA9是X而PA9是RX&#xff0c;我们看下图的官方文件可以看出)&#xff0c;那么接下来我们应该找到该引脚的定义是什么&#xf…

Kotlin在医疗大健康域的应用实例探究与编程剖析(下)

四、Kotlin医疗编程实例分析 4.1 移动医疗应用实例 4.1.1 患者健康监测应用 在当今数字化医疗时代,患者健康监测应用为人们提供了便捷的健康管理方式。利用Kotlin开发的患者健康监测应用,能够实时采集患者的各类生理数据,如心率、血压、血氧饱和度等,并通过直观的可视化…

探索数据之美,Plotly引领可视化新风尚

在数据如潮的今天&#xff0c;如何精准捕捉信息的脉搏&#xff0c;让数据说话&#xff1f;Plotly&#xff0c;这款强大的数据可视化工具&#xff0c;正以其卓越的性能和丰富的功能&#xff0c;成为数据分析师、科学家及工程师们的得力助手。 Plotly不仅仅是一个绘图库&#xf…

Redis 5设计与源码分析读书笔记

目录 引言Redis 5.0的新特性Redis源码概述Redis安装与调试 简单动态字符串数据结构基本操作创建字符串释放字符串拼接字符串扩容策略 其余API 本章小结兼容C语言字符串、保证二进制安全sdshdr5的特殊之处是什么SDS是如何扩容的 跳跃表简介跳跃表节点与结构跳跃表节点跳跃表结构…

Golang学习历程【第五篇 复合数据类型:数组切片】

Golang学习历程【第五篇 复合数据类型&#xff1a;数组&切片】 1. 数组&#xff08;Array&#xff09;1.1 数组的定义1.2 初始化数组1.3 数据的循环遍历1.4 多维数组 2. 切片&#xff08;Slice&#xff09;2.1 切片声明、初始化2.2 基于数组创建切片2.2 切片的长度(len)和容…