Object.defineProperty()方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。
Object.defineProperty(object, propName, descriptor)
参数
object 要定义属性的对象
propName 要定义或修改的属性的名称或 Symbol
descriptor 要定义或修改的属性描述符
返回值
被传递给函数的对象
描述
数据描述符
value: 该属性对应的值,可以是任何有效的Javascript值,包括数值、对象、函数。默认值为undefined。
writable:是否可修改。默认值为false。
enumerable:是否可遍历枚举。默认值为false。
configurable:是否可修改配置项。当值为true时,该属性的描述符才能被改变,同时该属性也能从对应的对象上被删除。默认值为false。
const object = {};
Object.defineProperty(object, 'name', {
value: 'test',
writable: false,
enumerable: false,
configurable: false
})
存取描述符
get: 当访问对象属性时,会调用此函数。执行时不传入任何参数,但是会传入this对象(由于继承的关系,这里的this并不一定是定义该属性的对象)。返回值会被用作属性的值。默认为undefined。
set: 属性值被修改时,会调用此函数。该方法接受一个参数(即被赋予的新值),会传入赋值时的this对象。默认为undefined。
let value = 'test';
const object = {};
Object.defineProperty(object, 'name', {
get() {return value},
set(newValue) {value = newValue}
})
如果一个描述符不具有value、writable、get、set中的任意一个键,那么它将被认为是一个数据描述符。
如果一个描述符同时拥有value或writable和get或set键,则会产生一个异常。
// 同时存在writable、get和set键
let value = 'test';
const object = {};
Object.defineProperty(object, 'name', {
get() {return value},
set(newValue) {value = newValue},
writable: true,
enumerable: false,
configurable: false
})
// 报异常 TypeError: Invalid property descriptor. Cannot both specify accessors and a value or writable attribute, #<Object>
属性示例
writable属性
const object = {};
Object.defineProperty(object, 'name', {
value: 'aa',
writable: false,
});
object.name = 'kk'; // 修改无效,仅当writable属性值为true时才可修改
console.log(object.name) // 输出aa
enumerable属性
const object = {};
Object.defineProperty(object, 'name', {
value: 'aa',
enumerable: false,
});
console.log(Object.keys(object)); // 输出[]。仅当enumerable属性值为true时输出['name']
configurable属性
// 属性值为false时
const object = {};
Object.defineProperty(object, 'name', {
value: 'aa',
configurable: false,
});
Object.defineProperty(object, 'name', {
writable: true,
});
// 报异常 TypeError: Cannot redefine property: name
// 属性值为true时
const object = {};
Object.defineProperty(object, 'name', {
value: 'aa',
configurable: true,
});
object.name = 'kk';
console.log(object); // 输出{name: 'aa'}
Object.defineProperty(object, 'name', {
writable: true,
});
object.name = 'kk';
console.log(object); // 输出{name: 'kk'}
属性默认值
const object = {};
object.name = 'aa';
// 等同于 =>
Object.defineProperty(object, 'name', {
value: 'aa',
writable: true,
enumerable: true,
configurable: true
});
Object.defineProperty(object, 'name', {
value: 'aa'
});
// 等同于 =>
Object.defineProperty(object, 'name', {
value: 'aa',
writable: false,
enumerable: false,
configurable: false
})
自定义getters和setters
function Archiver() {
let name = null;
let archive = [];
Object.defineProperty(this, 'name', {
get() {
console.log('get');
return name;
},
set(value) {
name = value;
archive.push({val: name});
}
})
this.getArchive = () => archive;
}
const arc = new Archiver();
arc.name; // get
arc.name = 'aa';
arc.name = 'bb';
arc.getArchive(); // [{val: 'aa'}, {val: 'bb'}]
继承属性
如果访问者的属性是被继承的,它的get和set方法会在子对象的属性被访问或者修改时被调用。如果这些方法用一个变量存值,该值会被所有对象共享。
function MyClass() {}
let value;
Object.defineProperty(MyClass.prototype, 'name', {
get() {return value},
set(val) {value = val}
});
const a = new MyClass();
const b = new MyClass();
a.name = 'aa';
console.log(a.name); // aa
console.log(b.name); // aa
可以通过将值储存在另一个属性中解决。在get和set方法中,this指向某个被访问和修改属性的对象。
function MyClass() {}
let value;
Object.defineProperty(MyClass.prototype, 'name', {
get() {return this.value},
set(val) {this.value = val}
});
const a = new MyClass();
const b = new MyClass();
a.name = 'aa';
console.log(a.name); // aa
console.log(b.name); // undefined
如果一个不可写的属性被继承时,仍然可以防止修改对象的属性。
function MyClass() {}
MyClass.prototype.name = 'aa';
Object.defineProperty(MyClass.prototype, 'age', {
value: 20,
writable: false
})
const a = new MyClass();
a.name = 'kk';
console.log(a.name); // 输出kk
console.log(MyClass.prototype.name); // 输出aa
a.age = 25;
console.log(a.age); // 输出20
console.log(MyClass.prototype.age); // 输出20
参考资料:[MDN](Object.defineProperty() - JavaScript | MDN (mozilla.org))