Object.defineProperty vs Proxy 对比总结

Object.defineProperty vs Proxy 对比总结

一、基本区别

特性 Object.defineProperty Proxy
出现时间 ES5 (2009) ES6 (2015)
监听范围 单个属性 整个对象
API复杂度 较复杂 更简洁
性能 较好 稍差(但可接受)
浏览器支持 几乎全部 IE不支持

二、核心能力对比

1. 监听范围

javascript 复制代码
// defineProperty - 需要逐个属性定义
const obj = { name: '张三', age: 18 }
Object.defineProperty(obj, 'name', { get() {...}, set() {...} })
Object.defineProperty(obj, 'age', { get() {...}, set() {...} })
// 新增属性无法监听

// Proxy - 监听整个对象
const proxy = new Proxy(obj, {
    get(target, key) {...},
    set(target, key, value) {...}
})
// 所有属性(包括新增)都能监听

2. 数组监听

javascript 复制代码
// defineProperty - 无法直接监听数组变化
const arr = [1, 2, 3]
// 需要重写数组方法才能实现

// Proxy - 完美支持数组
const proxyArr = new Proxy(arr, {
    set(target, key, value) {
        console.log(`设置索引${key}: ${value}`)
        target[key] = value
        return true
    }
})
proxyArr.push(4) // 可以监听到

3. 嵌套监听

javascript 复制代码
// defineProperty - 需要递归遍历
function observe(obj) {
    Object.keys(obj).forEach(key => {
        if (typeof obj[key] === 'object') {
            observe(obj[key]) // 递归
        }
        defineReactive(obj, key)
    })
}

// Proxy - 惰性代理
const proxy = new Proxy(obj, {
    get(target, key) {
        const val = target[key]
        // 只在访问时才代理对象
        if (typeof val === 'object') {
            return new Proxy(val, handler)
        }
        return val
    }
})

三、功能对比

功能 defineProperty Proxy
监听属性读取 ✅ get ✅ get
监听属性赋值 ✅ set ✅ set
监听属性删除 ❌ ✅ deleteProperty
监听函数调用 ❌ ✅ apply
监听构造函数 ❌ ✅ construct
监听in操作符 ❌ ✅ has
监听for...in ❌ ✅ ownKeys
监听属性定义 ❌ ✅ defineProperty

四、实际代码对比

Vue 2 vs Vue 3 响应式

javascript 复制代码
// Vue 2 - defineProperty
function defineReactive(obj, key, val) {
    Object.defineProperty(obj, key, {
        get() {
            console.log(`获取${key}: ${val}`)
            return val
        },
        set(newVal) {
            console.log(`设置${key}: ${newVal}`)
            val = newVal
        }
    })
}

// Vue 3 - Proxy
const reactive = (target) => {
    return new Proxy(target, {
        get(target, key, receiver) {
            const res = Reflect.get(target, key, receiver)
            console.log(`获取${String(key)}`)
            return res
        },
        set(target, key, value, receiver) {
            const res = Reflect.set(target, key, value, receiver)
            console.log(`设置${String(key)}: ${value}`)
            return res
        }
    })
}

五、优缺点总结

Object.defineProperty

优点:

  • 性能较好
  • 兼容性好(IE9+)
  • 可精确控制单个属性

缺点:

  • 无法监听数组变化(需要hack)
  • 无法监听新增/删除属性
  • 需要递归遍历对象
  • API不够直观

Proxy

优点:

  • 监听能力强(13种拦截器)
  • 支持数组、对象新增属性
  • 无需递归(惰性代理)
  • API更简洁
  • 可以代理多种数据结构

缺点:

  • 兼容性稍差(无IE)
  • 性能略低于defineProperty
  • 无法polyfill完全

六、使用场景建议

场景 推荐方案 原因
简单对象监听 defineProperty 性能好,足够用
复杂响应式系统 Proxy 功能强大,维护简单
需要兼容IE defineProperty Proxy不支持IE
数组操作监听 Proxy defineProperty需要hack
动态属性监听 Proxy defineProperty无法监听新增属性
性能敏感场景 defineProperty 执行效率更高

七、最佳实践示例

javascript 复制代码
// 简单属性监听 - 用defineProperty
const formData = {}
Object.defineProperty(formData, 'username', {
    set(val) {
        console.log('输入验证:', val)
        this._username = val
    }
})

// 复杂响应式 - 用Proxy
const reactive = (obj) => {
    return new Proxy(obj, {
        get(target, key) {
            if (typeof target[key] === 'object') {
                return reactive(target[key]) // 递归代理
            }
            return Reflect.get(target, key)
        },
        set(target, key, value) {
            console.log(`更新视图: ${key}=${value}`)
            return Reflect.set(target, key, value)
        }
    })
}

总结

  • defineProperty:简单、性能好、兼容性强,但功能受限
  • Proxy:强大、灵活、语义清晰,但需要现代浏览器

Vue 3 选择 Proxy 是因为它能更好地处理动态属性、数组等复杂场景,代码更简洁。如果不需要兼容IE,Proxy是更好的选择。

相关推荐
szial18 分钟前
JavaScript 的 call、apply 和 bind:如何控制函数调用
开发语言·前端·javascript
CopyCode30 分钟前
Agent 开发不是模型训练:一个前端的入门认知
前端·llm·agent
吠品40 分钟前
HTTPS 真身:HTTP 套了层 TLS
java·服务器·前端
JYeontu43 分钟前
实现一个「曲线滑块验证」功能
前端·javascript·html
Csvn44 分钟前
组件库(从 API 契约到 Headless 内核)
前端
deli0071 小时前
为什么最近邻路线总在绕远?2-opt 把 50 城 TSP 缩短了 20.9%
前端
guslegend1 小时前
底层数据设计:读写分离、分库分表与热点数据隔离
前端
Southern Wind1 小时前
AIAgent——第一章:本地 Agent 与 Vue 3 流式全栈实战
前端·javascript·vue.js
可乐鸡翅yeah_1 小时前
HLS 业务 Referer‑Policy 网页元标签引发播放异常排错
前端·javascript·网络·ffmpeg·php·音视频
jj_ccwgw1 小时前
从 0 到 1 搭建 pnpm + Workspaces + Turborepo Monorepo 保姆级指南
前端·node.js