对象 | 对象属性的类型

ECMA-262 使用一些内部特性来描述属性的特征。这些特性是由为JavaScript 实现引擎的规范定义的。因此,开发者不能在JavaScript 中直接访问这些特性。为了将某个特性标识为内部特性,规范会用两个中括号把特性的名称括起来,比如[[Enumerable]]。

属性分为两种:数据属性访问属性

数据属性

(1)[[Configurable]]

表示属性是否可以通过delete 删除并重新定义,是否可以修改它的特性,以及是否可以把它改为访问器属性

默认情况下,所有直接定义在对象上的属性的这个特性都是true

注意:一个属性被定义为不可配置(即)configurable=false后,就不能再变回之前的可配置了configurable=true

(2)[[Enumerable]]

表示属性是否可以通过for-in 循环返回(心里默念:for…in用于遍历对象上可枚举属性,包括原型上的可枚举属性,巴拉巴拉~)

默认情况下,所有直接定义在对象上的属性的这个特性都是true

(3)[[Writable]]

表示属性的值是否可以被修改

默认情况下,所有直接定义在对象上的属性的这个特性都是true

(4)[[Value]]

包含属性实际的值

这个特性的默认值为undefined

数据属性总结与应用

  1. 将属性显式添加到对象之后,[[Configurable]][[Enumerable]][[Writable]]都会被设置为true,而[[Value]]特性会被设置为指定的值

  2. Object.defineProperty()修改属性的默认特性

    1
    2
    3
    4
    5
    Object.defineProperty(obj, prop, descriptor);

    - obj: 要定义属性的对象
    - prop: 要定义或修改的属性的名称或Symbol
    - descriptor: 要定义或修改的属性描述符
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    let person = {};

    Object.defineProperty(person, 'name', {
    writable: false,
    value: 'Katrina',
    });

    console.log(person.name); // Katrina
    person.name = 'Jack';
    console.log(person.name); // Katrina

    /*
    因为设置了writable: false,说明name属性的值不可以被修改
    非严格模式,忽略
    严格模式,抛出错误
    */
  3. 一个属性被定义为不可配置(即)configurable=false后,就不能再变回之前的可配置了configurable=true

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    let person = {};

    Object.defineProperty(person, 'name', {
    configurable: false,
    value: 'Katrina',
    });

    // 抛出错误
    Object.defineProperty(person, 'name', {
    configurable: true,
    value: 'Katrina',
    });
  4. 在调用Object.defineProperty()时,configurable、enumerable 和writable 的值如果不指定,则都默认为false

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    let person = {};

    Object.defineProperty(person, 'name', {
    value: 'Katrina',
    });

    console.log(person.name); // Katrina
    person.name = 'Jack';
    console.log(person.name); // Katrina 说明writable: false
    delete person.name;
    console.log(person.name); // Katrina 说明configurable: false

    for (let k in person) {
    console.log(k) // 啥也没有 说明enumerable: false
    };

访问器属性

访问器属性包含一个获取(getter)函数和一个设置(setter)函数

  1. 在读取访问器属性时,会调用获取函数,这个函数的责任就是返回一个有效的值
  2. 在写入访问器属性时,会调用设置函数并传入新值,这个函数必须决定对数据做出什么修改

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

[[Configurable]]

表示属性是否可以通过delete 删除并重新定义,是否可以修改它的特性,以及是否可以把它改为数据属性

默认情况下,所有直接定义在对象上的属性的这个特性都是true

[[Enumerable]]

表示属性是否可以通过for-in 循环返回

默认情况下,所有直接定义在对象上的属性的这个特性都是true

[[Get]]

获取函数,在读取属性时调用

默认值为undefined。

[[Set]]

设置函数,在写入属性时调用

默认值为undefined

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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 - 2018;
}
},
})

book.year_ = 2018;
console.log(book.edition); //2

属性访问器的典型使用场景:设置一个属性值会导致一些其他变化发生

知识补充

循环语句 | for-in&&for-of的区别