浅谈Object.defineProperty()

本文详细介绍了JavaScript中的Object.defineProperty方法,包括其基本用法、参数详解以及如何通过该方法实现属性的读取和设置操作。

浅谈Object.defineProperty()

date : 2017.09.01

Object.defineProperty()在IE9及IE9以上才被支持,它接受三个参数,而且都是必填的。
1. 第一个参数:目标对象;
2. 第二个参数:需要定义的属性或方法的名字;
3. 第三个参数:目标属性所拥有的特性;(descriptor)

descriptor

descriptor这里我们统称是Object.defineProperty()这个方法的第三个参数,并且它拥有以下的取值

  • value
var obj = {};
Object.defineProperty(obj,"hello",{
    value:"hello"
})
console.log(obj)  //{hello:"hello"}

//这个参数较为好理解,如果说第一个参数是一个对象A,那么第二个参数则为对象A的一个属性B,那么value即为属性B的一个值;
  • writable
var obj = {};
Object.defineProperty(obj,"hello",{
    value : "hello",
    writable : false
    //writable有两个值可选 false(不可被重写),true(可以被重写)
})
console.log(obj.hello)   //hello
obj.hello = "hello world";  //重新定义obj.hello的值
console.log(obj.hello)  //hello 
  • configurable
/**
* @see:【总开关】
* @see:定义是否可以删除(再次修改)目标属性以及属性的特性(writable, configurable, enumerable)
* @see:true[可以被删除或可以重新设置特性]
* @see:false[不能被可以被删除或不可以重新设置特性] 默认为false
*/

/*--- false情况下删除属性 ---*/
var obj = {};
Object.defineProperty(obj,"hello",{
    value:"hello",
    configurable:false,
    writable:false
})
console.log(obj.hello)  //hello
delete obj.hello;
console.log(obj.hello)  //hello

/*--- true情况下删除属性 ---*/
var obj = {};
Object.defineProperty(obj,"hello",{
    value:"hello",
    configurable:true,
    writable:false
})
console.log(obj.hello)  //hello
delete obj.hello;
console.log(obj.hello)  //undefined

/*--- false情况下修改特性 ---*/
var obj = {};
Object.defineProperty(obj,"hello",{
    value:"hello",
    configurable:false,
    writable:false
})
//重新修改特性
Object.defineProperty(obj,"hello",{
    value:"hello",
    writable:true,
    configurable:true
});
console.log(obj.hello)  //Uncaught TypeError: Cannot redefine property: hello

/*--- true情况下修改特性 ---*/
var obj = {};
Object.defineProperty(obj,"hello",{
    value:"hello",
    configurable:true,
    writable:true
})
//重新修改特性
Object.defineProperty(obj,"hello",{
    value:"hello",
    writable:false,
    configurable:false
});
console.log(obj.hello)  //hello
  • enumerable
/**
* @see:使用for...in或Object.keys()
* @see:true[可以被枚举]
* @see:false[不能被枚举] 默认为false
*/

/*--- false情况下不能被枚举 ---*/
var obj = {};
Object.defineProperty(obj,"hello",{
    value:"hello",
    enumerable:false
});
for(item in obj){
    console.log(item)
}
// undefined

/*--- true情况下可以被枚举 ---*/
var obj = {};
Object.defineProperty(obj,"hello",{
    value:"hello",
    enumerable:true
});
for(item in obj){
    console.log(item)
}
// hello

注意:一旦使用Object.defineProperty给对象添加属性,那么如果不设置属性的特性,那么configurable、enumerable、writable这些值都为默认的false

var obj = {};
Object.defineProperty(obj,'hello',{
    value:"hello"
});
//定义的新属性后,这个属性的特性中configurable,enumerable,writable都为默认的值false
  • 存取器 get/set
var obj = {};
Object.defineProperty(obj,"hello",{
    set:function(newValue){
        console.log("newValue",newValue)
    },
    get:function(){
        console.log("Return completed !")
    }
});
obj.hello = "this is hello !"  //newValue this is hello !
/*从对象obj的属性hello设置来看,执行了set方法*/
console.log(obj.hello)  
//this is hello !
//Return completed !
/*console.log()方法打印除了两段话,说明获取对象属性,执行了get方法*/

那么就比较好理解了,对象属性的赋值以及取值会分别触发get 和 set 对应的函数

注意
- get或set不是必须成对出现。如果不设置方法,则get和set的默认值为undefined;
- 当使用了get或set方法,不允许使用writable和value这两个属性;

### Object.defineProperty的缺点 #### 1. 无法监听新增属性 `Object.defineProperty`只能劫持对象已存在的属性,当新增属性时,它无法检测到变化。例如: ```javascript const obj = {}; Object.defineProperty(obj, 'name', { get() { console.log('读取name'); }, set() { console.log('修改name'); } }); obj.name = '张三'; // 能触发setter obj.age = 18; // 无法触发任何反应!新增属性时Object.defineProperty根本不知道 ``` 这意味着如果对象需要动态添加属性并希望这些属性也具有响应性,必须手动重新定义这些属性的描述符。 #### 2. 无法监听删除属性 同样,当从对象中删除属性时,`Object.defineProperty`也无法检测到这一变化: ```javascript delete obj.name; // 同样无法触发反应! ``` 这导致了在处理对象属性的增删时,开发者需要额外的工作来确保状态的一致性和响应性。 #### 3. 对数组的支持有限 `Object.defineProperty`对数组的支持有限,特别是对于数组的方法调用(如`push`, `pop`, `shift`, `unshift`, `splice`, `sort`, `reverse`)不会触发属性的`set`方法。为了克服这个问题,Vue 2.x通过重写数组的方法来实现对数组变化的监听。 #### 4. 需要递归遍历对象 为了使对象的所有嵌套属性都具有响应性,需要递归地对每个属性应用`Object.defineProperty`。这不仅增加了代码的复杂度,而且对于大型对象来说,这可能会导致性能问题。 ```javascript function Observer(obj) { let keys = Object.keys(obj); for (let i of keys) { if (typeof obj[i] == 'object' && obj[i] !== null) { Observer(obj[i]); } Object.defineProperty(obj, i, { get() { console.log(`数据被获取了${obj[i]}...`); return obj[i]; }, set(newValue) { console.log(`数据变化了,obj.${i}:${newValue}...`); obj[i] = newValue; }, enumerable: true, configurable: true }); } } ``` #### 5. 不支持对整个对象的操作 `Object.defineProperty`只能拦截对象的属性访问,对于对象的整体操作(如对整个对象的赋值或属性遍历)并不会被拦截。这限制了其在某些场景下的灵活性和实用性。 ### 相关问题 1. 如何在JavaScript中使用Proxy来解决Object.defineProperty的局限性? 2. Vue 3.0中使用Proxy替代Object.defineProperty的具体实现方式是什么? 3. 在JavaScript中,如何通过自定义方法来实现对数组变化的监听? 4. 什么是Vue.set和Vue.delete,它们是如何帮助解决Object.defineProperty的局限性的? 5. 如何评估Object.defineProperty和Proxy在JavaScript中的性能差异?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值