Object.defineProperty
Object.defineProperty
Object.defineProperty()
方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性, 并返回这个对象。
语法
Object.defineProperty(obj, prop, descriptor)
参数
obj
要在其上定义属性的对象。
返回值
被传递给函数的对象。
描述
该方法允许精确添加或修改对象的属性。通过赋值来添加的普通属性会创建在属性枚举期间显示的属性(for...in
或 Object.keys
方法), 这些值可以被改变,也可以被删除。这种方法允许这些额外的细节从默认值改变。默认情况下,使用Object.defineProperty()
添加的属性值是不可变的。
对象里目前存在的属性描述符有两种主要形式:数据描述符
和存取描述符
。数据描述符
是一个具有值的属性,该值可能是可写的,也可能不是可写的。访问器描述符是由getter-setter函数对描述的属性。描述符必须是这两种形式之一;不能同时是两者。
数据描述符和存取描述符均具有
以下可选键值:
configurable
当且仅当该属性的 configurable
为 true 时,该属性描述符
才能够被改变,同时该属性也能从对应的对象上被删除。
默认为 false
。enumerable
当且仅当该属性的enumerable
为true
时,该属性才能够出现在对象的枚举属性中。
默认为 false
。
数据描述符同时具有以下可选键值:
value
该属性对应的值。可以是任何有效的 JavaScript 值(数值,对象,函数等)。
默认为 undefined
。writable
当且仅当该属性的writable
为true
时,value
才能被赋值运算符改变。
默认为false
存取描述符同时具有以下可选键值:
get
一个给属性提供 get
ter 的方法,如果没有 get
ter 则为 undefined
。该方法返回值被用作属性值。
默认为undefined
。set
一个给属性提供 set
ter 的方法,如果没有 set
ter 则为 undefined
。该方法将接受唯一参数,并将该参数的新值分配给该属性。
默认为undefined
。
记住,这些选项不一定是自身属性,如果是继承来的也要考虑。为了确认保留这些默认值,你可能要在这之前冻结Object.prototype
,明确指定所有的选项,或者将__proto__
属性指向null
。
// using __proto__
var obj = {};
var descriptor = Object.create(null // no inherited properties
// not enumerable, not configurable, not writable as defaults
descriptor.value = 'static';
Object.defineProperty(obj, 'key', descriptor
// being explicit
Object.defineProperty(obj, 'key', {
enumerable: false,
configurable: false,
writable: false,
value: 'static'
}
// recycling same object
function withValue(value) {
var d = withValue.d || (
withValue.d = {
enumerable: false,
writable: false,
configurable: false,
value: null
}
d.value = value;
return d;
}
// ... and ...
Object.defineProperty(obj, 'key', withValue('static')
// if freeze is available, prevents adding or
// removing the object prototype properties
// (value, get, set, enumerable, writable, configurable)
(Object.freeze || Object)(Object.prototype
示例
如果你想了解如何使用Object.defineProperty
方法和类二进制标记语法,看看这篇文章。
创建属性
如果对象中不存在指定的属性,Object.defineProperty()
就创建这个属性。当描述符中省略某些字段时,这些字段将使用它们的默认值。拥有布尔值的字段的默认值都是false
。value
,get
和set
字段的默认值为undefined
。一个没有get/set/value/writable
定义的属性被称为“通用的”,并被“键入”为一个数据描述符。
var o = {}; // Creates a new object
// Example of an object property added
// with defineProperty with a data property descriptor
Object.defineProperty(o, 'a', {
value: 37,
writable: true,
enumerable: true,
configurable: true
}
// 'a' property exists in the o object and its value is 37
// Example of an object property added
// with defineProperty with an accessor property descriptor
var bValue = 38;
Object.defineProperty(o, 'b', {
get: function() { return bValue; },
set: function(newValue) { bValue = newValue; },
enumerable: true,
configurable: true
}
o.b; // 38
// 'b' property exists in the o object and its value is 38
// The value of o.b is now always identical to bValue,
// unless o.b is redefined
// You cannot try to mix both:
Object.defineProperty(o, 'conflict', {
value: 0x9f91102,
get: function() { return 0xdeadbeef; }
}
// throws a TypeError: value appears
// only in data descriptors,
// get appears only in accessor descriptors
修改属性
如果属性已经存在,Object.defineProperty()
将尝试根据描述符中的值以及对象当前的配置来修改这个属性。如果旧描述符将其configurable
属性设置为false
,则该属性被认为是“不可配置的”,并且没有属性可以被改变(除了单向改变 writable 为 false
)。当属性不可配置时,不能在数据和访问器属性类型之间切换。
当试图改变不可配置属性(除了writable
属性之外)的值时会抛出{jsxref("TypeError")}},除非当前值和新值相同。
Writable 属性
当writable
属性设置为false
时,该属性被称为“不可写”。它不能被重新分配。
var o = {}; // Creates a new object
Object.defineProperty(o, 'a', {
value: 37,
writable: false
}
console.log(o.a // logs 37
o.a = 25; // No error thrown
// (it would throw in strict mode,
// even if the value had been the same)
console.log(o.a // logs 37. The assignment didn't work.
如示例所示,试图写入非可写属性不会改变它,也不会引发错误。
Enumerable 特性
enumerable
定义了对象的属性是否可以在 for...in
循环和 Object.keys()
中被枚举。
var o = {};
Object.defineProperty(o, 'a', {
value: 1,
enumerable: true
}
Object.defineProperty(o, 'b', {
value: 2,
enumerable: false
}
Object.defineProperty(o, 'c', {
value: 3
} // enumerable defaults to false
o.d = 4; // enumerable defaults to true
// when creating a property by setting it
for (var i in o) {
console.log(i
}
// logs 'a' and 'd' (in undefined order)
Object.keys(o // ['a', 'd']
o.propertyIsEnumerable('a' // true
o.propertyIsEnumerable('b' // false
o.propertyIsEnumerable('c' // false
Configurable 特性
configurable
特性表示对象的属性是否可以被删除,以及除writable
特性外的其他特性是否可以被修改。
var o = {};
Object.defineProperty(o, 'a', {
get: function() { return 1; },
configurable: false
}
Object.defineProperty(o, 'a', {
configurable: true
} // throws a TypeError
Object.defineProperty(o, 'a', {
enumerable: true
} // throws a TypeError
Object.defineProperty(o, 'a', {
set: function() {}
} // throws a TypeError (set was undefined previously)
Object.defineProperty(o, 'a', {
get: function() { return 1; }
} // throws a TypeError
// (even though the new get does exactly the same thing)
Object.defineProperty(o, 'a', {
value: 12
} // throws a TypeError
console.log(o.a // logs 1
delete o.a; // Nothing happens
console.log(o.a // logs 1
如果o.a
的configurable
属性为true
,则不会抛出任何错误,并且该属性将在最后被删除。
添加多个属性和默认值
考虑特性被赋予的默认特性值非常重要,通常,使用点运算符和Object.defineProperty()
为对象的属性赋值时,数据描述符中的属性默认值是不同的,如下例所示。
var o = {};
o.a = 1;
// is equivalent to:
Object.defineProperty(o, 'a', {
value: 1,
writable: true,
configurable: true,
enumerable: true
}
// On the other hand,
Object.defineProperty(o, 'a', { value: 1 }
// is equivalent to:
Object.defineProperty(o, 'a', {
value: 1,
writable: false,
configurable: false,
enumerable: false
}
一般的 Setters 和 Getters
下面的例子展示了如何实现一个自存档对象。 当设置temperature
属性时,archive
数组会获取日志条目。
function Archiver() {
var temperature = null;
var archive = [];
Object.defineProperty(this, 'temperature', {
get: function() {
console.log('get!'
return temperature;
},
set: function(value) {
temperature = value;
archive.push{ val: temperature }
}
}
this.getArchive = function() { return archive; };
}
var arc = new Archiver(
arc.temperature; // 'get!'
arc.temperature = 11;
arc.temperature = 13;
arc.getArchive( // [{ val: 11 }, { val: 13 }]
或
var pattern = {
get: function () {
return 'I always return this string, ' +
'whatever you have assigned';
},
set: function () {
this.myname = 'this is my name string';
}
};
function TestDefineSetAndGet() {
Object.defineProperty(this, 'myproperty', pattern
}
var instance = new TestDefineSetAndGet(
instance.myproperty = 'test';
console.log(instance.myproperty
// I always return this string, whatever you have assigned
console.log(instance.myname // this is my name string
规范
Specification | Status | Comment |
---|---|---|
ECMAScript 5.1 (ECMA-262)The definition of 'Object.defineProperty' in that specification. | Standard | Initial definition. Implemented in JavaScript 1.8.5. |
ECMAScript 2015 (6th Edition, ECMA-262)The definition of 'Object.defineProperty' in that specification. | Standard | |
ECMAScript Latest Draft (ECMA-262)The definition of 'Object.defineProperty' in that specification. | Living Standard | |
浏览器兼容
Feature | Chrome | Edge | Firefox | Internet Explorer | Opera | Safari |
---|---|---|---|---|---|---|
Basic Support | 5 | (Yes) | 4 | 91 | 11.6 | 5.12 |
Feature | Android | Chrome for Android | Edge mobile | Firefox for Android | IE mobile | Opera Android | iOS Safari |
---|---|---|---|---|---|---|---|
Basic Support | (Yes) | (Yes) | (Yes) | 4 | (Yes) | 11.5 | (Yes) |