建议观看一个教学视频再看下面内容更容易理解:Object.defineProperty
ECMAScript 中有两种属性:数据属性和访问器属性。
数据属性是用来保存数据的,其中有 4 个描述其行为的特性,其中有一个特性 [[Value]] 是存放值的位置,这个位置可以读取和写入设置的属性的值。四个特性如下:
① [[Configurable]]:表示能否通过 delete 删除属性从而重新定义属性,能否修改属性的特性,或者能否把属性修改为访问器属性。像前面例子中直接在对象上定义的属性,该特性的默认值都是 true。
② [[Enumerable]]:表示能否通过 for-in 循环返回属性。像前面例子中直接在对象上定义的属性,该特性的默认值都是 true。
③ [[Writable]]:表示能否修改属性的值。像前面例子中直接在对象上定义的属性,该特性的默认值都是 true。
④ [[Value]]:包含这个属性的数据值。读取属性值的时候从这个位置读;写入属性的时候,把新值保存在这个位置。这个特性的默认值为 undefined。
要修改属性的默认的特性,必须使用 ECMAScript5 中的 Object.defineProperty() 方法。这个方法接收三个参数:属性所在的对象、属性的名字、和一个描述符对象。其中,描述符对象的属性必须是:configurable、enumerable、writable 和 value。设置其中一个或多个值。例如:
var person = {};
Object.defineProperty(person,"name",{
writable: false, // 设置"name"属性的 writable 特性值为 false,
value: "andy"
});
alert(person.name); // andy
person.name = "Grey"; // 尝试修改属性值,无效
alert(person.name); // andy,不可修改的属性
上述第7行代码在非严格模式下被忽略,在严格模式下报错。【注意】,①一旦使用 Object.defineProperty() 方法,如果没有指定特性的值,configurable、enumerable 和 writable 特性的默认值都是 false 【注意与在对象上定义属性时,特性的默认值不同】。②在 configurable 设置为 false 之前是可以多次调用 Object.defineProperty() 方法去设置同一个属性值的特性的,一旦 configurable 设置为 false 之后,就不允许在调用 Object.defineProperty() 方法去设置属性特性。严格模式下会报错。
"use strict";
let person = {};
Object.defineProperty(person,"name",{
writable: false,
value: "andy"
});
// 下一行代码报错,因为第三行方法中为 configurable 特性设置了 false 值。
Object.defineProperty(person,"name",{
value: "jack"
});
或者使用 Object.defineProperties(obj, props) 一次定义多个新的属性或修改多个现有属性,并返回该对象。
let obj = {};
Object.defineProperties(obj, {
'property1': {
value: true,
writable: true
},
'property2': {
value: 'Hello',
writable: false
}
// etc. etc.
});
数据属性其实就是更加底层的设置属性的方法,可以对属性进行更加细微的设置,可以完全用数据属性的方法为一个对象设置相关属性。
访问器属性不包含数据值。它们包含一个获取 (getter) 函数和一个设置 (setter) 函数,不过这两个函数不是必需的。在读取访问器属性时,会调用获取函数,这个函数的责任就是返回一个有效的值。在写入访问器属性时,会调用设置函数并传入新值,这个函数必须决定对数据做出什么修改。访问器属性有 4 个特性描述它们的行为。
① [[Configurable]]:表示能否通过 delete 删除属性从而重新定义属性,能否修改属性的特性,或者能否把属性修改为数据属性。该特性的默认值都是 true。
② [[Enumerable]]:表示能否通过 for-in 循环返回属性。像前面例子中直接在对象上定义的属性,该特性的默认值都是 true。
③ [[Get]]:获取函数,在读取属性时调用。默认值为 undefined。
④ [[Set]]:设置函数,在写入属性时调用。默认值为 undefined。
访问器属性是不能直接定义的,必须使用 Object.defineProperty()。下面是一个例子:
let book = {
year_:2017,
edition:1
};
Object.defineProperty(book,"year",{
get() {
return this.year_;
},
set(newValue){
if(newValue > 2017){
this.year_ = newValue;
this.edition += newValue - 2017;
}
}
});
book.year = 2018;
console.log(book.edition); // 2
获取函数和设置函数不一定都要定义。只定义获取函数意味着属性是只读的,尝试修改属性会被忽略。在严格模式下,尝试写入只定义了获取函数的属性会抛出错误。类似地,只有一个设置函数的属性是不能读取的,非严格模式下读取会返回 undefined,严格模式下会抛出错误。
建议看完上面讲解再看一遍视频加深理解理解:Object.defineProperty