当前位置: 首页 > news >正文

JavaScript中Object.defineProperty的作用和用法以及和proxy的区别

在 JavaScript 中,Object.defineProperty() 是一个强大的内置方法,用于精确控制对象属性的配置。它允许你直接修改对象属性的底层特性(如可读写性、可枚举性、可配置性),甚至可以自定义属性的 getter 和 setter 方法。

核心作用

  1. 精确控制属性配置
    通过设置属性描述符(descriptor),可以精确控制属性的行为。
  2. 实现数据劫持
    通过 getter/setter 拦截属性的读取和赋值操作,常用于响应式系统(如 Vue.js 的数据绑定)。
  3. 阻止属性被意外修改
    通过设置 writable: falseconfigurable: false 保护属性不被修改或删除。

基本语法

Object.defineProperty(obj, prop, descriptor);
  • obj:需要定义属性的对象。
  • prop:需要定义或修改的属性名称(字符串或 Symbol)。
  • descriptor:属性描述符对象,包含以下可选键:
    • value:属性的值(默认 undefined)。
    • writable:属性值是否可修改(默认 false)。
    • enumerable:属性是否可被枚举(如 for...in 循环,默认 false)。
    • configurable:属性是否可被删除或重新配置(默认 false)。
    • get:获取属性值的函数(与 value 互斥)。
    • set:设置属性值的函数(与 value 互斥)。

用法示例

1. 基本属性定义
const person = {};Object.defineProperty(person, 'name', {value: 'John',writable: true,     // 允许修改值enumerable: true,   // 可被枚举configurable: true  // 可被重新配置或删除
});console.log(person.name); // "John"
person.name = 'Jane';     // 修改有效(writable: true)
console.log(person.name); // "Jane"
2. 只读属性(writable: false
const person = {};Object.defineProperty(person, 'age', {value: 30,writable: false,    // 只读enumerable: true
});console.log(person.age); // 30
person.age = 31;         // 赋值无效(严格模式下会报错)
console.log(person.age); // 30
3. 不可枚举属性(enumerable: false
const person = { name: 'John' };Object.defineProperty(person, 'age', {value: 30,enumerable: false   // 不可枚举
});console.log(person.age); // 30
for (const key in person) {console.log(key); // 仅输出 "name"(age 不可枚举)
}
4. 不可配置属性(configurable: false
const person = {};Object.defineProperty(person, 'name', {value: 'John',configurable: false  // 不可重新配置或删除
});delete person.name;    // 删除无效
console.log(person.name); // "John"// 尝试重新配置会报错(严格模式下)
Object.defineProperty(person, 'name', {value: 'Jane'
}); // TypeError: Cannot redefine property: name
5. 数据劫持(getter/setter)
const person = {_age: 30 // 约定使用下划线表示私有属性
};Object.defineProperty(person, 'age', {get() {console.log('读取 age');return this._age;},set(newValue) {console.log('设置 age 为', newValue);if (newValue < 0) throw new Error('年龄不能为负数');this._age = newValue;}
});console.log(person.age); // 读取 age → 30
person.age = 31;         // 设置 age 为 31
person.age = -5;         // Error: 年龄不能为负数

应用场景

  1. 实现响应式系统
    Vue.js 2.x 利用 Object.defineProperty() 实现数据劫持,当属性值变化时自动更新 DOM:

    function defineReactive(obj, key, value) {Object.defineProperty(obj, key, {get() {// 依赖收集return value;},set(newValue) {if (value !== newValue) {value = newValue;// 触发更新updateDOM();}}});
    }
    
  2. 实现私有属性
    通过 getter/setter 控制属性访问权限:

    const person = {};
    let _age = 30; // 闭包中的私有变量Object.defineProperty(person, 'age', {get() { return _age; },set(value) { _age = value; }
    });
    
  3. 阻止对象被篡改
    通过设置 configurable: falsewritable: false 保护关键属性:

    const config = { API_KEY: 'secret' };Object.defineProperty(config, 'API_KEY', {writable: false,configurable: false
    });// 无法修改或删除 API_KEY
    

注意事项

  1. 兼容性Object.defineProperty() 是 ES5 引入的方法,不支持 IE8 及以下版本。
  2. 性能影响:频繁触发 getter/setter 会有一定性能开销,避免在高性能场景(如大型循环)中过度使用。
  3. 深度监听Object.defineProperty() 只能监听对象的直接属性,若要监听嵌套对象,需要递归处理。
  4. 数组监听限制:直接监听数组变化存在局限性(Vue.js 3 改用 Proxy 解决此问题)。

Proxy 的对比

特性Object.defineProperty()Proxy
监听范围只能监听对象的已有属性(需逐个定义)可以监听整个对象(包括新增属性)
数组支持对数组的监听有限(需特殊处理)全面支持数组操作
元编程能力仅能控制单个属性可以拦截多种操作(如 indelete
兼容性ES5(支持 IE9+)ES6(不支持 IE)

Object.defineProperty() 是 JavaScript 中强大的元编程工具,适合需要精确控制对象属性行为的场景,尤其在实现响应式系统、数据验证和属性保护等方面有广泛应用。

http://www.lryc.cn/news/587758.html

相关文章:

  • Linux多进程
  • 《美术教育研究》是什么级别的期刊?是正规期刊吗?能评职称吗?
  • Combine的介绍与使用
  • C++-linux 7.文件IO(三)文件元数据与 C 标准库文件操作
  • SVD、DCT图像压缩实践
  • 什么是电磁锁控制板?24路锁控板的使用步骤概述
  • MySQL数据库的基础操作
  • Java Integer包装类缓存机制详解
  • 《汇编语言:基于X86处理器》第7章 复习题和练习,编程练习
  • 最大最小公平策略(Max-Min Fairness)
  • 测试驱动开发(TDD)实战:在 Spring 框架实现中践行 “红 - 绿 - 重构“ 循环
  • 软考 系统架构设计师系列知识点之杂项集萃(111)
  • EasyExcel实现Excel文件导入导出
  • 文心4.5开源之路:引领技术开放新时代!
  • Cannot add property 0, object is not extensible
  • 收集飞花令碎片——VS调试技巧
  • Linux(Ubuntu)硬盘使用情况解析(已房子举例)
  • 中间件部署
  • Ubuntu22.04 python环境管理
  • LabVIEW-Origin 船模数据处理系统
  • ubuntu之坑(十五)——设备树
  • SnapKit介绍与使用
  • EPLAN 电气制图(八):宏应用与变频器控制回路绘制全攻略
  • 基于esp32系列的开源无线dap-link项目使用介绍
  • RocketMQ 5.x初体验
  • Linux 音频的基石: ALSA
  • React 第六十九节 Router中renderMatches的使用详解及注意事项
  • Android 性能优化:启动优化全解析
  • 019_工具集成与外部API调用
  • LabVIEW浏览器ActiveX事件交互