这是我参与「第四届青训营 」笔记创作活动的第3天
总体知识
ECMA-262使用一些内部特性来描述属性的特征。开发者不能在 JavaScript 中直接访问这些特性。为了将某个特性标识为内部特性,规范会用两个中括号把特性的名称括起来,比如[[Enumerable]]。
属性有两种:数据属性和访问器属性。
数据属性
-
[[Configurable]]:表示属性是否可以通过 delete 删除并重新定义,是否可以修改它的特性,以及是否可以把它改为访问器属性。默认情况下,所有直接定义在对象上的属性的这个特性都是 true,如前面的例子所示。
-
[[Enumerable]]:表示属性是否可以通过 for-in 循环返回。默认情况下,所有直接定义在对象上的属性的这个特性都是 true,如前面的例子所示。
-
[[Writable]]:表示属性的值是否可以被修改。默认情况下,所有直接定义在对象上的属性的这个特性都是 true,如前面的例子所示。
-
[[Value]]:包含属性实际的值。这就是前面提到的那个读取和写入属性值的位置。这个特性的默认值为 undefined。
这里我们可以写一个例子
用Object..defineProperty(),可以修改默认的属性,参数有三个:对象、属性的名称和一个描述符对象,描述符对象就是上面所提到的四个数据属性
let person2 = {};
Object.defineProperty(person2, 'name', {writable: false/*重点!*/, value: 'MikuScallion'});
// 一旦设置为不可配置以后,就不能设置为可配置了。
Object.defineProperty(person2, 'age', {configurable: false, value: 21});
// 会报错
Object.defineProperty(person2, 'age', {configurable: true, value: 21});
console.log(person2.name);
person2.name = 'wq';
console.log(person2.name);
console.log(person2.age);
// 无法删除掉,下面的console还会继续运行。
delete person2.age;
console.log(person2.age);
-
这里把person2对象中的name属性的writable属性设置为false,也就是不可以被修改的。然后把value属性设置为‘MikuScallion’,也就是name的值时多少。
-
我们试图把person2.name的值设置为‘wq’。我们可以看看最后的结果
-
值没有改变!
-
下面我们讲configurable设置为false并试着删除,发现无法删除。
-
-
configurable设置为true报错。
-
在调用 Object.defineProperty()时,configurable、enumerable 和 writable 的值如果不指定,则都默认为 false。
访问器属性
个人理解:访问器属性最重要的两个方法就是get()方法和set方法。这两个是决定对象中对那个值进行修改和读取的。对某个属性设置时,如果只设置了get()方法,证明了此属性时不可以修改值的。如果只设置了set()方法,证明此属性时不可访问读取的。对于设置set和get的方法也时使用Object.defineProperty。
let book =
{
year_: 2017,
edition: 1
};
// 对year属性设置get和set函数,这样时无法直接访问
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);
-
对于属性带下划线year_表示的时不希望外部可以访问到此属性,但是可以通过一些方法来设置此属性。
-
我们设置了一个新的year属性来代替year_
-
如果我们想要获得year_的值,我们直接打印就好,因为给year设置了get方法。
-
如果我们想要设置year_ 的值,我们直接赋值就好,因为我们给year也设置了set方法。
-
因为我们设置了year = 2018,我们最后edtion的值就变成了2。
定义多个属性
使用的是 Object.defineProperties
let book1 = {};
Object.defineProperties(book1,
{
year_: {
value: 2017
},
edition: {
value: 1
},
year: {
get(){
return this.year_;
},
set(newValue){
if (newValue > 2017)
{
this.year_ = newValue;
this.edition += newValue - 2017;
}
}
}
}
)
book1.year = 5000;
console.log(book1.edition);
这段代码在 book 对象上定义了两个数据属性 year_和 edition,还有一个访问器属性 year。最终的对象跟上一节示例中的一样。唯一的区别是所有属性都是同时定义的,并且数据属性的configurable、enumerable 和 writable 特性值都是 false。
所以可以增加一些设置让程序可以跑起来,例如给year_和edition设置writable: true,这样就可以读写了。
读取属性
使用 Object.getOwnPropertyDescriptor()方法可以取得指定属性的属性描述符。这个方法接
收两个参数:属性所在的对象和要取得其描述符的属性名。
descript = Object.getOwnPropertyDescriptor(book1, 'year');
console.log(descript.value); // undefined
console.log(descript.configurable); // false
console.log(typeof descript.get); // function
descript1 = Object.getOwnPropertyDescriptor(book1, 'year_');
console.log(descript1.value); // 2018
console.log(descript1.configurable);// false
console.log(typeof descript1.get); // undefined // undefined console.log(descript.configurable); // false console.log(typeof descript.get); // functiondescript1 = Object.getOwnPropertyDescriptor(book1, 'year_');console.log(descript1.value); // 2018console.log(descript1.configurable);// falseconsole.log(typeof descript1.get); // undefined
ECMAScript 2017 新增了 Object.getOwnPropertyDescriptors()静态方法。这个方法实际上
会在每个自有属性上调用 Object.getOwnPropertyDescriptor()并在一个新对象中返回它们。
console.log(Object.getOwnPropertyDescriptors(book1));
/*
edition: {value: 2, writable: true, enumerable: false, configurable: false}
year: {enumerable: false, configurable: false, get: ƒ, set: ƒ}
year_: {value: 2018, writable: true, enumerable: false, configurable: false}
[[Prototype]]: Object
*/