JavaScript 中的 Object.defineProperty

用户头像
Verlime
关注
发布于: 2020 年 07 月 21 日

在 JavaScript 中我们对一个对象添加属性一般会采用对象属性赋值的方式来处理,例如:



var obj = { name: 'tom' };
obj.age = '20';



当然还有一种方式,就是通过 Object.defineProperty() 来为对象添加属性。

Object.defineProperty

语法如下:



Object.defineProperty(obj, prop, descriptor)



其中 obj 代表目标对象,prop 表示需要添加的属性名称,descriptor 表示对该属性的一些描述,包括是否可写、是否可配置、是否可枚举等,用 ts 描述的话就是:



type obj = any
type prop = string | number | symbol;
interface descriptor {
configurable?: boolean;
enumerable?: boolean;
value?: any;
writable?: boolean;
get?(): any;
set?(v: any): void;
}



下面就来逐个看看各个属性的使用。

configurable

configurable 表示所定义的属性是否可配置,默认为 false,也就是说如果该属性设置为 false,那么后续所有再次对该属性的修改都会视为无效,若使用 Object.defineProperty 重新定义的话还会报错, 例如:



var obj = { name: 'tom' };
Object.defineProperty(obj, 'job', {
value: 'doctor',
configurable: false,
});
console.log(obj.job);
// doctor
// 本次修改无效
obj.job = 'engineer';
console.log(obj.job);
// doctor
// 本句会报错:由于 job 属性已经是不可配置了,重新定义会报错
// Object.defineProperty(obj, 'job', {
// value: 'doctor',
// writable: true,
// });

enumerable

enumerable 表示所定义的属性是否可枚举,默认为 false,如果为 false 的话,无法通过 for..in.. 循环遍历到该属性,也无法通过 Object.keys() 访问,例如:



var obj = { name: 'tom' };
Object.defineProperty(obj, 'job', {
value: 'doctor',
enumerable: false,
});
// 由于新添加的 job 属性不可枚举,因此通过 Object.keys 无法访问
console.log(Object.keys(obj));
// ["name"]

value

这个没什么可说的,就是为属性赋值。

writable

writable 用来描述该属性是否可写,writable 属性的默认值为 false, 如果为 false 的话,则后续对该属性的再次赋值会视为无效操作,例如:



var obj = { name: 'tom' };
Object.defineProperty(obj, 'job', {
value: 'doctor',
writable: false,
});
console.log(obj.job);
// doctor
obj.job = 'engineer';
// 由于 writable 为 false, 因此这里的赋值操作无效
console.log(obj.job);
// doctor



综上,通过 Object.defineProperty 定义的属性 writable, enumerable, configurable 在默认状态下均为 false,但通过直接属性赋值的方式所得到的值均为 true,这个可以通过以下代码来证明:



var obj = { name: 'tom' };
Object.defineProperty(obj, 'job', {
value: 'doctor',
});
console.log(Object.getOwnPropertyDescriptor(obj, 'job'));
// {value: "doctor", writable: false, enumerable: false, configurable: false}
obj.age = '20';
console.log(Object.getOwnPropertyDescriptor(obj, 'age'));
// {value: "20", writable: true, enumerable: true, configurable: true}

getter 和 setter

除了以上的描述属性外,还有两个比较重要的方法:get()set(),这也是在一些 MVVM 框架里常常用到的,例如 Vue.js 2.x 里面的数据劫持。



get() 方法会在访问对象属性时进行调用,默认返回 undefined,而 set() 方法会在设置对象属性时调用,默认为 undefined,下面来看一个例子:



var obj = { name: 'tom' };
var job = 'doctor';
Object.defineProperty(obj, 'job', {
get: function () {
console.log('-- getter');
return job;
},
set: function (newValue) {
console.log('-- setter');
job = newValue;
},
});
console.log(obj.job);
// -- getter
// doctor
obj.job = 'engineer';
// -- setter



我们可以看到在对属性进行修改时,触发了目标对象的 gettersetter 方法,因此可以在里面进行数据劫持,当数据变化时进行一些操作。

总结

  • 通过 Object.defineProperty(obj, prop, descriptor) 可以为目标对象添加属性

  • 添加属性时可以对以下的描述属性进行设置,默认全为 false

* writable: 是否可写,取值为 false 时,重新赋值后不会改变原来的值

* enumerable: 是否可枚举,取值为 false 时,不能通过 for..inObject.keys() 读取

* configurable: 是否可配置,取值为 false 时,不能再次修改该属性

  • 访问属性时会调用 get() 方法,修改属性时会调用 set() 方法,可以利用这个特性进行数据劫持

参考资料

Object.defineProperty() - JavaScript | MDN

发布于: 2020 年 07 月 21 日 阅读数: 38
用户头像

Verlime

关注

一个前端工程师 2018.01.01 加入

每天进步一点点ヽ(•̀ω•́ )ゝ

评论

发布
暂无评论
JavaScript 中的 Object.defineProperty