JavaScript 数据属性和访问器属性

最后更新:
阅读次数:

一个例子

先看代码:

var obj = { name: "percy" };
console.log(obj.name); // percy
obj.name = "zyj";
console.log(obj.name); // zyj

// 这里借 Math 对象来举例
// 首先说明一下, Math 对象和上面定义的 obj 对象都只是 Object 对象的一个实例
console.log(Math.PI); // 3.1415926...
Math.PI = 1234;
console.log(Math.PI); // 3.1415926...

看到了吗?同样是 Object 的实例,obj 对象的属性却可以被改写,而 Math 对象的属性去不能被改写。好,让我们来码一行,从而让 obj 对象的属性也不能被改写:

var obj = { name: "percy" };
console.log(obj.name); // percy

Object.defineProperty(obj, "name", { writable: false });

obj.name = "zyj";
console.log(obj.name); // percy

看到了吗,这其中另有玄机,接下来就引入了我们的主角:对象的数据属性访问器属性

为了表示特性是内部值,规范(ECMA-262)就把它们放到了两对儿方括号里了,例如 [[Enumerable]]。

数据属性(Data Properties)

  • 数据属性有 4 个描述其行为的特性:
    • [[Configurable]] : 表示能否通过 delete 删除属性从而重新定义属性,能否修改属性的特性,或者能否把属性修改为访问器属性。
    • [[Enumerable]] : 表示能否通过 for-in 循环返回属性。
    • [[Writable]] : 表示能否修改属性的值。
    • [[Value]] : 包含这个属性的数据值。读取属性值的时候,从这个位置读;写入属性值时,把新值保存在这个位置。默认值是 undefined。

像这样(var obj = new Object(); obj.name = "percy";)或者像通过对象字面量(var obj = {name: "percy"};)直接在对象上定义的属性,它们的 [[Configurable]]、[[Enumerable]] 和 [[Writable]] 特性默认都被设置为 true,而 [[Value]] 特性被设置为指定的值。

这里注意一下这个(可以先跳过,完了再看):

var person = {}; // 新建一个空对象
Object.defineProperty(person, "name", {
value: "percy"
});
console.log(Object.getOwnPropertyDescriptor(person, "name"));
// 打印:Object {value: "percy", writable: false, enumerable: false, configurable: false}

没有按上面的 2 种方法为对象添加属性,而直接通过 Object.defineProperty() 为对象添加属性以及值,这种情况下,这个对象的这个属性的另外 3 个特性的默认值都是 false。

要修改属性默认的特性,必须使用 ECMAScript 的 Object.defineProperty() 方法。

  • Object.defineProperty( obj, prop, descriptor)
    • obj:需要定义属性的对象。
    • prop:需定义或修改的属性的名字。
    • descriptor:一个包含设置特性的对象
var person = { name: "percy" };
Object.defineProperty(person, "name", {
writable: false
});
console.log(person.name); // percy
person.name = "zyj";
console.log(person.name); // percy

for (let prop in person) {
console.log(prop + " : " + person[prop]);
} // name : percy
Object.defineProperty(person, "name", {
enumerable: false
});
for (let prop in person) {
console.log(prop + " : " + person[prop]);
} // 什么也没打印

Object.defineProperty(person, "name", {
configurable: false
});
Object.defineProperty(person, "name", {
configurable: true
}); // 报错:TypeError: Cannot redefine property: name(…)

// 一旦把属性定义为不可配置的,那么就再也不能把属性定义回可配置的了。

访问器属性(Accessor Properties)

访问器属性不包含数据值,它们包含一对儿 gettersetter 函数(不过,这两个函数都不是必需的)。在读取访问器属性时,会调用 getter 函数,在写入访问器属性时,又会调用 setter 函数并传入新值。

  • 访问器属性有如下 4 个特性:
    • [[Configurable]]:表示能否通过 delete 删除属性从而重新定义属性,能否修改属性的特性,或者能否把属性修改为数据属性。
    • [[Enumerable]]:表示能否通过 for-in 循环返回属性。
    • [[Get]]:在读取属性时调用的函数。默认值为 undefined。
    • [[Set]]:在写入属性时调用的函数。默认值为 undefined。

访问器属性不能直接定义,必须使用 Object.defineProperty() 来定义。

var book = {
_year: 2004,
edition: 1
};
Object.defineProperty(book, "year", {
get: function() {
alert(this._year);
},
set: function(newValue) {
if (newValue > 2004) {
this._year = newValue;
this.edition += newValue - 2004;
}
}
});
book.year; // 弹出窗口,显示 2004
book.year = 2005;
console.log(book.edition); // 2

要是上面的代码有不懂得地方,参看这里,JavaScript 理解对象:属性类型(推荐)

定义多个属性

var obj = {};
Object.defineProperties(obj, {
property1: {
value: true,
writable: true
},
property2: {
value: "Hello",
writable: false
}
// 等等.
});

console.log(obj); // Object {property1: true, property2: "Hello"}

读取属性的特性

Object.getOwnPropertyDescriptor(obj, prop) 返回指定对象上一个自有属性对应的属性描述符。(自有属性指的是直接赋予该对象的属性,不需要从原型链上进行查找的属性)

var girl = { name: "zyj" };
console.log(Object.getOwnPropertyDescriptor(girl, "name"));
// Object {value: "zyj", writable: true, enumerable: true, configurable: true}

Object.defineProperties(girl, {
name: {
writable: false
},
age: {
writable: true,
value: 22
}
});

console.log(Object.getOwnPropertyDescriptor(girl, "name"));
// Object {value: "zyj", writable: false, enumerable: true, configurable: true}
console.log(Object.getOwnPropertyDescriptor(girl, "age"));
// Object {value: 22, writable: true, enumerable: false, configurable: false}

var descriptor = Object.getOwnPropertyDescriptor(girl, "age");
console.log(descriptor.value); // 22
console.log(descriptor.configurable); // false
console.log(descriptor.writable); // true
console.log(descriptor.get); // undefined
console.log(descriptor.set); // undefined

摘抄自文末的链接

A property is a name (string identifier) associated with a property descriptor.

  • As of ECMAScript 5, three property types are available:
    • Internal Properties
    • Data properties
    • Accessor properties

参考资料